diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DatagramDnsQueryDecoder.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DatagramDnsQueryDecoder.cs index d6db25035..40e008a26 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DatagramDnsQueryDecoder.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DatagramDnsQueryDecoder.cs @@ -1,24 +1,55 @@ -using DotNetty.Transport.Channels.Sockets; -using System; -using System.Collections.Generic; -using DotNetty.Transport.Channels; +using DotNetty.Buffers; using DotNetty.Codecs.DNS.Messages; -using DotNetty.Buffers; using DotNetty.Codecs.DNS.Records; +using DotNetty.Transport.Channels; +using DotNetty.Transport.Channels.Sockets; +using System; +using System.Collections.Generic; namespace DotNetty.Codecs.DNS { + /// + /// Defines the + /// public class DatagramDnsQueryDecoder : MessageToMessageDecoder { + #region 字段 + + /// + /// Defines the recordDecoder + /// private readonly IDnsRecordDecoder recordDecoder; - public DatagramDnsQueryDecoder() : this(new DefaultDnsRecordDecoder()) { } + #endregion 字段 + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + public DatagramDnsQueryDecoder() : this(new DefaultDnsRecordDecoder()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The recordDecoder public DatagramDnsQueryDecoder(IDnsRecordDecoder recordDecoder) { this.recordDecoder = recordDecoder ?? throw new ArgumentNullException(nameof(recordDecoder)); } + #endregion 构造函数 + + #region 方法 + + /// + /// The Decode + /// + /// The context + /// The message + /// The output protected override void Decode(IChannelHandlerContext context, DatagramPacket message, List output) { IByteBuffer buffer = message.Content; @@ -47,6 +78,12 @@ protected override void Decode(IChannelHandlerContext context, DatagramPacket me } } + /// + /// The NewQuery + /// + /// The packet + /// The buffer + /// The private static IDnsQuery NewQuery(DatagramPacket packet, IByteBuffer buffer) { int id = buffer.ReadUnsignedShort(); @@ -55,7 +92,7 @@ private static IDnsQuery NewQuery(DatagramPacket packet, IByteBuffer buffer) throw new CorruptedFrameException("not a query"); IDnsQuery query = new DatagramDnsQuery( - packet.Sender, packet.Recipient, id, + packet.Sender, packet.Recipient, id, DnsOpCode.From((byte)(flags >> 11 & 0xf))); query.IsRecursionDesired = (flags >> 8 & 1) == 1; @@ -63,6 +100,12 @@ private static IDnsQuery NewQuery(DatagramPacket packet, IByteBuffer buffer) return query; } + /// + /// The DecodeQuestions + /// + /// The query + /// The buffer + /// The questionCount private void DecodeQuestions(IDnsQuery query, IByteBuffer buffer, int questionCount) { for (int i = questionCount; i > 0; i--) @@ -71,6 +114,13 @@ private void DecodeQuestions(IDnsQuery query, IByteBuffer buffer, int questionCo } } + /// + /// The DecodeRecords + /// + /// The query + /// The section + /// The buffer + /// The count private void DecodeRecords(IDnsQuery query, DnsSection section, IByteBuffer buffer, int count) { for (int i = count; i > 0; i--) @@ -82,5 +132,7 @@ private void DecodeRecords(IDnsQuery query, DnsSection section, IByteBuffer buff query.AddRecord(section, r); } } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DatagramDnsQueryEncoder.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DatagramDnsQueryEncoder.cs index 4280b8bed..53dc21049 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DatagramDnsQueryEncoder.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DatagramDnsQueryEncoder.cs @@ -1,25 +1,56 @@ -using System; -using System.Collections.Generic; -using DotNetty.Transport.Channels; +using DotNetty.Buffers; using DotNetty.Codecs.DNS.Messages; -using DotNetty.Buffers; using DotNetty.Codecs.DNS.Records; -using System.Net; +using DotNetty.Transport.Channels; using DotNetty.Transport.Channels.Sockets; +using System; +using System.Collections.Generic; +using System.Net; namespace DotNetty.Codecs.DNS { + /// + /// Defines the + /// public class DatagramDnsQueryEncoder : MessageToMessageEncoder> { + #region 字段 + + /// + /// Defines the recordEncoder + /// private readonly IDnsRecordEncoder recordEncoder; - public DatagramDnsQueryEncoder() : this(new DefaultDnsRecordEncoder()) { } + #endregion 字段 + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + public DatagramDnsQueryEncoder() : this(new DefaultDnsRecordEncoder()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The recordEncoder public DatagramDnsQueryEncoder(IDnsRecordEncoder recordEncoder) { this.recordEncoder = recordEncoder ?? throw new ArgumentNullException(nameof(recordEncoder)); } + #endregion 构造函数 + + #region 方法 + + /// + /// The Encode + /// + /// The context + /// The message + /// The output protected override void Encode(IChannelHandlerContext context, IAddressedEnvelope message, List output) { EndPoint recipient = message.Recipient; @@ -43,9 +74,20 @@ protected override void Encode(IChannelHandlerContext context, IAddressedEnvelop output.Add(new DatagramPacket(buffer, recipient, null)); } - private IByteBuffer AllocateBuffer(IChannelHandlerContext ctx, + /// + /// The AllocateBuffer + /// + /// The ctx + /// The message + /// The + private IByteBuffer AllocateBuffer(IChannelHandlerContext ctx, IAddressedEnvelope message) => ctx.Allocator.Buffer(1024); + /// + /// The EncodeHeader + /// + /// The query + /// The buffer private void EncodeHeader(IDnsQuery query, IByteBuffer buffer) { buffer.WriteShort(query.Id); @@ -61,6 +103,11 @@ private void EncodeHeader(IDnsQuery query, IByteBuffer buffer) buffer.WriteShort(query.Count(DnsSection.ADDITIONAL)); } + /// + /// The EncodeQuestions + /// + /// The query + /// The buffer private void EncodeQuestions(IDnsQuery query, IByteBuffer buffer) { int count = query.Count(DnsSection.QUESTION); @@ -70,6 +117,12 @@ private void EncodeQuestions(IDnsQuery query, IByteBuffer buffer) } } + /// + /// The EncodeRecords + /// + /// The query + /// The section + /// The buffer private void EncodeRecords(IDnsQuery query, DnsSection section, IByteBuffer buffer) { int count = query.Count(section); @@ -78,5 +131,7 @@ private void EncodeRecords(IDnsQuery query, DnsSection section, IByteBuffer buff recordEncoder.EncodeRecord(query.GetRecord(section, i), buffer); } } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DatagramDnsResponseDecoder.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DatagramDnsResponseDecoder.cs index c39c48da0..8176b456b 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DatagramDnsResponseDecoder.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DatagramDnsResponseDecoder.cs @@ -1,24 +1,55 @@ -using DotNetty.Transport.Channels.Sockets; -using System; -using System.Collections.Generic; -using DotNetty.Transport.Channels; +using DotNetty.Buffers; using DotNetty.Codecs.DNS.Messages; -using DotNetty.Buffers; using DotNetty.Codecs.DNS.Records; +using DotNetty.Transport.Channels; +using DotNetty.Transport.Channels.Sockets; +using System; +using System.Collections.Generic; namespace DotNetty.Codecs.DNS { + /// + /// Defines the + /// public class DatagramDnsResponseDecoder : MessageToMessageDecoder { + #region 字段 + + /// + /// Defines the recordDecoder + /// private readonly IDnsRecordDecoder recordDecoder; - public DatagramDnsResponseDecoder() : this(new DefaultDnsRecordDecoder()) { } + #endregion 字段 + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + public DatagramDnsResponseDecoder() : this(new DefaultDnsRecordDecoder()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The recordDecoder public DatagramDnsResponseDecoder(IDnsRecordDecoder recordDecoder) { this.recordDecoder = recordDecoder ?? throw new ArgumentNullException(nameof(recordDecoder)); } + #endregion 构造函数 + + #region 方法 + + /// + /// The Decode + /// + /// The context + /// The message + /// The output protected override void Decode(IChannelHandlerContext context, DatagramPacket message, List output) { IByteBuffer buffer = message.Content; @@ -47,6 +78,12 @@ protected override void Decode(IChannelHandlerContext context, DatagramPacket me } } + /// + /// The NewResponse + /// + /// The packet + /// The buffer + /// The private static IDnsResponse NewResponse(DatagramPacket packet, IByteBuffer buffer) { int id = buffer.ReadUnsignedShort(); @@ -54,14 +91,20 @@ private static IDnsResponse NewResponse(DatagramPacket packet, IByteBuffer buffe if (flags >> 15 == 0) throw new CorruptedFrameException("not a response"); IDnsResponse response = new DatagramDnsResponse( - packet.Sender, - packet.Recipient, - id, - DnsOpCode.From((byte)(flags >> 1 & 0xf)), + packet.Sender, + packet.Recipient, + id, + DnsOpCode.From((byte)(flags >> 1 & 0xf)), DnsResponseCode.From((byte)(flags & 0xf))); return response; } + /// + /// The DecodeQuestions + /// + /// The response + /// The buffer + /// The questionCount private void DecodeQuestions(IDnsResponse response, IByteBuffer buffer, int questionCount) { for (int i = questionCount - 1; i > 0; i--) @@ -70,6 +113,13 @@ private void DecodeQuestions(IDnsResponse response, IByteBuffer buffer, int ques } } + /// + /// The DecodeRecords + /// + /// The response + /// The section + /// The buffer + /// The count private void DecodeRecords(IDnsResponse response, DnsSection section, IByteBuffer buffer, int count) { for (int i = count - 1; i > 0; i--) @@ -81,5 +131,7 @@ private void DecodeRecords(IDnsResponse response, DnsSection section, IByteBuffe response.AddRecord(section, r); } } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DatagramDnsResponseEncoder.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DatagramDnsResponseEncoder.cs index 55473698b..3024e33e0 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DatagramDnsResponseEncoder.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DatagramDnsResponseEncoder.cs @@ -9,17 +9,57 @@ namespace DotNetty.Codecs.DNS { + /// + /// Defines the + /// public class DatagramDnsResponseEncoder : MessageToMessageEncoder> { + #region 字段 + + /// + /// Defines the recordEncoder + /// private IDnsRecordEncoder recordEncoder; - public DatagramDnsResponseEncoder() : this(new DefaultDnsRecordEncoder()) { } + #endregion 字段 + + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + public DatagramDnsResponseEncoder() : this(new DefaultDnsRecordEncoder()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The recordEncoder public DatagramDnsResponseEncoder(IDnsRecordEncoder recordEncoder) { this.recordEncoder = recordEncoder ?? throw new ArgumentNullException(nameof(recordEncoder)); } + #endregion 构造函数 + + #region 方法 + + /// + /// The AllocateBuffer + /// + /// The ctx + /// The message + /// The + protected IByteBuffer AllocateBuffer(IChannelHandlerContext ctx, + IAddressedEnvelope message) => ctx.Allocator.Buffer(1024); + + /// + /// The Encode + /// + /// The context + /// The message + /// The output protected override void Encode(IChannelHandlerContext context, IAddressedEnvelope message, List output) { EndPoint recipient = message.Recipient; @@ -44,13 +84,15 @@ protected override void Encode(IChannelHandlerContext context, IAddressedEnvelop buffer.Release(); } - output.Add(new DatagramPacket(buffer,null, recipient)); + output.Add(new DatagramPacket(buffer, null, recipient)); } } - protected IByteBuffer AllocateBuffer(IChannelHandlerContext ctx, - IAddressedEnvelope message) => ctx.Allocator.Buffer(1024); - + /// + /// The EncodeHeader + /// + /// The response + /// The buffer private static void EncodeHeader(IDnsResponse response, IByteBuffer buffer) { buffer.WriteShort(response.Id); @@ -77,6 +119,11 @@ private static void EncodeHeader(IDnsResponse response, IByteBuffer buffer) buffer.WriteShort(response.Count(DnsSection.ADDITIONAL)); } + /// + /// The EncodeQuestions + /// + /// The response + /// The buffer private void EncodeQuestions(IDnsResponse response, IByteBuffer buffer) { int count = response.Count(DnsSection.QUESTION); @@ -86,6 +133,12 @@ private void EncodeQuestions(IDnsResponse response, IByteBuffer buffer) } } + /// + /// The EncodeRecords + /// + /// The response + /// The section + /// The buffer private void EncodeRecords(IDnsResponse response, DnsSection section, IByteBuffer buffer) { int count = response.Count(section); @@ -95,6 +148,6 @@ private void EncodeRecords(IDnsResponse response, DnsSection section, IByteBuffe } } - + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DefaultDnsRecordDecoder.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DefaultDnsRecordDecoder.cs index 25158faf4..35542fbcb 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DefaultDnsRecordDecoder.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DefaultDnsRecordDecoder.cs @@ -1,61 +1,41 @@ -using System.Text; -using DotNetty.Buffers; +using DotNetty.Buffers; using DotNetty.Codecs.DNS.Records; +using System.Text; namespace DotNetty.Codecs.DNS { + /// + /// Defines the + /// public class DefaultDnsRecordDecoder : IDnsRecordDecoder { + #region 常量 + + /// + /// Defines the ROOT + /// private const string ROOT = "."; - internal DefaultDnsRecordDecoder() { } + #endregion 常量 - public IDnsQuestion DecodeQuestion(IByteBuffer inputBuffer) - { - string name = DecodeName(inputBuffer); - DnsRecordType type = DnsRecordType.From(inputBuffer.ReadUnsignedShort()); - var recordClass = (DnsRecordClass)inputBuffer.ReadUnsignedShort(); - return new DefaultDnsQuestion(name, type, recordClass); - } + #region 构造函数 - public IDnsRecord DecodeRecord(IByteBuffer inputBuffer) + /// + /// Initializes a new instance of the class. + /// + internal DefaultDnsRecordDecoder() { - int startOffset = inputBuffer.ReaderIndex; - string name = DecodeName(inputBuffer); - - int endOffset = inputBuffer.WriterIndex; - if (endOffset - startOffset < 10) - { - inputBuffer.SetReaderIndex(startOffset); - return null; - } - - DnsRecordType type = DnsRecordType.From(inputBuffer.ReadUnsignedShort()); - var recordClass = (DnsRecordClass)inputBuffer.ReadUnsignedShort(); - long ttl = inputBuffer.ReadUnsignedInt(); - int length = inputBuffer.ReadUnsignedShort(); - int offset = inputBuffer.ReaderIndex; - - if (endOffset - offset < length) - { - inputBuffer.SetReaderIndex(startOffset); - return null; - } - - IDnsRecord record = DecodeRecord(name, type, recordClass, ttl, inputBuffer, offset, length); - inputBuffer.SetReaderIndex(offset + length); - return record; } - protected IDnsRecord DecodeRecord(string name, DnsRecordType type, DnsRecordClass dnsClass, long timeToLive, - IByteBuffer inputBuffer, int offset, int length) - { - if (type == DnsRecordType.PTR) - return new DefaultDnsPtrRecord(name, dnsClass, timeToLive, DecodeName(inputBuffer.SetIndex(offset, offset + length))); + #endregion 构造函数 - return new DefaultDnsRawRecord(name, type, dnsClass, timeToLive, inputBuffer.SetIndex(offset, offset + length)); - } + #region 方法 + /// + /// The DecodeName + /// + /// The inputBuffer + /// The public static string DecodeName(IByteBuffer inputBuffer) { int position = -1; @@ -69,7 +49,7 @@ public static string DecodeName(IByteBuffer inputBuffer) var name = new StringBuilder(readable << 1); while (inputBuffer.IsReadable()) { - int len = inputBuffer.ReadByte() & 0xff; + int len = inputBuffer.ReadByte() & 0xff; bool pointer = (len & 0xc0) == 0xc0; if (pointer) @@ -116,5 +96,74 @@ public static string DecodeName(IByteBuffer inputBuffer) return name.ToString(); } + + /// + /// The DecodeQuestion + /// + /// The inputBuffer + /// The + public IDnsQuestion DecodeQuestion(IByteBuffer inputBuffer) + { + string name = DecodeName(inputBuffer); + DnsRecordType type = DnsRecordType.From(inputBuffer.ReadUnsignedShort()); + var recordClass = (DnsRecordClass)inputBuffer.ReadUnsignedShort(); + return new DefaultDnsQuestion(name, type, recordClass); + } + + /// + /// The DecodeRecord + /// + /// The inputBuffer + /// The + public IDnsRecord DecodeRecord(IByteBuffer inputBuffer) + { + int startOffset = inputBuffer.ReaderIndex; + string name = DecodeName(inputBuffer); + + int endOffset = inputBuffer.WriterIndex; + if (endOffset - startOffset < 10) + { + inputBuffer.SetReaderIndex(startOffset); + return null; + } + + DnsRecordType type = DnsRecordType.From(inputBuffer.ReadUnsignedShort()); + var recordClass = (DnsRecordClass)inputBuffer.ReadUnsignedShort(); + long ttl = inputBuffer.ReadUnsignedInt(); + int length = inputBuffer.ReadUnsignedShort(); + int offset = inputBuffer.ReaderIndex; + + if (endOffset - offset < length) + { + inputBuffer.SetReaderIndex(startOffset); + return null; + } + + IDnsRecord record = DecodeRecord(name, type, recordClass, ttl, inputBuffer, offset, length); + inputBuffer.SetReaderIndex(offset + length); + return record; + } + + /// + /// The DecodeRecord + /// + /// The name + /// The type + /// The dnsClass + /// The timeToLive + /// The inputBuffer + /// The offset + /// The length + /// The + protected IDnsRecord DecodeRecord(string name, DnsRecordType type, DnsRecordClass dnsClass, long timeToLive, + IByteBuffer inputBuffer, int offset, int length) + { + if (type == DnsRecordType.PTR) + return new DefaultDnsPtrRecord(name, dnsClass, timeToLive, DecodeName(inputBuffer.SetIndex(offset, offset + length))); + + return new DefaultDnsRawRecord(name, type, dnsClass, timeToLive, inputBuffer.SetIndex(offset, offset + length)); + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DefaultDnsRecordEncoder.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DefaultDnsRecordEncoder.cs index 4c58502e3..1aa3396c9 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DefaultDnsRecordEncoder.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DefaultDnsRecordEncoder.cs @@ -1,19 +1,49 @@ -using System; -using System.Text; -using DotNetty.Buffers; +using DotNetty.Buffers; using DotNetty.Codecs.DNS.Records; -using System.Net.Sockets; using DotNetty.Common.Utilities; +using System; +using System.Net.Sockets; +using System.Text; namespace DotNetty.Codecs.DNS { + /// + /// Defines the + /// public class DefaultDnsRecordEncoder : IDnsRecordEncoder { + #region 常量 + + /// + /// Defines the PREFIX_MASK + /// private const int PREFIX_MASK = sizeof(byte) - 1; + + /// + /// Defines the ROOT + /// private const string ROOT = "."; - internal DefaultDnsRecordEncoder() { } + #endregion 常量 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + internal DefaultDnsRecordEncoder() + { + } + + #endregion 构造函数 + + #region 方法 + /// + /// The EncodeQuestion + /// + /// The question + /// The output public void EncodeQuestion(IDnsQuestion question, IByteBuffer output) { EncodeName(question.Name, output); @@ -21,6 +51,11 @@ public void EncodeQuestion(IDnsQuestion question, IByteBuffer output) output.WriteShort((int)question.DnsClass); } + /// + /// The EncodeRecord + /// + /// The record + /// The output public void EncodeRecord(IDnsRecord record, IByteBuffer output) { if (record is IDnsQuestion) @@ -49,22 +84,90 @@ public void EncodeRecord(IDnsRecord record, IByteBuffer output) } } - private void EncodeRawRecord(IDnsRawRecord record, IByteBuffer output) + /// + /// The EncodeName + /// + /// The name + /// The buffer + protected void EncodeName(string name, IByteBuffer buffer) { - EncodeRecordCore(record, output); + if (ROOT.Equals(name)) + { + buffer.WriteByte(0); + return; + } - IByteBuffer content = record.Content; - int contentLen = content.ReadableBytes; - output.WriteShort(contentLen); - output.WriteBytes(content, content.ReaderIndex, contentLen); + string[] labels = name.Split('.'); + foreach (var label in labels) + { + int labelLen = label.Length; + if (labelLen == 0) + break; + + buffer.WriteByte(labelLen); + buffer.WriteBytes(Encoding.UTF8.GetBytes(label)); //TODO: Use ByteBufferUtil.WriteAscii() when available + } + buffer.WriteByte(0); } - private void EncodeOptPseudoRecord(IDnsOptPseudoRecord record, IByteBuffer output) + /// + /// The CalculateEcsAddressLength + /// + /// The sourcePrefixLength + /// The lowOrderBitsToPreserve + /// The + private static int CalculateEcsAddressLength(int sourcePrefixLength, int lowOrderBitsToPreserve) { - EncodeRecordCore(record, output); - output.WriteShort(0); + return sourcePrefixLength.RightUShift(3) + (lowOrderBitsToPreserve != 0 ? 1 : 0); + } + + /// + /// The PadWithZeros + /// + /// The b + /// The lowOrderBitsToPreserve + /// The + private static byte PadWithZeros(byte b, int lowOrderBitsToPreserve) + { + switch (lowOrderBitsToPreserve) + { + case 0: + return 0; + + case 1: + return (byte)(0x80 & b); + + case 2: + return (byte)(0xC0 & b); + + case 3: + return (byte)(0xE0 & b); + + case 4: + return (byte)(0xF0 & b); + + case 5: + return (byte)(0xF8 & b); + + case 6: + return (byte)(0xFC & b); + + case 7: + return (byte)(0xFE & b); + + case 8: + return b; + + default: + throw new ArgumentException($"lowOrderBitsToPreserve: {lowOrderBitsToPreserve}"); + } } + /// + /// The EncodeOptEcsRecord + /// + /// The record + /// The output private void EncodeOptEcsRecord(IDnsOptEcsRecord record, IByteBuffer output) { EncodeRecordCore(record, output); @@ -101,17 +204,48 @@ private void EncodeOptEcsRecord(IDnsOptEcsRecord record, IByteBuffer output) } } - private static int CalculateEcsAddressLength(int sourcePrefixLength, int lowOrderBitsToPreserve) + /// + /// The EncodeOptPseudoRecord + /// + /// The record + /// The output + private void EncodeOptPseudoRecord(IDnsOptPseudoRecord record, IByteBuffer output) { - return sourcePrefixLength.RightUShift(3) + (lowOrderBitsToPreserve != 0 ? 1 : 0); + EncodeRecordCore(record, output); + output.WriteShort(0); } + /// + /// The EncodePtrRecord + /// + /// The record + /// The output private void EncodePtrRecord(IDnsPtrRecord record, IByteBuffer output) { EncodeRecordCore(record, output); EncodeName(record.HostName, output); } + /// + /// The EncodeRawRecord + /// + /// The record + /// The output + private void EncodeRawRecord(IDnsRawRecord record, IByteBuffer output) + { + EncodeRecordCore(record, output); + + IByteBuffer content = record.Content; + int contentLen = content.ReadableBytes; + output.WriteShort(contentLen); + output.WriteBytes(content, content.ReaderIndex, contentLen); + } + + /// + /// The EncodeRecordCore + /// + /// The record + /// The output private void EncodeRecordCore(IDnsRecord record, IByteBuffer output) { EncodeName(record.Name, output); @@ -120,52 +254,6 @@ private void EncodeRecordCore(IDnsRecord record, IByteBuffer output) output.WriteInt((int)record.TimeToLive); } - protected void EncodeName(string name, IByteBuffer buffer) - { - if (ROOT.Equals(name)) - { - buffer.WriteByte(0); - return; - } - - string[] labels = name.Split('.'); - foreach (var label in labels) - { - int labelLen = label.Length; - if (labelLen == 0) - break; - - buffer.WriteByte(labelLen); - buffer.WriteBytes(Encoding.UTF8.GetBytes(label)); //TODO: Use ByteBufferUtil.WriteAscii() when available - } - buffer.WriteByte(0); - } - - private static byte PadWithZeros(byte b, int lowOrderBitsToPreserve) - { - switch (lowOrderBitsToPreserve) - { - case 0: - return 0; - case 1: - return (byte)(0x80 & b); - case 2: - return (byte)(0xC0 & b); - case 3: - return (byte)(0xE0 & b); - case 4: - return (byte)(0xF0 & b); - case 5: - return (byte)(0xF8 & b); - case 6: - return (byte)(0xFC & b); - case 7: - return (byte)(0xFE & b); - case 8: - return b; - default: - throw new ArgumentException($"lowOrderBitsToPreserve: {lowOrderBitsToPreserve}"); - } - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DnsOpCode.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DnsOpCode.cs index 0b3943885..fdaf1b4d7 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DnsOpCode.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DnsOpCode.cs @@ -4,47 +4,110 @@ namespace DotNetty.Codecs.DNS { + /// + /// Defines the + /// public class DnsOpCode { - public static readonly DnsOpCode QUERY = new DnsOpCode(0x00, "QUERY"); - + #region 字段 + + /// + /// Defines the IQUERY + /// public static readonly DnsOpCode IQUERY = new DnsOpCode(0x01, "IQUERY"); - - public static readonly DnsOpCode STATUS = new DnsOpCode(0x02, "STATUS"); - + + /// + /// Defines the NOTIFY + /// public static readonly DnsOpCode NOTIFY = new DnsOpCode(0x04, "NOTIFY"); - + + /// + /// Defines the QUERY + /// + public static readonly DnsOpCode QUERY = new DnsOpCode(0x00, "QUERY"); + + /// + /// Defines the STATUS + /// + public static readonly DnsOpCode STATUS = new DnsOpCode(0x02, "STATUS"); + + /// + /// Defines the UPDATE + /// public static readonly DnsOpCode UPDATE = new DnsOpCode(0x05, "UPDATE"); + /// + /// Defines the text + /// + private string text; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The byteValue + /// The name + public DnsOpCode(int byteValue, string name = "UNKNOWN") + { + Name = name ?? throw new ArgumentNullException(nameof(name)); + ByteValue = (byte)byteValue; + } + + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the ByteValue + /// public byte ByteValue { get; } + + /// + /// Gets the Name + /// public string Name { get; } - private string text; + #endregion 属性 + + #region 方法 + + /// + /// The From + /// + /// The byteValue + /// The public static DnsOpCode From(int byteValue) { switch (byteValue) { case 0x00: return QUERY; + case 0x01: return IQUERY; + case 0x02: return STATUS; + case 0x04: return NOTIFY; + case 0x05: return UPDATE; + default: return new DnsOpCode(byteValue); } } - public DnsOpCode(int byteValue, string name = "UNKNOWN") - { - Name = name ?? throw new ArgumentNullException(nameof(name)); - ByteValue = (byte)byteValue; - } - + /// + /// The Equals + /// + /// The obj + /// The public override bool Equals(object obj) { if (this == obj) @@ -56,11 +119,19 @@ public override bool Equals(object obj) return ByteValue == ((DnsOpCode)obj).ByteValue; } + /// + /// The GetHashCode + /// + /// The public override int GetHashCode() { return ByteValue; } + /// + /// The ToString + /// + /// The public override string ToString() { string text = this.text; @@ -69,5 +140,7 @@ public override string ToString() return text; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DnsQueryEncoder.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DnsQueryEncoder.cs index a29f10ec6..f4c4f6474 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DnsQueryEncoder.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DnsQueryEncoder.cs @@ -8,17 +8,47 @@ namespace DotNetty.Codecs.DNS { - sealed class DnsQueryEncoder + /// + /// Defines the + /// + internal sealed class DnsQueryEncoder { + #region 字段 + + /// + /// Defines the recordEncoder + /// private readonly IDnsRecordEncoder recordEncoder; - public DnsQueryEncoder() : this(new DefaultDnsRecordEncoder()) { } + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + public DnsQueryEncoder() : this(new DefaultDnsRecordEncoder()) + { + } + /// + /// Initializes a new instance of the class. + /// + /// The recordEncoder public DnsQueryEncoder(IDnsRecordEncoder recordEncoder) { this.recordEncoder = recordEncoder ?? throw new ArgumentNullException(nameof(recordEncoder)); } + #endregion 构造函数 + + #region 方法 + + /// + /// The Encode + /// + /// The query + /// The buffer public void Encode(IDnsQuery query, IByteBuffer buffer) { EncodeHeader(query, buffer); @@ -26,6 +56,11 @@ public void Encode(IDnsQuery query, IByteBuffer buffer) EncodeRecords(query, DnsSection.ADDITIONAL, buffer); } + /// + /// The EncodeHeader + /// + /// The query + /// The buffer private void EncodeHeader(IDnsQuery query, IByteBuffer buffer) { buffer.WriteShort(query.Id); @@ -41,6 +76,11 @@ private void EncodeHeader(IDnsQuery query, IByteBuffer buffer) buffer.WriteShort(query.Count(DnsSection.ADDITIONAL)); } + /// + /// The EncodeQuestions + /// + /// The query + /// The buffer private void EncodeQuestions(IDnsQuery query, IByteBuffer buffer) { int count = query.Count(DnsSection.QUESTION); @@ -50,6 +90,12 @@ private void EncodeQuestions(IDnsQuery query, IByteBuffer buffer) } } + /// + /// The EncodeRecords + /// + /// The query + /// The section + /// The buffer private void EncodeRecords(IDnsQuery query, DnsSection section, IByteBuffer buffer) { int count = query.Count(section); @@ -58,5 +104,7 @@ private void EncodeRecords(IDnsQuery query, DnsSection section, IByteBuffer buff recordEncoder.EncodeRecord(query.GetRecord(section, i), buffer); } } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DnsResponseDecoder.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DnsResponseDecoder.cs index 0876d940f..c4af835a3 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DnsResponseDecoder.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DnsResponseDecoder.cs @@ -7,15 +7,43 @@ namespace DotNetty.Codecs.DNS { - public class DnsResponseDecoder where T : EndPoint + /// + /// Defines the + /// + /// + public class DnsResponseDecoder where T : EndPoint { + #region 字段 + + /// + /// Defines the recordDecoder + /// private readonly IDnsRecordDecoder recordDecoder; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The recordDecoder public DnsResponseDecoder(IDnsRecordDecoder recordDecoder) { this.recordDecoder = recordDecoder ?? throw new ArgumentNullException(nameof(recordDecoder)); } + #endregion 构造函数 + + #region 方法 + + /// + /// The Decode + /// + /// The sender + /// The recipient + /// The buffer + /// The public IDnsResponse Decode(T sender, T recipient, IByteBuffer buffer) { int id = buffer.ReadUnsignedShort(); @@ -61,10 +89,24 @@ public IDnsResponse Decode(T sender, T recipient, IByteBuffer buffer) } } - protected virtual IDnsResponse NewResponse(T sender, T recipient, int id, - DnsOpCode opCode, DnsResponseCode responseCode) => new DefaultDnsResponse(id, opCode, responseCode); - + /// + /// The NewResponse + /// + /// The sender + /// The recipient + /// The id + /// The opCode + /// The responseCode + /// The + protected virtual IDnsResponse NewResponse(T sender, T recipient, int id, + DnsOpCode opCode, DnsResponseCode responseCode) => new DefaultDnsResponse(id, opCode, responseCode); + /// + /// The DecodeQuestions + /// + /// The response + /// The buf + /// The questionCount private void DecodeQuestions(IDnsResponse response, IByteBuffer buf, int questionCount) { for (int i = questionCount; i > 0; i--) @@ -73,18 +115,27 @@ private void DecodeQuestions(IDnsResponse response, IByteBuffer buf, int questio } } + /// + /// The DecodeRecords + /// + /// The response + /// The section + /// The buf + /// The count private void DecodeRecords(IDnsResponse response, DnsSection section, IByteBuffer buf, int count) { for (int i = count; i > 0; i--) { var r = recordDecoder.DecodeRecord(buf); if (r == null) - { + { break; } response.AddRecord(section, r); } } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DnsSection.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DnsSection.cs index 946502a12..1c40d78c2 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DnsSection.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/DnsSection.cs @@ -1,10 +1,32 @@ namespace DotNetty.Codecs.DNS { + #region 枚举 + + /// + /// Defines the DnsSection + /// public enum DnsSection { + /// + /// Defines the QUESTION + /// QUESTION, + + /// + /// Defines the ANSWER + /// ANSWER, + + /// + /// Defines the AUTHORITY + /// AUTHORITY, + + /// + /// Defines the ADDITIONAL + /// ADDITIONAL } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/IDnsRecordDecoder.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/IDnsRecordDecoder.cs index 39deb4c62..11d9df1b0 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/IDnsRecordDecoder.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/IDnsRecordDecoder.cs @@ -3,9 +3,31 @@ namespace DotNetty.Codecs.DNS { + #region 接口 + + /// + /// Defines the + /// public interface IDnsRecordDecoder { + #region 方法 + + /// + /// The DecodeQuestion + /// + /// The inputBuffer + /// The IDnsQuestion DecodeQuestion(IByteBuffer inputBuffer); + + /// + /// The DecodeRecord + /// + /// The inputBuffer + /// The IDnsRecord DecodeRecord(IByteBuffer inputBuffer); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/IDnsRecordEncoder.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/IDnsRecordEncoder.cs index e7521ae81..46eab73d3 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/IDnsRecordEncoder.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/IDnsRecordEncoder.cs @@ -3,9 +3,31 @@ namespace DotNetty.Codecs.DNS { + #region 接口 + + /// + /// Defines the + /// public interface IDnsRecordEncoder { + #region 方法 + + /// + /// The EncodeQuestion + /// + /// The question + /// The output void EncodeQuestion(IDnsQuestion question, IByteBuffer output); + + /// + /// The EncodeRecord + /// + /// The record + /// The output void EncodeRecord(IDnsRecord record, IByteBuffer output); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/AbstractDnsMessage.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/AbstractDnsMessage.cs index ecb245217..c24d19428 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/AbstractDnsMessage.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/AbstractDnsMessage.cs @@ -7,24 +7,74 @@ namespace DotNetty.Codecs.DNS.Messages { + /// + /// Defines the + /// public class AbstractDnsMessage : AbstractReferenceCounted, IDnsMessage { + #region 常量 + + /// + /// Defines the SECTION_COUNT + /// + private const int SECTION_COUNT = 4; + + /// + /// Defines the SECTION_QUESTION + /// + private const DnsSection SECTION_QUESTION = DnsSection.QUESTION; + + #endregion 常量 + + #region 字段 + + /// + /// Defines the leakDetector + /// private static readonly ResourceLeakDetector leakDetector = ResourceLeakDetector.Create(); + + /// + /// Defines the leak + /// private readonly IResourceLeakTracker leak; - private const DnsSection SECTION_QUESTION = DnsSection.QUESTION; - private const int SECTION_COUNT = 4; - private object questions; + + /// + /// Defines the additionals + /// + private object additionals; + + /// + /// Defines the answers + /// private object answers; + + /// + /// Defines the authorities + /// private object authorities; - private object additionals; - public int Id { get; set; } - public DnsOpCode OpCode { get; set; } - public bool IsRecursionDesired { get; set; } - public int Z { get; set; } + /// + /// Defines the questions + /// + private object questions; - protected AbstractDnsMessage(int id) : this(id, DnsOpCode.QUERY) { } + #endregion 字段 + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The id + protected AbstractDnsMessage(int id) : this(id, DnsOpCode.QUERY) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The id + /// The opcode protected AbstractDnsMessage(int id, DnsOpCode opcode) { Id = id; @@ -32,46 +82,39 @@ protected AbstractDnsMessage(int id, DnsOpCode opcode) leak = leakDetector.Track(this); } - public int Count() - { - int count = 0; - for (int i = 0; i < SECTION_COUNT; i++) - { - count += Count((DnsSection)i); - } - return count; - } + #endregion 构造函数 - public int Count(DnsSection section) - { - object records = SectionAt(section); - if (records == null) - return 0; + #region 属性 - if (records is IDnsRecord) - return 1; + /// + /// Gets or sets the Id + /// + public int Id { get; set; } - List recordList = (List)records; - return recordList.Count; - } + /// + /// Gets or sets a value indicating whether IsRecursionDesired + /// + public bool IsRecursionDesired { get; set; } - private object SectionAt(DnsSection section) - { - switch (section) - { - case DnsSection.QUESTION: - return questions; - case DnsSection.ANSWER: - return answers; - case DnsSection.AUTHORITY: - return authorities; - case DnsSection.ADDITIONAL: - return additionals; - default: - return null; - } - } + /// + /// Gets or sets the OpCode + /// + public DnsOpCode OpCode { get; set; } + + /// + /// Gets or sets the Z + /// + public int Z { get; set; } + + #endregion 属性 + #region 方法 + + /// + /// The AddRecord + /// + /// The section + /// The record public void AddRecord(DnsSection section, IDnsRecord record) { CheckQuestion(section, record); @@ -97,6 +140,12 @@ public void AddRecord(DnsSection section, IDnsRecord record) recordList.Add(record); } + /// + /// The AddRecord + /// + /// The section + /// The index + /// The record public void AddRecord(DnsSection section, int index, IDnsRecord record) { CheckQuestion(section, record); @@ -138,6 +187,21 @@ public void AddRecord(DnsSection section, int index, IDnsRecord record) recordList[index] = record; } + /// + /// The Clear + /// + public void Clear() + { + for (int i = 0; i < SECTION_COUNT; i++) + { + Clear((DnsSection)i); + } + } + + /// + /// The Clear + /// + /// The section public void Clear(DnsSection section) { object recordOrList = SectionAt(section); @@ -160,14 +224,81 @@ public void Clear(DnsSection section) } } - public void Clear() + /// + /// The Count + /// + /// The + public int Count() { + int count = 0; for (int i = 0; i < SECTION_COUNT; i++) { - Clear((DnsSection)i); + count += Count((DnsSection)i); } + return count; + } + + /// + /// The Count + /// + /// The section + /// The + public int Count(DnsSection section) + { + object records = SectionAt(section); + if (records == null) + return 0; + + if (records is IDnsRecord) + return 1; + + List recordList = (List)records; + return recordList.Count; } + /// + /// The Equals + /// + /// The obj + /// The + public override bool Equals(object obj) + { + if (this == obj) return true; + + if (!(obj is IDnsMessage)) return false; + + IDnsMessage that = (IDnsMessage)obj; + if (Id != that.Id) + return false; + + if (this is IDnsQuestion) + { + if (!(that is IDnsQuestion)) + return false; + } + else if (that is IDnsQuestion) + { + return false; + } + + return true; + } + + /// + /// The GetHashCode + /// + /// The + public override int GetHashCode() + { + return Id * 31 + (this is IDnsQuestion ? 0 : 1); + } + + /// + /// The GetRecord + /// + /// + /// The section + /// The public TRecord GetRecord(DnsSection section) where TRecord : IDnsRecord { object records = SectionAt(section); @@ -184,6 +315,13 @@ public TRecord GetRecord(DnsSection section) where TRecord : IDnsRecord return (TRecord)recordList[0]; } + /// + /// The GetRecord + /// + /// + /// The section + /// The index + /// The public TRecord GetRecord(DnsSection section, int index) where TRecord : IDnsRecord { object records = SectionAt(section); @@ -202,6 +340,11 @@ public TRecord GetRecord(DnsSection section, int index) where TRecord : return (TRecord)recordList[index]; } + /// + /// The RemoveRecord + /// + /// The section + /// The index public void RemoveRecord(DnsSection section, int index) { object records = SectionAt(section); @@ -220,12 +363,23 @@ public void RemoveRecord(DnsSection section, int index) recordList.RemoveAt(index); } + /// + /// The SetRecord + /// + /// The section + /// The record public void SetRecord(DnsSection section, IDnsRecord record) { Clear(section); SetSection(section, record); } + /// + /// The SetRecord + /// + /// The section + /// The index + /// The record public void SetRecord(DnsSection section, int index, IDnsRecord record) { CheckQuestion(section, record); @@ -250,33 +404,11 @@ public void SetRecord(DnsSection section, int index, IDnsRecord record) recordList[index] = record; } - private void SetSection(DnsSection section, object value) - { - switch (section) - { - case DnsSection.QUESTION: - questions = value; - break; - case DnsSection.ANSWER: - answers = value; - break; - case DnsSection.AUTHORITY: - authorities = value; - break; - case DnsSection.ADDITIONAL: - additionals = value; - break; - } - } - - private static void CheckQuestion(DnsSection section, IDnsRecord record) - { - if (section == SECTION_QUESTION && - record != null && - !(record is IDnsQuestion)) - throw new ArgumentException($"record: {record} (expected: DnsQuestion)"); - } - + /// + /// The Touch + /// + /// The hint + /// The public override IReferenceCounted Touch(object hint) { if (leak != null) @@ -285,39 +417,82 @@ public override IReferenceCounted Touch(object hint) return this; } + /// + /// The Deallocate + /// protected override void Deallocate() { Clear(); if (leak != null) - leak.Close(this); + leak.Close(this); } - public override bool Equals(object obj) + /// + /// The CheckQuestion + /// + /// The section + /// The record + private static void CheckQuestion(DnsSection section, IDnsRecord record) { - if (this == obj) return true; + if (section == SECTION_QUESTION && + record != null && + !(record is IDnsQuestion)) + throw new ArgumentException($"record: {record} (expected: DnsQuestion)"); + } - if (!(obj is IDnsMessage)) return false; + /// + /// The SectionAt + /// + /// The section + /// The + private object SectionAt(DnsSection section) + { + switch (section) + { + case DnsSection.QUESTION: + return questions; - IDnsMessage that = (IDnsMessage)obj; - if (Id != that.Id) - return false; + case DnsSection.ANSWER: + return answers; - if (this is IDnsQuestion) - { - if (!(that is IDnsQuestion)) - return false; - } - else if (that is IDnsQuestion) - { - return false; - } + case DnsSection.AUTHORITY: + return authorities; - return true; + case DnsSection.ADDITIONAL: + return additionals; + + default: + return null; + } } - public override int GetHashCode() + /// + /// The SetSection + /// + /// The section + /// The value + private void SetSection(DnsSection section, object value) { - return Id * 31 + (this is IDnsQuestion ? 0 : 1); + switch (section) + { + case DnsSection.QUESTION: + questions = value; + break; + + case DnsSection.ANSWER: + answers = value; + break; + + case DnsSection.AUTHORITY: + authorities = value; + break; + + case DnsSection.ADDITIONAL: + additionals = value; + break; + } } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/DatagramDnsQuery.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/DatagramDnsQuery.cs index ad5c3d9c8..f1b912144 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/DatagramDnsQuery.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/DatagramDnsQuery.cs @@ -4,25 +4,67 @@ namespace DotNetty.Codecs.DNS.Messages { + /// + /// Defines the + /// public class DatagramDnsQuery : DefaultDnsQuery, IAddressedEnvelope { - public DatagramDnsQuery Content => this; - - public EndPoint Sender { get; } - - public EndPoint Recipient { get; } + #region 构造函数 - public DatagramDnsQuery(EndPoint sender, EndPoint recipient, int id) : this(sender, recipient, id, DnsOpCode.QUERY) { } + /// + /// Initializes a new instance of the class. + /// + /// The sender + /// The recipient + /// The id + public DatagramDnsQuery(EndPoint sender, EndPoint recipient, int id) : this(sender, recipient, id, DnsOpCode.QUERY) + { + } + /// + /// Initializes a new instance of the class. + /// + /// The sender + /// The recipient + /// The id + /// The opCode public DatagramDnsQuery(EndPoint sender, EndPoint recipient, int id, DnsOpCode opCode) : base(id, opCode) { if (recipient == null && sender == null) throw new ArgumentNullException("recipient and sender"); - + Sender = sender; Recipient = recipient; } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the Content + /// + public DatagramDnsQuery Content => this; + + /// + /// Gets the Recipient + /// + public EndPoint Recipient { get; } + + /// + /// Gets the Sender + /// + public EndPoint Sender { get; } + + #endregion 属性 + + #region 方法 + + /// + /// The Equals + /// + /// The obj + /// The public override bool Equals(object obj) { if (this == obj) @@ -58,6 +100,10 @@ public override bool Equals(object obj) return true; } + /// + /// The GetHashCode + /// + /// The public override int GetHashCode() { int hashCode = base.GetHashCode(); @@ -69,5 +115,7 @@ public override int GetHashCode() return hashCode; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/DatagramDnsResponse.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/DatagramDnsResponse.cs index 874dc7008..53d115328 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/DatagramDnsResponse.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/DatagramDnsResponse.cs @@ -4,20 +4,44 @@ namespace DotNetty.Codecs.DNS.Messages { + /// + /// Defines the + /// public class DatagramDnsResponse : DefaultDnsResponse, IAddressedEnvelope { - public EndPoint Sender { get; } - - public EndPoint Recipient { get; } - - public DatagramDnsResponse Content=>this ; - + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The sender + /// The recipient + /// The id public DatagramDnsResponse(EndPoint sender, EndPoint recipient, int id) - : this(sender, recipient, id, DnsOpCode.QUERY, DnsResponseCode.NOERROR) { } + : this(sender, recipient, id, DnsOpCode.QUERY, DnsResponseCode.NOERROR) + { + } + /// + /// Initializes a new instance of the class. + /// + /// The sender + /// The recipient + /// The id + /// The opCode public DatagramDnsResponse(EndPoint sender, EndPoint recipient, int id, DnsOpCode opCode) - : this(sender, recipient, id, opCode, DnsResponseCode.NOERROR) { } + : this(sender, recipient, id, opCode, DnsResponseCode.NOERROR) + { + } + /// + /// Initializes a new instance of the class. + /// + /// The sender + /// The recipient + /// The id + /// The opCode + /// The responseCode public DatagramDnsResponse(EndPoint sender, EndPoint recipient, int id, DnsOpCode opCode, DnsResponseCode responseCode) : base(id, opCode, responseCode) { @@ -25,6 +49,34 @@ public DatagramDnsResponse(EndPoint sender, EndPoint recipient, int id, DnsOpCod Recipient = recipient ?? throw new ArgumentNullException(nameof(recipient)); } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the Content + /// + public DatagramDnsResponse Content => this; + + /// + /// Gets the Recipient + /// + public EndPoint Recipient { get; } + + /// + /// Gets the Sender + /// + public EndPoint Sender { get; } + + #endregion 属性 + + #region 方法 + + /// + /// The Equals + /// + /// The obj + /// The public override bool Equals(object obj) { if (this == obj) return true; @@ -58,6 +110,10 @@ public override bool Equals(object obj) return true; } + /// + /// The GetHashCode + /// + /// The public override int GetHashCode() { int hashCode = base.GetHashCode(); @@ -73,5 +129,7 @@ public override int GetHashCode() return hashCode; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/DefaultDnsQuery.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/DefaultDnsQuery.cs index b30ceec38..532f25d49 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/DefaultDnsQuery.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/DefaultDnsQuery.cs @@ -1,9 +1,29 @@ namespace DotNetty.Codecs.DNS.Messages { + /// + /// Defines the + /// public class DefaultDnsQuery : AbstractDnsMessage, IDnsQuery { - public DefaultDnsQuery(int id) : base(id) { } + #region 构造函数 - public DefaultDnsQuery(int id, DnsOpCode opCode) : base(id, opCode) { } + /// + /// Initializes a new instance of the class. + /// + /// The id + public DefaultDnsQuery(int id) : base(id) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The id + /// The opCode + public DefaultDnsQuery(int id, DnsOpCode opCode) : base(id, opCode) + { + } + + #endregion 构造函数 } -} +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/DefaultDnsResponse.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/DefaultDnsResponse.cs index 419bf9271..f26579354 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/DefaultDnsResponse.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/DefaultDnsResponse.cs @@ -3,28 +3,81 @@ namespace DotNetty.Codecs.DNS.Messages { + /// + /// Defines the + /// public class DefaultDnsResponse : AbstractDnsMessage, IDnsResponse { - public bool IsAuthoritativeAnswer { get; set; } - public bool IsTruncated { get; set; } - public bool IsRecursionAvailable { get; set; } - public DnsResponseCode Code { get; set; } + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The id public DefaultDnsResponse(int id) - : this(id, DnsOpCode.QUERY, DnsResponseCode.NOERROR) { } + : this(id, DnsOpCode.QUERY, DnsResponseCode.NOERROR) + { + } + /// + /// Initializes a new instance of the class. + /// + /// The id + /// The opCode public DefaultDnsResponse(int id, DnsOpCode opCode) - : this(id, opCode, DnsResponseCode.NOERROR) { } + : this(id, opCode, DnsResponseCode.NOERROR) + { + } + /// + /// Initializes a new instance of the class. + /// + /// The id + /// The opCode + /// The code public DefaultDnsResponse(int id, DnsOpCode opCode, DnsResponseCode code) : base(id, opCode) - { + { Code = code ?? throw new ArgumentNullException(nameof(code)); } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets or sets the Code + /// + public DnsResponseCode Code { get; set; } + + /// + /// Gets or sets a value indicating whether IsAuthoritativeAnswer + /// + public bool IsAuthoritativeAnswer { get; set; } + + /// + /// Gets or sets a value indicating whether IsRecursionAvailable + /// + public bool IsRecursionAvailable { get; set; } + + /// + /// Gets or sets a value indicating whether IsTruncated + /// + public bool IsTruncated { get; set; } + + #endregion 属性 + + #region 方法 + + /// + /// The ToString + /// + /// The public override string ToString() { return new StringBuilder(128).AppendResponse(this).ToString(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/DnsResponseCode.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/DnsResponseCode.cs index 9f6b40086..7992c2622 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/DnsResponseCode.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/DnsResponseCode.cs @@ -2,33 +2,112 @@ namespace DotNetty.Codecs.DNS.Messages { + /// + /// Defines the + /// public class DnsResponseCode { - public static DnsResponseCode NOERROR = new DnsResponseCode(0, "NoError"); + #region 字段 + + /// + /// Defines the BADALG + /// + public static DnsResponseCode BADALG = new DnsResponseCode(21, "BADALG"); + + /// + /// Defines the BADKEY + /// + public static DnsResponseCode BADKEY = new DnsResponseCode(17, "BADKEY"); + + /// + /// Defines the BADMODE + /// + public static DnsResponseCode BADMODE = new DnsResponseCode(19, "BADMODE"); + + /// + /// Defines the BADNAME + /// + public static DnsResponseCode BADNAME = new DnsResponseCode(20, "BADNAME"); + + /// + /// Defines the BADTIME + /// + public static DnsResponseCode BADTIME = new DnsResponseCode(18, "BADTIME"); + + /// + /// Defines the BADVERS_OR_BADSIG + /// + public static DnsResponseCode BADVERS_OR_BADSIG = new DnsResponseCode(16, "BADVERS_OR_BADSIG"); + + /// + /// Defines the FORMERR + /// public static DnsResponseCode FORMERR = new DnsResponseCode(1, "FormErr"); - public static DnsResponseCode SERVFAIL = new DnsResponseCode(2, "ServFail"); - public static DnsResponseCode NXDOMAIN = new DnsResponseCode(3, "NXDomain"); + + /// + /// Defines the NOERROR + /// + public static DnsResponseCode NOERROR = new DnsResponseCode(0, "NoError"); + + /// + /// Defines the NOTAUTH + /// + public static DnsResponseCode NOTAUTH = new DnsResponseCode(9, "NotAuth"); + + /// + /// Defines the NOTIMP + /// public static DnsResponseCode NOTIMP = new DnsResponseCode(4, "NotImp"); + + /// + /// Defines the NOTZONE + /// + public static DnsResponseCode NOTZONE = new DnsResponseCode(10, "NotZone"); + + /// + /// Defines the NXDOMAIN + /// + public static DnsResponseCode NXDOMAIN = new DnsResponseCode(3, "NXDomain"); + + /// + /// Defines the NXRRSET + /// + public static DnsResponseCode NXRRSET = new DnsResponseCode(8, "NXRRSet"); + + /// + /// Defines the REFUSED + /// public static DnsResponseCode REFUSED = new DnsResponseCode(5, "Refused"); + + /// + /// Defines the SERVFAIL + /// + public static DnsResponseCode SERVFAIL = new DnsResponseCode(2, "ServFail"); + + /// + /// Defines the YXDOMAIN + /// public static DnsResponseCode YXDOMAIN = new DnsResponseCode(6, "YXDomain"); + + /// + /// Defines the YXRRSET + /// public static DnsResponseCode YXRRSET = new DnsResponseCode(7, "YXRRSet"); - public static DnsResponseCode NXRRSET = new DnsResponseCode(8, "NXRRSet"); - public static DnsResponseCode NOTAUTH = new DnsResponseCode(9, "NotAuth"); - public static DnsResponseCode NOTZONE = new DnsResponseCode(10, "NotZone"); - public static DnsResponseCode BADVERS_OR_BADSIG = new DnsResponseCode(16, "BADVERS_OR_BADSIG"); - public static DnsResponseCode BADKEY = new DnsResponseCode(17, "BADKEY"); - public static DnsResponseCode BADTIME = new DnsResponseCode(18, "BADTIME"); - public static DnsResponseCode BADMODE = new DnsResponseCode(19, "BADMODE"); - public static DnsResponseCode BADNAME = new DnsResponseCode(20, "BADNAME"); - public static DnsResponseCode BADALG = new DnsResponseCode(21, "BADALG"); + /// + /// Defines the text + /// private string text; - public int IntValue { get; } - public string Name { get; } + #endregion 字段 - private DnsResponseCode(int code) : this(code, "UNKNOWN") { } + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The code + /// The name public DnsResponseCode(int code, string name) { if (code < 0 || code > 65535) @@ -38,54 +117,102 @@ public DnsResponseCode(int code, string name) Name = name ?? throw new ArgumentNullException(nameof(name)); } + /// + /// Prevents a default instance of the class from being created. + /// + /// The code + private DnsResponseCode(int code) : this(code, "UNKNOWN") + { + } + + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the IntValue + /// + public int IntValue { get; } + + /// + /// Gets the Name + /// + public string Name { get; } + + #endregion 属性 + + #region 方法 + + /// + /// The From + /// + /// The responseCode + /// The public static DnsResponseCode From(int responseCode) { switch (responseCode) { case 0: return NOERROR; + case 1: return FORMERR; + case 2: return SERVFAIL; + case 3: return NXDOMAIN; + case 4: return NOTIMP; + case 5: return REFUSED; + case 6: return YXDOMAIN; + case 7: return YXRRSET; + case 8: return NXRRSET; + case 9: return NOTAUTH; + case 10: return NOTZONE; + case 16: return BADVERS_OR_BADSIG; + case 17: return BADKEY; + case 18: return BADTIME; + case 19: return BADMODE; + case 20: return BADNAME; + case 21: return BADALG; + default: return new DnsResponseCode(responseCode); } } - public override int GetHashCode() - { - return IntValue; - } - + /// + /// The Equals + /// + /// The obj + /// The public override bool Equals(object obj) { if (!(obj is DnsResponseCode)) @@ -94,6 +221,19 @@ public override bool Equals(object obj) return IntValue == ((DnsResponseCode)obj).IntValue; } + /// + /// The GetHashCode + /// + /// The + public override int GetHashCode() + { + return IntValue; + } + + /// + /// The ToString + /// + /// The public override string ToString() { string text = this.text; @@ -101,5 +241,7 @@ public override string ToString() this.text = text = $"{Name} ({IntValue})"; return text; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/IDnsMessage.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/IDnsMessage.cs index bbd8c4c7f..6878b2019 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/IDnsMessage.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/IDnsMessage.cs @@ -1,24 +1,121 @@ -using DotNetty.Common; -using DotNetty.Codecs.DNS.Records; +using DotNetty.Codecs.DNS.Records; +using DotNetty.Common; namespace DotNetty.Codecs.DNS.Messages { + #region 接口 + + /// + /// Defines the + /// public interface IDnsMessage : IReferenceCounted { + #region 属性 + + /// + /// Gets or sets the Id + /// int Id { get; set; } - DnsOpCode OpCode { get; set; } + + /// + /// Gets or sets a value indicating whether IsRecursionDesired + /// bool IsRecursionDesired { get; set; } + + /// + /// Gets or sets the OpCode + /// + DnsOpCode OpCode { get; set; } + + /// + /// Gets or sets the Z + /// int Z { get; set; } - int Count(DnsSection section); + + #endregion 属性 + + #region 方法 + + /// + /// The AddRecord + /// + /// The section + /// The record + void AddRecord(DnsSection section, IDnsRecord record); + + /// + /// The AddRecord + /// + /// The section + /// The index + /// The record + void AddRecord(DnsSection section, int index, IDnsRecord record); + + /// + /// The Clear + /// + void Clear(); + + /// + /// The Clear + /// + /// The section + void Clear(DnsSection section); + + /// + /// The Count + /// + /// The int Count(); + + /// + /// The Count + /// + /// The section + /// The + int Count(DnsSection section); + + /// + /// The GetRecord + /// + /// + /// The section + /// The TRecord GetRecord(DnsSection section) where TRecord : IDnsRecord; + + /// + /// The GetRecord + /// + /// + /// The section + /// The index + /// The TRecord GetRecord(DnsSection section, int index) where TRecord : IDnsRecord; + + /// + /// The RemoveRecord + /// + /// The section + /// The index + void RemoveRecord(DnsSection section, int index); + + /// + /// The SetRecord + /// + /// The section + /// The record void SetRecord(DnsSection section, IDnsRecord record); + + /// + /// The SetRecord + /// + /// The section + /// The index + /// The record void SetRecord(DnsSection section, int index, IDnsRecord record); - void AddRecord(DnsSection section, IDnsRecord record); - void AddRecord(DnsSection section, int index, IDnsRecord record); - void RemoveRecord(DnsSection section, int index); - void Clear(DnsSection section); - void Clear(); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/IDnsQuery.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/IDnsQuery.cs index 806417d9d..32f382dbd 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/IDnsQuery.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/IDnsQuery.cs @@ -1,8 +1,13 @@ - -namespace DotNetty.Codecs.DNS.Messages +namespace DotNetty.Codecs.DNS.Messages { + #region 接口 + + /// + /// Defines the + /// public interface IDnsQuery : IDnsMessage { - } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/IDnsResponse.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/IDnsResponse.cs index 71335b010..649382b38 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/IDnsResponse.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Messages/IDnsResponse.cs @@ -1,10 +1,36 @@ namespace DotNetty.Codecs.DNS.Messages { + #region 接口 + + /// + /// Defines the + /// public interface IDnsResponse : IDnsMessage { + #region 属性 + + /// + /// Gets or sets the Code + /// + DnsResponseCode Code { get; set; } + + /// + /// Gets or sets a value indicating whether IsAuthoritativeAnswer + /// bool IsAuthoritativeAnswer { get; set; } - bool IsTruncated { get; set; } + + /// + /// Gets or sets a value indicating whether IsRecursionAvailable + /// bool IsRecursionAvailable { get; set; } - DnsResponseCode Code { get; set; } + + /// + /// Gets or sets a value indicating whether IsTruncated + /// + bool IsTruncated { get; set; } + + #endregion 属性 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/AbstractDnsOptPseudoRrRecord.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/AbstractDnsOptPseudoRrRecord.cs index 07297a90f..fbb239ba5 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/AbstractDnsOptPseudoRrRecord.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/AbstractDnsOptPseudoRrRecord.cs @@ -3,32 +3,78 @@ namespace DotNetty.Codecs.DNS.Records { + /// + /// Defines the + /// public abstract class AbstractDnsOptPseudoRrRecord : AbstractDnsRecord, IDnsOptPseudoRecord { + #region 常量 + + /// + /// Defines the EMPTY_STRING + /// private const string EMPTY_STRING = ""; - protected AbstractDnsOptPseudoRrRecord(int maxPayloadSize, int extendedRcode, int version) - : base(EMPTY_STRING, DnsRecordType.OPT, PackIntoLong(extendedRcode, version), (DnsRecordClass)maxPayloadSize ) { } + #endregion 常量 + + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The maxPayloadSize protected AbstractDnsOptPseudoRrRecord(int maxPayloadSize) - : base(EMPTY_STRING, DnsRecordType.OPT, 0, (DnsRecordClass)maxPayloadSize) { } + : base(EMPTY_STRING, DnsRecordType.OPT, 0, (DnsRecordClass)maxPayloadSize) + { + } - private static long PackIntoLong(int val, int val2) + /// + /// Initializes a new instance of the class. + /// + /// The maxPayloadSize + /// The extendedRcode + /// The version + protected AbstractDnsOptPseudoRrRecord(int maxPayloadSize, int extendedRcode, int version) + : base(EMPTY_STRING, DnsRecordType.OPT, PackIntoLong(extendedRcode, version), (DnsRecordClass)maxPayloadSize) { - return ((val & 0xff) << 24 | (val2 & 0xff) << 16 | (0 & 0xff) << 8 | 0 & 0xff) & 0xFFFFFFFFL; } - public int ExtendedRcode => (short) (((int) TimeToLive >> 16) & 0xff); + #endregion 构造函数 - public int Version => (short)(((int)TimeToLive >> 16) & 0xff); + #region 属性 + + /// + /// Gets the ExtendedRcode + /// + public int ExtendedRcode => (short)(((int)TimeToLive >> 16) & 0xff); + /// + /// Gets the Flags + /// public int Flags => (short)((short)TimeToLive & 0xff); + /// + /// Gets the Version + /// + public int Version => (short)(((int)TimeToLive >> 16) & 0xff); + + #endregion 属性 + + #region 方法 + + /// + /// The ToString + /// + /// The public override string ToString() { return GetBuilder().ToString(); } + /// + /// The GetBuilder + /// + /// The protected StringBuilder GetBuilder() { return new StringBuilder(64) @@ -44,5 +90,18 @@ protected StringBuilder GetBuilder() .Append(DnsClass) .Append(')'); } + + /// + /// The PackIntoLong + /// + /// The val + /// The val2 + /// The + private static long PackIntoLong(int val, int val2) + { + return ((val & 0xff) << 24 | (val2 & 0xff) << 16 | (0 & 0xff) << 8 | 0 & 0xff) & 0xFFFFFFFFL; + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/AbstractDnsRecord.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/AbstractDnsRecord.cs index bd8003321..21f6f1b17 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/AbstractDnsRecord.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/AbstractDnsRecord.cs @@ -5,17 +5,75 @@ namespace DotNetty.Codecs.DNS.Records { + #region 枚举 + + /// + /// Defines the DnsRecordClass + /// + public enum DnsRecordClass : int + { + /// + /// Defines the IN + /// + IN = 0x0001, + + /// + /// Defines the CSNET + /// + CSNET = 0x0002, + + /// + /// Defines the CHAOS + /// + CHAOS = 0x0003, + + /// + /// Defines the HESIOD + /// + HESIOD = 0x0004, + + /// + /// Defines the NONE + /// + NONE = 0x00fe, + + /// + /// Defines the ANY + /// + ANY = 0x00ff + } + + #endregion 枚举 + + /// + /// Defines the + /// public abstract class AbstractDnsRecord : IDnsRecord { + #region 字段 + + /// + /// Defines the idn + /// private readonly IdnMapping idn = new IdnMapping(); + + /// + /// Defines the hashCode + /// private int hashCode; - public DnsRecordType Type { get; } - public string Name { get; } - public DnsRecordClass DnsClass { get; } - public long TimeToLive { get; set; } + #endregion 字段 + + #region 构造函数 - protected AbstractDnsRecord(string name, DnsRecordType type, + /// + /// Initializes a new instance of the class. + /// + /// The name + /// The type + /// The timeToLive + /// The dnsClass + protected AbstractDnsRecord(string name, DnsRecordType type, long timeToLive, DnsRecordClass dnsClass = DnsRecordClass.IN) { if (TimeToLive < 0) @@ -31,14 +89,39 @@ protected AbstractDnsRecord(string name, DnsRecordType type, DnsClass = dnsClass; } - private static string AppendTrailingDot(string name) - { - if (name.Length > 0 && !name.EndsWith(".")) - return name + "."; + #endregion 构造函数 - return name; - } + #region 属性 + + /// + /// Gets the DnsClass + /// + public DnsRecordClass DnsClass { get; } + + /// + /// Gets the Name + /// + public string Name { get; } + + /// + /// Gets or sets the TimeToLive + /// + public long TimeToLive { get; set; } + /// + /// Gets the Type + /// + public DnsRecordType Type { get; } + + #endregion 属性 + + #region 方法 + + /// + /// The Equals + /// + /// The obj + /// The public override bool Equals(object obj) { if (this == obj) @@ -57,17 +140,24 @@ public override bool Equals(object obj) Name.Equals(that.Name); } + /// + /// The GetHashCode + /// + /// The public override int GetHashCode() { int hashCode = this.hashCode; if (hashCode != 0) return hashCode; - return this.hashCode = Name.GetHashCode() * 31 + + return this.hashCode = Name.GetHashCode() * 31 + Type.IntValue * 31 + (int)DnsClass; - } + /// + /// The ToString + /// + /// The public override string ToString() { var builder = new StringBuilder(64); @@ -85,16 +175,19 @@ public override string ToString() return builder.ToString(); } - } + /// + /// The AppendTrailingDot + /// + /// The name + /// The + private static string AppendTrailingDot(string name) + { + if (name.Length > 0 && !name.EndsWith(".")) + return name + "."; - public enum DnsRecordClass : int - { - IN = 0x0001, - CSNET = 0x0002, - CHAOS = 0x0003, - HESIOD = 0x0004, - NONE = 0x00fe, - ANY = 0x00ff + return name; + } + + #endregion 方法 } - -} +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/DefaultDnsOptEcsRecord.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/DefaultDnsOptEcsRecord.cs index 43e62bd04..cb398127e 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/DefaultDnsOptEcsRecord.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/DefaultDnsOptEcsRecord.cs @@ -4,17 +4,35 @@ namespace DotNetty.Codecs.DNS.Records { + /// + /// Defines the + /// public class DefaultDnsOptEcsRecord : AbstractDnsOptPseudoRrRecord, IDnsOptEcsRecord { - private readonly int srcPrefixLength; + #region 字段 + + /// + /// Defines the address + /// private readonly byte[] address; - public int SourcePrefixLength { get; } + /// + /// Defines the srcPrefixLength + /// + private readonly int srcPrefixLength; - public int ScopePrefixLength => 0; + #endregion 字段 - public byte[] Address => (byte[])address.Clone(); + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The maxPayloadSize + /// The extendedRcode + /// The version + /// The srcPrefixLength + /// The address public DefaultDnsOptEcsRecord(int maxPayloadSize, int extendedRcode, int version, int srcPrefixLength, byte[] address) : base(maxPayloadSize, extendedRcode, version) { @@ -22,20 +40,54 @@ public DefaultDnsOptEcsRecord(int maxPayloadSize, int extendedRcode, int version address = VerifyAddress(address); } + /// + /// Initializes a new instance of the class. + /// + /// The maxPayloadSize + /// The srcPrefixLength + /// The address public DefaultDnsOptEcsRecord(int maxPayloadSize, int srcPrefixLength, byte[] address) - : this(maxPayloadSize, 0, 0, srcPrefixLength, address) { } + : this(maxPayloadSize, 0, 0, srcPrefixLength, address) + { + } + /// + /// Initializes a new instance of the class. + /// + /// The maxPayloadSize + /// The address public DefaultDnsOptEcsRecord(int maxPayloadSize, IPAddress address) - : this(maxPayloadSize, 0, 0, 0, address.GetAddressBytes()) { } - - private static byte[] VerifyAddress(byte[] bytes) + : this(maxPayloadSize, 0, 0, 0, address.GetAddressBytes()) { - if (bytes.Length == 4 || bytes.Length == 16) - return bytes; - - throw new ArgumentException("bytes.length must either 4 or 16"); } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the Address + /// + public byte[] Address => (byte[])address.Clone(); + + /// + /// Gets the ScopePrefixLength + /// + public int ScopePrefixLength => 0; + + /// + /// Gets the SourcePrefixLength + /// + public int SourcePrefixLength { get; } + + #endregion 属性 + + #region 方法 + + /// + /// The ToString + /// + /// The public override string ToString() { StringBuilder builder = GetBuilder(); @@ -48,5 +100,20 @@ public override string ToString() .Append(ScopePrefixLength) .Append(')').ToString(); } + + /// + /// The VerifyAddress + /// + /// The bytes + /// The + private static byte[] VerifyAddress(byte[] bytes) + { + if (bytes.Length == 4 || bytes.Length == 16) + return bytes; + + throw new ArgumentException("bytes.length must either 4 or 16"); + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/DefaultDnsPtrRecord.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/DefaultDnsPtrRecord.cs index fea8f293e..818a78e8a 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/DefaultDnsPtrRecord.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/DefaultDnsPtrRecord.cs @@ -4,10 +4,20 @@ namespace DotNetty.Codecs.DNS.Records { + /// + /// Defines the + /// public class DefaultDnsPtrRecord : AbstractDnsRecord, IDnsPtrRecord { - public string HostName { get; } + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The name + /// The dnsClass + /// The timeToLive + /// The hostname public DefaultDnsPtrRecord(string name, DnsRecordClass dnsClass, long timeToLive, string hostname) : base(name, DnsRecordType.PTR, timeToLive, dnsClass) { @@ -17,6 +27,23 @@ public DefaultDnsPtrRecord(string name, DnsRecordClass dnsClass, long timeToLive HostName = hostname; } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the HostName + /// + public string HostName { get; } + + #endregion 属性 + + #region 方法 + + /// + /// The ToString + /// + /// The public override string ToString() { var builder = new StringBuilder(64); @@ -35,5 +62,7 @@ public override string ToString() return builder.ToString(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/DefaultDnsQuestion.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/DefaultDnsQuestion.cs index d05c079a8..ebfd0a490 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/DefaultDnsQuestion.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/DefaultDnsQuestion.cs @@ -3,18 +3,55 @@ namespace DotNetty.Codecs.DNS.Records { + /// + /// Defines the + /// public class DefaultDnsQuestion : AbstractDnsRecord, IDnsQuestion { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The name + /// The type + public DefaultDnsQuestion(string name, DnsRecordType type) : base(name, type, 0) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The name + /// The type + /// The dnsClass + public DefaultDnsQuestion(string name, DnsRecordType type, DnsRecordClass dnsClass) : + base(name, type, 0, dnsClass) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The name + /// The type + /// The timeToLive + /// The dnsClass public DefaultDnsQuestion(string name, DnsRecordType type, long timeToLive, DnsRecordClass dnsClass = DnsRecordClass.IN) - : base(name, type, timeToLive, dnsClass) { } + : base(name, type, timeToLive, dnsClass) + { + } - public DefaultDnsQuestion(string name, DnsRecordType type) : base(name, type, 0) { } + #endregion 构造函数 - public DefaultDnsQuestion(string name, DnsRecordType type, DnsRecordClass dnsClass) : - base(name, type, 0, dnsClass){ } + #region 方法 + /// + /// The ToString + /// + /// The public override string ToString() { var builder = new StringBuilder(64); @@ -30,5 +67,7 @@ public override string ToString() return builder.ToString(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/DefaultDnsRawRecord.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/DefaultDnsRawRecord.cs index f2c4c352e..4b935fc47 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/DefaultDnsRawRecord.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/DefaultDnsRawRecord.cs @@ -1,73 +1,137 @@ -using System; -using System.Text; -using DotNetty.Buffers; +using DotNetty.Buffers; using DotNetty.Common; +using System; using System.Reflection; +using System.Text; namespace DotNetty.Codecs.DNS.Records { + /// + /// Defines the + /// public class DefaultDnsRawRecord : AbstractDnsRecord, IDnsRawRecord { - public IByteBuffer Content { get; } - - public int ReferenceCount { get; } + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The name + /// The type + /// The dnsClass + /// The timeToLive + /// The content + public DefaultDnsRawRecord(string name, DnsRecordType type, DnsRecordClass dnsClass, + long timeToLive, IByteBuffer content) : base(name, type, timeToLive, dnsClass) + { + Content = content ?? throw new ArgumentNullException(nameof(content)); + } + /// + /// Initializes a new instance of the class. + /// + /// The name + /// The type + /// The timeToLive + /// The content public DefaultDnsRawRecord(string name, DnsRecordType type, long timeToLive, IByteBuffer content) : this(name, type, DnsRecordClass.IN, timeToLive, content) { } - public DefaultDnsRawRecord(string name, DnsRecordType type, DnsRecordClass dnsClass, - long timeToLive, IByteBuffer content) : base(name, type, timeToLive, dnsClass) - { - Content = content ?? throw new ArgumentNullException(nameof(content)); - } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the Content + /// + public IByteBuffer Content { get; } + + /// + /// Gets the ReferenceCount + /// + public int ReferenceCount { get; } + + #endregion 属性 + + #region 方法 + /// + /// The Copy + /// + /// The public IByteBufferHolder Copy() { return Replace(Content.Copy()); } + /// + /// The Duplicate + /// + /// The public IByteBufferHolder Duplicate() { return Replace(Content.Duplicate()); } + /// + /// The Release + /// + /// The public bool Release() { return Content.Release(); } + /// + /// The Release + /// + /// The decrement + /// The public bool Release(int decrement) { return Content.Release(decrement); } + /// + /// The Replace + /// + /// The content + /// The + public virtual IByteBufferHolder Replace(IByteBuffer content) => new DefaultByteBufferHolder(content); + + /// + /// The Retain + /// + /// The public IReferenceCounted Retain() { Content.Retain(); return this; } + /// + /// The Retain + /// + /// The increment + /// The public IReferenceCounted Retain(int increment) { Content.Retain(increment); return this; } - public IReferenceCounted Touch() - { - Content.Touch(); - return this; - } - - public IReferenceCounted Touch(object hint) - { - Content.Touch(hint); - return this; - } - + /// + /// The RetainedDuplicate + /// + /// The + public IByteBufferHolder RetainedDuplicate() => this.Replace(this.Content.RetainedDuplicate()); + /// + /// The ToString + /// + /// The public override string ToString() { var builder = new StringBuilder(64); @@ -98,8 +162,27 @@ public override string ToString() return builder.ToString(); } - public IByteBufferHolder RetainedDuplicate() => this.Replace(this.Content.RetainedDuplicate()); + /// + /// The Touch + /// + /// The + public IReferenceCounted Touch() + { + Content.Touch(); + return this; + } - public virtual IByteBufferHolder Replace(IByteBuffer content) => new DefaultByteBufferHolder(content); + /// + /// The Touch + /// + /// The hint + /// The + public IReferenceCounted Touch(object hint) + { + Content.Touch(hint); + return this; + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/DnsRecordType.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/DnsRecordType.cs index 46f22cd1a..7447c310a 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/DnsRecordType.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/DnsRecordType.cs @@ -9,98 +9,237 @@ namespace DotNetty.Codecs.DNS.Records /// public class DnsRecordType { - #region Types - public static readonly DnsRecordType A = new DnsRecordType(0x0001, "A"); + #region 字段 - public static readonly DnsRecordType NS = new DnsRecordType(0x0002, "NS"); + /// + /// Defines the A + /// + public static readonly DnsRecordType A = new DnsRecordType(0x0001, "A"); - public static readonly DnsRecordType CNAME = new DnsRecordType(0x0005, "CNAME"); + /// + /// Defines the AAAA + /// + public static readonly DnsRecordType AAAA = new DnsRecordType(0x001c, "AAAA"); - public static readonly DnsRecordType SOA = new DnsRecordType(0x0006, "SOA"); + /// + /// Defines the AFSDB + /// + public static readonly DnsRecordType AFSDB = new DnsRecordType(0x0012, "AFSDB"); - public static readonly DnsRecordType PTR = new DnsRecordType(0x000c, "PTR"); + /// + /// Defines the ANY + /// + public static readonly DnsRecordType ANY = new DnsRecordType(0x00ff, "ANY"); - public static readonly DnsRecordType MX = new DnsRecordType(0x000f, "MX"); + /// + /// Defines the APL + /// + public static readonly DnsRecordType APL = new DnsRecordType(0x002a, "APL"); - public static readonly DnsRecordType TXT = new DnsRecordType(0x0010, "TXT"); + /// + /// Defines the AXFR + /// + public static readonly DnsRecordType AXFR = new DnsRecordType(0x00fc, "AXFR"); - public static readonly DnsRecordType RP = new DnsRecordType(0x0011, "RP"); + /// + /// Defines the CAA + /// + public static readonly DnsRecordType CAA = new DnsRecordType(0x0101, "CAA"); - public static readonly DnsRecordType AFSDB = new DnsRecordType(0x0012, "AFSDB"); + /// + /// Defines the CERT + /// + public static readonly DnsRecordType CERT = new DnsRecordType(0x0025, "CERT"); - public static readonly DnsRecordType SIG = new DnsRecordType(0x0018, "SIG"); + /// + /// Defines the CNAME + /// + public static readonly DnsRecordType CNAME = new DnsRecordType(0x0005, "CNAME"); - public static readonly DnsRecordType KEY = new DnsRecordType(0x0019, "KEY"); + /// + /// Defines the DHCID + /// + public static readonly DnsRecordType DHCID = new DnsRecordType(0x0031, "DHCID"); - public static readonly DnsRecordType AAAA = new DnsRecordType(0x001c, "AAAA"); + /// + /// Defines the DLV + /// + public static readonly DnsRecordType DLV = new DnsRecordType(0x8001, "DLV"); - public static readonly DnsRecordType LOC = new DnsRecordType(0x001d, "LOC"); + /// + /// Defines the DNAME + /// + public static readonly DnsRecordType DNAME = new DnsRecordType(0x0027, "DNAME"); - public static readonly DnsRecordType SRV = new DnsRecordType(0x0021, "SRV"); + /// + /// Defines the DNSKEY + /// + public static readonly DnsRecordType DNSKEY = new DnsRecordType(0x0030, "DNSKEY"); - public static readonly DnsRecordType NAPTR = new DnsRecordType(0x0023, "NAPTR"); + /// + /// Defines the DS + /// + public static readonly DnsRecordType DS = new DnsRecordType(0x002b, "DS"); - public static readonly DnsRecordType KX = new DnsRecordType(0x0024, "KX"); + /// + /// Defines the HIP + /// + public static readonly DnsRecordType HIP = new DnsRecordType(0x0037, "HIP"); - public static readonly DnsRecordType CERT = new DnsRecordType(0x0025, "CERT"); + /// + /// Defines the IPSECKEY + /// + public static readonly DnsRecordType IPSECKEY = new DnsRecordType(0x002d, "IPSECKEY"); - public static readonly DnsRecordType DNAME = new DnsRecordType(0x0027, "DNAME"); + /// + /// Defines the IXFR + /// + public static readonly DnsRecordType IXFR = new DnsRecordType(0x00fb, "IXFR"); - public static readonly DnsRecordType OPT = new DnsRecordType(0x0029, "OPT"); + /// + /// Defines the KEY + /// + public static readonly DnsRecordType KEY = new DnsRecordType(0x0019, "KEY"); - public static readonly DnsRecordType APL = new DnsRecordType(0x002a, "APL"); + /// + /// Defines the KX + /// + public static readonly DnsRecordType KX = new DnsRecordType(0x0024, "KX"); - public static readonly DnsRecordType DS = new DnsRecordType(0x002b, "DS"); + /// + /// Defines the LOC + /// + public static readonly DnsRecordType LOC = new DnsRecordType(0x001d, "LOC"); - public static readonly DnsRecordType SSHFP = new DnsRecordType(0x002c, "SSHFP"); + /// + /// Defines the MX + /// + public static readonly DnsRecordType MX = new DnsRecordType(0x000f, "MX"); - public static readonly DnsRecordType IPSECKEY = new DnsRecordType(0x002d, "IPSECKEY"); + /// + /// Defines the NAPTR + /// + public static readonly DnsRecordType NAPTR = new DnsRecordType(0x0023, "NAPTR"); - public static readonly DnsRecordType RRSIG = new DnsRecordType(0x002e, "RRSIG"); + /// + /// Defines the NS + /// + public static readonly DnsRecordType NS = new DnsRecordType(0x0002, "NS"); + /// + /// Defines the NSEC + /// public static readonly DnsRecordType NSEC = new DnsRecordType(0x002f, "NSEC"); - public static readonly DnsRecordType DNSKEY = new DnsRecordType(0x0030, "DNSKEY"); - - public static readonly DnsRecordType DHCID = new DnsRecordType(0x0031, "DHCID"); - + /// + /// Defines the NSEC3 + /// public static readonly DnsRecordType NSEC3 = new DnsRecordType(0x0032, "NSEC3"); + /// + /// Defines the NSEC3PARAM + /// public static readonly DnsRecordType NSEC3PARAM = new DnsRecordType(0x0033, "NSEC3PARAM"); - public static readonly DnsRecordType TLSA = new DnsRecordType(0x0034, "TLSA"); + /// + /// Defines the OPT + /// + public static readonly DnsRecordType OPT = new DnsRecordType(0x0029, "OPT"); - public static readonly DnsRecordType HIP = new DnsRecordType(0x0037, "HIP"); + /// + /// Defines the PTR + /// + public static readonly DnsRecordType PTR = new DnsRecordType(0x000c, "PTR"); - public static readonly DnsRecordType SPF = new DnsRecordType(0x0063, "SPF"); + /// + /// Defines the RP + /// + public static readonly DnsRecordType RP = new DnsRecordType(0x0011, "RP"); - public static readonly DnsRecordType TKEY = new DnsRecordType(0x00f9, "TKEY"); + /// + /// Defines the RRSIG + /// + public static readonly DnsRecordType RRSIG = new DnsRecordType(0x002e, "RRSIG"); - public static readonly DnsRecordType TSIG = new DnsRecordType(0x00fa, "TSIG"); + /// + /// Defines the SIG + /// + public static readonly DnsRecordType SIG = new DnsRecordType(0x0018, "SIG"); - public static readonly DnsRecordType IXFR = new DnsRecordType(0x00fb, "IXFR"); + /// + /// Defines the SOA + /// + public static readonly DnsRecordType SOA = new DnsRecordType(0x0006, "SOA"); - public static readonly DnsRecordType AXFR = new DnsRecordType(0x00fc, "AXFR"); + /// + /// Defines the SPF + /// + public static readonly DnsRecordType SPF = new DnsRecordType(0x0063, "SPF"); - public static readonly DnsRecordType ANY = new DnsRecordType(0x00ff, "ANY"); + /// + /// Defines the SRV + /// + public static readonly DnsRecordType SRV = new DnsRecordType(0x0021, "SRV"); - public static readonly DnsRecordType CAA = new DnsRecordType(0x0101, "CAA"); + /// + /// Defines the SSHFP + /// + public static readonly DnsRecordType SSHFP = new DnsRecordType(0x002c, "SSHFP"); + /// + /// Defines the TA + /// public static readonly DnsRecordType TA = new DnsRecordType(0x8000, "TA"); - public static readonly DnsRecordType DLV = new DnsRecordType(0x8001, "DLV"); - #endregion + /// + /// Defines the TKEY + /// + public static readonly DnsRecordType TKEY = new DnsRecordType(0x00f9, "TKEY"); + + /// + /// Defines the TLSA + /// + public static readonly DnsRecordType TLSA = new DnsRecordType(0x0034, "TLSA"); + + /// + /// Defines the TSIG + /// + public static readonly DnsRecordType TSIG = new DnsRecordType(0x00fa, "TSIG"); + + /// + /// Defines the TXT + /// + public static readonly DnsRecordType TXT = new DnsRecordType(0x0010, "TXT"); + /// + /// Defines the byName + /// private static readonly Dictionary byName = new Dictionary(); + + /// + /// Defines the byType + /// private static readonly Dictionary byType = new Dictionary(); + + /// + /// Defines the EXPECTED + /// private static readonly string EXPECTED; + + /// + /// Defines the text + /// private string text = string.Empty; - public int IntValue { get; } - public string Name { get; } + #endregion 字段 - private DnsRecordType(int intValue) : this(intValue, "UNKNOWN") { } + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The intValue + /// The name public DnsRecordType(int intValue, string name) { if ((intValue & 0xffff) != intValue) @@ -111,6 +250,17 @@ public DnsRecordType(int intValue, string name) Name = name; } + /// + /// Prevents a default instance of the class from being created. + /// + /// The intValue + private DnsRecordType(int intValue) : this(intValue, "UNKNOWN") + { + } + + /// + /// Initializes static members of the class. + /// static DnsRecordType() { DnsRecordType[] all = { @@ -131,14 +281,37 @@ static DnsRecordType() expected.Append(type.Name) .Append('(') .Append(type.IntValue) - .Append("), "); + .Append("), "); } expected.Length = expected.Length - 2; expected.Append(')'); - EXPECTED = expected.ToString(); + EXPECTED = expected.ToString(); } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the IntValue + /// + public int IntValue { get; } + + /// + /// Gets the Name + /// + public string Name { get; } + + #endregion 属性 + + #region 方法 + + /// + /// The From + /// + /// The intValue + /// The public static DnsRecordType From(int intValue) { if (byType.ContainsKey(intValue)) @@ -147,6 +320,11 @@ public static DnsRecordType From(int intValue) return new DnsRecordType(intValue); } + /// + /// The From + /// + /// The name + /// The public static DnsRecordType From(string name) { if (byName.ContainsKey(name)) @@ -155,16 +333,29 @@ public static DnsRecordType From(string name) throw new ArgumentException($"name: {name} {EXPECTED}"); } - public override int GetHashCode() + /// + /// The Equals + /// + /// The obj + /// The + public override bool Equals(object obj) { - return IntValue; + return obj is DnsRecordType && ((DnsRecordType)obj).IntValue == IntValue; } - public override bool Equals(object obj) + /// + /// The GetHashCode + /// + /// The + public override int GetHashCode() { - return obj is DnsRecordType && ((DnsRecordType)obj).IntValue == IntValue; + return IntValue; } + /// + /// The ToString + /// + /// The public override string ToString() { string text = this.text; @@ -173,5 +364,7 @@ public override string ToString() return text; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/IDnsOptEcsRecord.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/IDnsOptEcsRecord.cs index 9dca7a61b..a4b64559d 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/IDnsOptEcsRecord.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/IDnsOptEcsRecord.cs @@ -1,9 +1,31 @@ namespace DotNetty.Codecs.DNS.Records { + #region 接口 + + /// + /// Defines the + /// public interface IDnsOptEcsRecord : IDnsOptPseudoRecord { - int SourcePrefixLength { get; } - int ScopePrefixLength { get; } + #region 属性 + + /// + /// Gets the Address + /// byte[] Address { get; } + + /// + /// Gets the ScopePrefixLength + /// + int ScopePrefixLength { get; } + + /// + /// Gets the SourcePrefixLength + /// + int SourcePrefixLength { get; } + + #endregion 属性 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/IDnsOptPseudoRecord.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/IDnsOptPseudoRecord.cs index 82ad7e57f..5e462ad11 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/IDnsOptPseudoRecord.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/IDnsOptPseudoRecord.cs @@ -1,9 +1,31 @@ namespace DotNetty.Codecs.DNS.Records { + #region 接口 + + /// + /// Defines the + /// public interface IDnsOptPseudoRecord : IDnsRecord { + #region 属性 + + /// + /// Gets the ExtendedRcode + /// int ExtendedRcode { get; } - int Version { get; } + + /// + /// Gets the Flags + /// int Flags { get; } + + /// + /// Gets the Version + /// + int Version { get; } + + #endregion 属性 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/IDnsPtrRecord.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/IDnsPtrRecord.cs index 2d3cc1950..782f8c89a 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/IDnsPtrRecord.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/IDnsPtrRecord.cs @@ -1,8 +1,21 @@ - -namespace DotNetty.Codecs.DNS.Records +namespace DotNetty.Codecs.DNS.Records { + #region 接口 + + /// + /// Defines the + /// public interface IDnsPtrRecord : IDnsRecord { + #region 属性 + + /// + /// Gets the HostName + /// string HostName { get; } + + #endregion 属性 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/IDnsQuestion.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/IDnsQuestion.cs index 8abab5ba9..fed82e0c6 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/IDnsQuestion.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/IDnsQuestion.cs @@ -1,6 +1,13 @@ namespace DotNetty.Codecs.DNS.Records { + #region 接口 + + /// + /// Defines the + /// public interface IDnsQuestion : IDnsRecord { } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/IDnsRawRecord.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/IDnsRawRecord.cs index c85d9984e..99c3ed8e5 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/IDnsRawRecord.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/IDnsRawRecord.cs @@ -2,7 +2,14 @@ namespace DotNetty.Codecs.DNS.Records { + #region 接口 + + /// + /// Defines the + /// public interface IDnsRawRecord : IDnsRecord, IByteBufferHolder { } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/IDnsRecord.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/IDnsRecord.cs index 5cd8f703e..2f9ebd05e 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/IDnsRecord.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Records/IDnsRecord.cs @@ -1,10 +1,36 @@ namespace DotNetty.Codecs.DNS.Records { + #region 接口 + + /// + /// Defines the + /// public interface IDnsRecord { + #region 属性 + + /// + /// Gets the DnsClass + /// DnsRecordClass DnsClass { get; } + + /// + /// Gets the Name + /// string Name { get; } + + /// + /// Gets or sets the TimeToLive + /// long TimeToLive { get; set; } + + /// + /// Gets the Type + /// DnsRecordType Type { get; } + + #endregion 属性 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/TcpDnsQueryEncoder.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/TcpDnsQueryEncoder.cs index 504793926..ecdc0dd26 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/TcpDnsQueryEncoder.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/TcpDnsQueryEncoder.cs @@ -8,28 +8,65 @@ namespace DotNetty.Codecs.DNS { - public sealed class TcpDnsQueryEncoder:MessageToByteEncoder + /// + /// Defines the + /// + public sealed class TcpDnsQueryEncoder : MessageToByteEncoder { + #region 字段 + + /// + /// Defines the encoder + /// private readonly DnsQueryEncoder encoder; - - public TcpDnsQueryEncoder():this(new DefaultDnsRecordEncoder()) - { + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + public TcpDnsQueryEncoder() : this(new DefaultDnsRecordEncoder()) + { } - + + /// + /// Initializes a new instance of the class. + /// + /// The recordEncoder public TcpDnsQueryEncoder(IDnsRecordEncoder recordEncoder) { this.encoder = new DnsQueryEncoder(recordEncoder); } + #endregion 构造函数 + + #region 方法 + + /// + /// The AllocateBuffer + /// + /// The ctx + /// The + protected override IByteBuffer AllocateBuffer(IChannelHandlerContext ctx) + { + return ctx.Allocator.Buffer(1024); + } + + /// + /// The Encode + /// + /// The context + /// The message + /// The output protected override void Encode(IChannelHandlerContext context, IDnsQuery message, IByteBuffer output) { output.SetWriterIndex(output.WriterIndex + 2); - encoder.Encode(message, output); - output.SetShort(0, output.ReadableBytes- 2); + encoder.Encode(message, output); + output.SetShort(0, output.ReadableBytes - 2); } - protected override IByteBuffer AllocateBuffer(IChannelHandlerContext ctx) { - return ctx.Allocator.Buffer(1024); - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/TcpDnsResponseDecoder.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/TcpDnsResponseDecoder.cs index a8d24f37d..67346f568 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/TcpDnsResponseDecoder.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/TcpDnsResponseDecoder.cs @@ -8,40 +8,80 @@ namespace DotNetty.Codecs.DNS { - public class TcpDnsResponseDecoder : LengthFieldBasedFrameDecoder + /// + /// Defines the + /// + public class TcpDnsResponseDecoder : LengthFieldBasedFrameDecoder { + #region 字段 + + /// + /// Defines the responseDecoder + /// private readonly DnsResponseDecoder responseDecoder; - public TcpDnsResponseDecoder():this(new DefaultDnsRecordDecoder(), 64 * 1024) - { + #endregion 字段 + + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + public TcpDnsResponseDecoder() : this(new DefaultDnsRecordDecoder(), 64 * 1024) + { } + /// + /// Initializes a new instance of the class. + /// + /// The recordDecoder + /// The maxFrameLength public TcpDnsResponseDecoder(IDnsRecordDecoder recordDecoder, int maxFrameLength) : base(maxFrameLength, 0, 2, 0, 2) { - this.responseDecoder = new DnsResponseDecoder(recordDecoder); } + #endregion 构造函数 + + #region 方法 + + /// + /// The Decode + /// + /// The ctx + /// The buffer + /// The protected override Object Decode(IChannelHandlerContext ctx, IByteBuffer buffer) { var frame = (IByteBuffer)base.Decode(ctx, buffer); - if (frame == null) { + if (frame == null) + { return null; } - try { + try + { return responseDecoder.Decode(ctx.Channel.RemoteAddress, ctx.Channel.LocalAddress, frame.Slice()); - } finally { + } + finally + { frame.Release(); } } - + /// + /// The ExtractFrame + /// + /// The ctx + /// The buffer + /// The index + /// The length + /// The protected override IByteBuffer ExtractFrame(IChannelHandlerContext ctx, IByteBuffer buffer, int index, int length) { return buffer.Copy(index, length); } - } -} + #endregion 方法 + } +} \ No newline at end of file diff --git a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Utils.cs b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Utils.cs index 12769999c..fb97c9830 100644 --- a/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Utils.cs +++ b/src/DotNetty.Codecs/DotNetty.Codecs.DNS/Utils.cs @@ -7,8 +7,19 @@ namespace DotNetty.Codecs.DNS { + /// + /// Defines the + /// internal static class Utils { + #region 方法 + + /// + /// The AppendRecordClass + /// + /// The builder + /// The dnsClass + /// The internal static StringBuilder AppendRecordClass(this StringBuilder builder, DnsRecordClass dnsClass) { string name; @@ -17,21 +28,27 @@ internal static StringBuilder AppendRecordClass(this StringBuilder builder, DnsR case DnsRecordClass.IN: name = "IN"; break; + case DnsRecordClass.CSNET: name = "CSNET"; break; + case DnsRecordClass.CHAOS: name = "CHAOS"; break; + case DnsRecordClass.HESIOD: name = "HESIOD"; break; + case DnsRecordClass.NONE: name = "NONE"; break; + case DnsRecordClass.ANY: name = "ANY"; break; + default: name = $"UNKNOWN({dnsClass})"; break; @@ -41,6 +58,12 @@ internal static StringBuilder AppendRecordClass(this StringBuilder builder, DnsR return builder; } + /// + /// The AppendResponse + /// + /// The builder + /// The response + /// The internal static StringBuilder AppendResponse(this StringBuilder builder, IDnsResponse response) { builder.AppendResponseHeader(response) @@ -48,33 +71,12 @@ internal static StringBuilder AppendResponse(this StringBuilder builder, IDnsRes return builder; } - private static StringBuilder AppendAddresses(this StringBuilder builder, IDnsMessage response) - { - - if (!(response is IAddressedEnvelope)) - return builder; - - IAddressedEnvelope envelope = (IAddressedEnvelope) response; - - var addr = envelope.Sender; - if (addr != null) - { - builder.Append("from: ") - .Append(addr) - .Append(", "); - } - - addr = envelope.Recipient; - if (addr != null) - { - builder.Append("to: ") - .Append(addr) - .Append(", "); - } - - return builder; - } - + /// + /// The AppendResponseHeader + /// + /// The builder + /// The response + /// The internal static StringBuilder AppendResponseHeader(this StringBuilder builder, IDnsResponse response) { builder.Append(response.GetType().GetTypeInfo().Name) @@ -130,14 +132,59 @@ internal static StringBuilder AppendResponseHeader(this StringBuilder builder, I return builder; } + /// + /// The AppendAddresses + /// + /// The builder + /// The response + /// The + private static StringBuilder AppendAddresses(this StringBuilder builder, IDnsMessage response) + { + if (!(response is IAddressedEnvelope)) + return builder; + + IAddressedEnvelope envelope = (IAddressedEnvelope)response; + + var addr = envelope.Sender; + if (addr != null) + { + builder.Append("from: ") + .Append(addr) + .Append(", "); + } + + addr = envelope.Recipient; + if (addr != null) + { + builder.Append("to: ") + .Append(addr) + .Append(", "); + } + + return builder; + } + + /// + /// The AppendAllRecords + /// + /// The builder + /// The msg + /// The private static StringBuilder AppendAllRecords(this StringBuilder builder, IDnsMessage msg) { - return builder.AppendRecords(msg, DnsSection.QUESTION) - .AppendRecords(msg, DnsSection.ANSWER) - .AppendRecords(msg, DnsSection.AUTHORITY) - .AppendRecords(msg, DnsSection.ADDITIONAL); + return builder.AppendRecords(msg, DnsSection.QUESTION) + .AppendRecords(msg, DnsSection.ANSWER) + .AppendRecords(msg, DnsSection.AUTHORITY) + .AppendRecords(msg, DnsSection.ADDITIONAL); } + /// + /// The AppendRecords + /// + /// The builder + /// The message + /// The section + /// The private static StringBuilder AppendRecords(this StringBuilder builder, IDnsMessage message, DnsSection section) { int count = message.Count(section); @@ -150,5 +197,7 @@ private static StringBuilder AppendRecords(this StringBuilder builder, IDnsMessa return builder; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.ApiGateway/Controllers/AuthenticationManageController.cs b/src/Surging.ApiGateway/Controllers/AuthenticationManageController.cs index 92b13235a..4cd74b247 100644 --- a/src/Surging.ApiGateway/Controllers/AuthenticationManageController.cs +++ b/src/Surging.ApiGateway/Controllers/AuthenticationManageController.cs @@ -8,24 +8,47 @@ namespace Surging.ApiGateway.Controllers { + /// + /// Defines the + /// public class AuthenticationManageController : Controller { - public IActionResult Index() + #region 方法 + + /// + /// The EditServiceToken + /// + /// The serviceDiscoveryProvider + /// The model + /// The + [HttpPost] + public async Task EditServiceToken([FromServices]IServiceDiscoveryProvider serviceDiscoveryProvider, IpAddressModel model) { - return View(); + await serviceDiscoveryProvider.EditServiceToken(model); + return Json(ServiceResult.Create(true)); } + /// + /// The EditServiceToken + /// + /// The serviceDiscoveryProvider + /// The address + /// The public async Task EditServiceToken([FromServices]IServiceDiscoveryProvider serviceDiscoveryProvider, string address) { var list = await serviceDiscoveryProvider.GetAddressAsync(address); ; return View(list.FirstOrDefault()); } - [HttpPost] - public async Task EditServiceToken([FromServices]IServiceDiscoveryProvider serviceDiscoveryProvider, IpAddressModel model) + /// + /// The Index + /// + /// The + public IActionResult Index() { - await serviceDiscoveryProvider.EditServiceToken(model); - return Json(ServiceResult.Create(true)); + return View(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.ApiGateway/Controllers/HomeController.cs b/src/Surging.ApiGateway/Controllers/HomeController.cs index e81047ba8..f825d8cd8 100644 --- a/src/Surging.ApiGateway/Controllers/HomeController.cs +++ b/src/Surging.ApiGateway/Controllers/HomeController.cs @@ -4,16 +4,31 @@ namespace Surging.ApiGateway.Controllers { + /// + /// Defines the + /// public class HomeController : Controller { - public IActionResult Index() - { - return View(); - } - + #region 方法 + + /// + /// The Error + /// + /// The public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } + + /// + /// The Index + /// + /// The + public IActionResult Index() + { + return View(); + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.ApiGateway/Controllers/ServiceManageController.cs b/src/Surging.ApiGateway/Controllers/ServiceManageController.cs index 18ea86811..3d8a2779d 100644 --- a/src/Surging.ApiGateway/Controllers/ServiceManageController.cs +++ b/src/Surging.ApiGateway/Controllers/ServiceManageController.cs @@ -13,53 +13,86 @@ using System.Threading.Tasks; // For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 - namespace Surging.ApiGateway.Controllers { + /// + /// Defines the + /// public class ServiceManageController : Controller { - // GET: // - public IActionResult Index() - { - return View(); - } + #region 方法 - public IActionResult ServiceManage() + /// + /// The DelCacheEndPoint + /// + /// The serviceCacheProvider + /// The cacheId + /// The endpoint + /// The + [HttpPost] + public async Task DelCacheEndPoint([FromServices]IServiceCacheProvider serviceCacheProvider, string cacheId, string endpoint) { - return View(); + await serviceCacheProvider.DelCacheEndpointAsync(cacheId, endpoint); + return Json(ServiceResult.Create(true)); } - + /// + /// The EditCacheEndPoint + /// + /// The serviceCacheProvider + /// The param + /// The [HttpPost] - public async Task GetRegisterAddress([FromServices]IServiceRegisterProvider serviceRegisterProvide, string queryParam) + public async Task EditCacheEndPoint([FromServices]IServiceCacheProvider serviceCacheProvider, CacheEndpointParam param) { - var list = await serviceRegisterProvide.GetAddressAsync(queryParam); - var result = ServiceResult>.Create(true, list); - return Json(result); + await serviceCacheProvider.SetCacheEndpointByEndpoint(param.CacheId, param.Endpoint, param.CacheEndpoint); + return Json(ServiceResult.Create(true)); } - [HttpPost] - public async Task GetAddress([FromServices]IServiceDiscoveryProvider serviceDiscoveryProvider, string queryParam) + /// + /// The EditCacheEndPoint + /// + /// The serviceCacheProvider + /// The cacheId + /// The endpoint + /// The + public async Task EditCacheEndPoint([FromServices]IServiceCacheProvider serviceCacheProvider, string cacheId, string endpoint) { - var list = await serviceDiscoveryProvider.GetAddressAsync(queryParam); - var result = ServiceResult>.Create(true, list); - return Json(result); + var model = await serviceCacheProvider.GetCacheEndpointAsync(cacheId, endpoint); + return View(model); } + /// + /// The EditFaultTolerant + /// + /// The faultTolerantProvider + /// The model + /// The [HttpPost] - public async Task GetServiceDescriptor([FromServices]IServiceDiscoveryProvider serviceDiscoveryProvider, string address, string queryParam) + public async Task EditFaultTolerant([FromServices]IFaultTolerantProvider faultTolerantProvider, ServiceCommandDescriptor model) { - var list = await serviceDiscoveryProvider.GetServiceDescriptorAsync(address, queryParam); - var result = ServiceResult>.Create(true, list); - return Json(result); + await faultTolerantProvider.SetCommandDescriptorByAddress(model); + return Json(ServiceResult.Create(true)); } - public IActionResult ServiceDescriptor(string address) + /// + /// The EditFaultTolerant + /// + /// The faultTolerantProvider + /// The serviceId + /// The + public async Task EditFaultTolerant([FromServices]IFaultTolerantProvider faultTolerantProvider, string serviceId) { - ViewBag.address = address; - return View(); + var list = await faultTolerantProvider.GetCommandDescriptor(serviceId); + return View(list.FirstOrDefault()); } + /// + /// The FaultTolerant + /// + /// The serviceId + /// The address + /// The public IActionResult FaultTolerant(string serviceId, string address) { ViewBag.ServiceId = serviceId; @@ -67,41 +100,43 @@ public IActionResult FaultTolerant(string serviceId, string address) return View(); } - public async Task EditCacheEndPoint([FromServices]IServiceCacheProvider serviceCacheProvider, string cacheId,string endpoint) - { - var model = await serviceCacheProvider.GetCacheEndpointAsync(cacheId, endpoint); - return View(model); - } - + /// + /// The GetAddress + /// + /// The serviceDiscoveryProvider + /// The queryParam + /// The [HttpPost] - public async Task DelCacheEndPoint([FromServices]IServiceCacheProvider serviceCacheProvider, string cacheId, string endpoint) - { - await serviceCacheProvider.DelCacheEndpointAsync(cacheId, endpoint); - return Json(ServiceResult.Create(true)); - } - - [HttpPost] - public async Task EditCacheEndPoint([FromServices]IServiceCacheProvider serviceCacheProvider, CacheEndpointParam param) + public async Task GetAddress([FromServices]IServiceDiscoveryProvider serviceDiscoveryProvider, string queryParam) { - await serviceCacheProvider.SetCacheEndpointByEndpoint(param.CacheId, param.Endpoint, param.CacheEndpoint); - return Json(ServiceResult.Create(true)); + var list = await serviceDiscoveryProvider.GetAddressAsync(queryParam); + var result = ServiceResult>.Create(true, list); + return Json(result); } - public async Task EditFaultTolerant([FromServices]IFaultTolerantProvider faultTolerantProvider,string serviceId) - { - var list = await faultTolerantProvider.GetCommandDescriptor(serviceId); - return View(list.FirstOrDefault()); - } - - [HttpPost] - public async Task EditFaultTolerant([FromServices]IFaultTolerantProvider faultTolerantProvider, ServiceCommandDescriptor model) + /// + /// The GetCacheEndpoint + /// + /// The serviceCacheProvider + /// The cacheId + /// The + public async Task GetCacheEndpoint([FromServices]IServiceCacheProvider serviceCacheProvider, + string cacheId) { - await faultTolerantProvider.SetCommandDescriptorByAddress(model); - return Json(ServiceResult.Create(true)); + var list = await serviceCacheProvider.GetCacheEndpointAsync(cacheId); + var result = ServiceResult>.Create(true, list); + return Json(result); } + /// + /// The GetCommandDescriptor + /// + /// The faultTolerantProvider + /// The serviceId + /// The address + /// The [HttpPost] - public async Task GetCommandDescriptor([FromServices]IFaultTolerantProvider faultTolerantProvider, + public async Task GetCommandDescriptor([FromServices]IFaultTolerantProvider faultTolerantProvider, string serviceId, string address) { IEnumerable list = null; @@ -117,11 +152,26 @@ public async Task GetCommandDescriptor([FromServices]IFaultTolera return Json(result); } - public IActionResult ServiceCache() + /// + /// The GetRegisterAddress + /// + /// The serviceRegisterProvide + /// The queryParam + /// The + [HttpPost] + public async Task GetRegisterAddress([FromServices]IServiceRegisterProvider serviceRegisterProvide, string queryParam) { - return View(); + var list = await serviceRegisterProvide.GetAddressAsync(queryParam); + var result = ServiceResult>.Create(true, list); + return Json(result); } + /// + /// The GetServiceCache + /// + /// The serviceCacheProvider + /// The queryParam + /// The [HttpPost] public async Task GetServiceCache([FromServices]IServiceCacheProvider serviceCacheProvider, string queryParam) { @@ -130,34 +180,97 @@ public async Task GetServiceCache([FromServices]IServiceCacheProv return Json(result); } - public IActionResult ServiceCacheEndpoint(string cacheId) + /// + /// The GetServiceDescriptor + /// + /// The serviceDiscoveryProvider + /// The address + /// The queryParam + /// The + [HttpPost] + public async Task GetServiceDescriptor([FromServices]IServiceDiscoveryProvider serviceDiscoveryProvider, string address, string queryParam) + { + var list = await serviceDiscoveryProvider.GetServiceDescriptorAsync(address, queryParam); + var result = ServiceResult>.Create(true, list); + return Json(result); + } + + /// + /// The GetSubscriber + /// + /// The serviceSubscribeProvider + /// The queryParam + /// The + [HttpPost] + public async Task GetSubscriber([FromServices]IServiceSubscribeProvider serviceSubscribeProvider, + string queryParam) + { + var list = await serviceSubscribeProvider.GetAddressAsync(queryParam); + var result = ServiceResult>.Create(true, list); + return Json(result); + } + + // GET: // + /// + /// The Index + /// + /// The + public IActionResult Index() + { + return View(); + } + + /// + /// The ServiceCache + /// + /// The + public IActionResult ServiceCache() + { + return View(); + } + + /// + /// The ServiceCacheEndpoint + /// + /// The cacheId + /// The + public IActionResult ServiceCacheEndpoint(string cacheId) { ViewBag.CacheId = cacheId; return View(); } - public async Task GetCacheEndpoint([FromServices]IServiceCacheProvider serviceCacheProvider, - string cacheId) + /// + /// The ServiceDescriptor + /// + /// The address + /// The + public IActionResult ServiceDescriptor(string address) { - var list = await serviceCacheProvider.GetCacheEndpointAsync(cacheId); - var result = ServiceResult>.Create(true, list); - return Json(result); + ViewBag.address = address; + return View(); } + /// + /// The ServiceManage + /// + /// The + public IActionResult ServiceManage() + { + return View(); + } + /// + /// The ServiceSubscriber + /// + /// The serviceId + /// The public IActionResult ServiceSubscriber(string serviceId) { ViewBag.ServiceId = serviceId; return View(); } - [HttpPost] - public async Task GetSubscriber([FromServices]IServiceSubscribeProvider serviceSubscribeProvider, - string queryParam) - { - var list = await serviceSubscribeProvider.GetAddressAsync(queryParam); - var result = ServiceResult>.Create(true, list); - return Json(result); - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.ApiGateway/Controllers/ServicesController.cs b/src/Surging.ApiGateway/Controllers/ServicesController.cs index 18943eb86..63cb13c54 100644 --- a/src/Surging.ApiGateway/Controllers/ServicesController.cs +++ b/src/Surging.ApiGateway/Controllers/ServicesController.cs @@ -1,34 +1,59 @@ using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json.Linq; using Surging.Core.ApiGateWay; using Surging.Core.ApiGateWay.OAuth; using Surging.Core.CPlatform; using Surging.Core.CPlatform.Filters.Implementation; using Surging.Core.CPlatform.Routing; +using Surging.Core.CPlatform.Routing.Template; +using Surging.Core.CPlatform.Transport.Implementation; +using Surging.Core.CPlatform.Utilities; using Surging.Core.ProxyGenerator; using Surging.Core.ProxyGenerator.Utilitys; using System; using System.Collections.Generic; +using System.Linq; +using System.Reflection; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; -using System.Linq; using GateWayAppConfig = Surging.Core.ApiGateWay.AppConfig; -using System.Reflection; -using Surging.Core.CPlatform.Utilities; -using Newtonsoft.Json.Linq; -using Surging.Core.CPlatform.Transport.Implementation; -using Surging.Core.CPlatform.Routing.Template; namespace Surging.ApiGateway.Controllers { + /// + /// Defines the + /// public class ServicesController : Controller { + #region 字段 + + /// + /// Defines the _authorizationServerProvider + /// + private readonly IAuthorizationServerProvider _authorizationServerProvider; + + /// + /// Defines the _serviceProxyProvider + /// private readonly IServiceProxyProvider _serviceProxyProvider; + + /// + /// Defines the _serviceRouteProvider + /// private readonly IServiceRouteProvider _serviceRouteProvider; - private readonly IAuthorizationServerProvider _authorizationServerProvider; - - public ServicesController(IServiceProxyProvider serviceProxyProvider, + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serviceProxyProvider + /// The serviceRouteProvider + /// The authorizationServerProvider + public ServicesController(IServiceProxyProvider serviceProxyProvider, IServiceRouteProvider serviceRouteProvider, IAuthorizationServerProvider authorizationServerProvider) { @@ -37,6 +62,43 @@ public ServicesController(IServiceProxyProvider serviceProxyProvider, _authorizationServerProvider = authorizationServerProvider; } + #endregion 构造函数 + + #region 方法 + + /// + /// The GetMD5 + /// + /// The encypStr + /// The + public static string GetMD5(string encypStr) + { + try + { + var md5 = MD5.Create(); + var bs = md5.ComputeHash(Encoding.UTF8.GetBytes(encypStr)); + var sb = new StringBuilder(); + foreach (byte b in bs) + { + sb.Append(b.ToString("X2")); + } + //所有字符转为大写 + return sb.ToString().ToLower(); + } + catch (Exception e) + { + Console.Error.WriteLine(e.StackTrace); + return null; + } + } + + /// + /// The Path + /// + /// The servicePartProvider + /// The path + /// The model + /// The public async Task> Path([FromServices]IServicePartProvider servicePartProvider, string path, [FromBody]Dictionary model) { string serviceKey = this.Request.Query["servicekey"]; @@ -80,18 +142,16 @@ public async Task> Path([FromServices]IServicePartProvider } else { - if (String.Compare(route.ServiceDescriptor.RoutePath, path, true) != 0) { var pamars = RouteTemplateSegmenter.Segment(route.ServiceDescriptor.RoutePath, path); foreach (KeyValuePair item in pamars) { - model.Add(item.Key,item.Value); + model.Add(item.Key, item.Value); } } if (!string.IsNullOrEmpty(serviceKey)) { - result = ServiceResult.Create(true, await _serviceProxyProvider.Invoke(model, route.ServiceDescriptor.RoutePath, serviceKey)); result.StatusCode = (int)ServiceStatusCode.Success; } @@ -106,33 +166,16 @@ public async Task> Path([FromServices]IServicePartProvider return result; } - private bool GetAllowRequest(ServiceRoute route) - { - return !route.ServiceDescriptor.DisableNetwork(); - } - - private (bool, ServiceResult) OnAuthorization(ServiceRoute route, Dictionary model) - { - bool isSuccess = true; - var serviceResult = ServiceResult.Create(false, null); - if (route.ServiceDescriptor.EnableAuthorization()) - { - if(route.ServiceDescriptor.AuthType()== AuthorizationType.JWT.ToString()) - { - isSuccess= ValidateJwtAuthentication(route,model, ref serviceResult); - } - else - { - isSuccess = ValidateAppSecretAuthentication(route, model, ref serviceResult); - } - - } - return new ValueTuple>(isSuccess,serviceResult); - } - + /// + /// The ValidateJwtAuthentication + /// + /// The route + /// The model + /// The result + /// The public bool ValidateJwtAuthentication(ServiceRoute route, Dictionary model, ref ServiceResult result) { - bool isSuccess = true; + bool isSuccess = true; var author = HttpContext.Request.Headers["Authorization"]; if (author.Count > 0) { @@ -145,7 +188,7 @@ public bool ValidateJwtAuthentication(ServiceRoute route, Dictionary0) + if (model.Count > 0) { var keyValue = model.FirstOrDefault(); if (!(keyValue.Value is IConvertible) || !typeof(IConvertible).GetTypeInfo().IsAssignableFrom(keyValue.Value.GetType())) @@ -166,27 +209,62 @@ public bool ValidateJwtAuthentication(ServiceRoute route, Dictionary + /// The GetAllowRequest + /// + /// The route + /// The + private bool GetAllowRequest(ServiceRoute route) + { + return !route.ServiceDescriptor.DisableNetwork(); + } + + /// + /// The OnAuthorization + /// + /// The route + /// The model + /// The + private (bool, ServiceResult) OnAuthorization(ServiceRoute route, Dictionary model) + { + bool isSuccess = true; + var serviceResult = ServiceResult.Create(false, null); + if (route.ServiceDescriptor.EnableAuthorization()) + { + if (route.ServiceDescriptor.AuthType() == AuthorizationType.JWT.ToString()) + { + isSuccess = ValidateJwtAuthentication(route, model, ref serviceResult); + } + else + { + isSuccess = ValidateAppSecretAuthentication(route, model, ref serviceResult); + } + } + return new ValueTuple>(isSuccess, serviceResult); + } + + /// + /// The ValidateAppSecretAuthentication + /// + /// The route + /// The model + /// The result + /// The private bool ValidateAppSecretAuthentication(ServiceRoute route, Dictionary model, ref ServiceResult result) { bool isSuccess = true; DateTime time; var author = HttpContext.Request.Headers["Authorization"]; - - if ( model.ContainsKey("timeStamp") && author.Count>0) + + if (model.ContainsKey("timeStamp") && author.Count > 0) + { + if (DateTime.TryParse(model["timeStamp"].ToString(), out time)) { - if (DateTime.TryParse(model["timeStamp"].ToString(), out time)) + var seconds = (DateTime.Now - time).TotalSeconds; + if (seconds <= 3560 && seconds >= 0) { - var seconds = (DateTime.Now - time).TotalSeconds; - if (seconds <= 3560 && seconds >= 0) - { - if (GetMD5($"{route.ServiceDescriptor.Token}{time.ToString("yyyy-MM-dd hh:mm:ss") }") != author.ToString()) - { - result = new ServiceResult { IsSucceed = false, StatusCode = (int)ServiceStatusCode.AuthorizationFailed, Message = "Invalid authentication credentials" }; - isSuccess = false; - } - } - else + if (GetMD5($"{route.ServiceDescriptor.Token}{time.ToString("yyyy-MM-dd hh:mm:ss") }") != author.ToString()) { result = new ServiceResult { IsSucceed = false, StatusCode = (int)ServiceStatusCode.AuthorizationFailed, Message = "Invalid authentication credentials" }; isSuccess = false; @@ -200,31 +278,18 @@ private bool ValidateAppSecretAuthentication(ServiceRoute route, } else { - result = new ServiceResult { IsSucceed = false, StatusCode = (int)ServiceStatusCode.RequestError, Message = "Request error" }; + result = new ServiceResult { IsSucceed = false, StatusCode = (int)ServiceStatusCode.AuthorizationFailed, Message = "Invalid authentication credentials" }; isSuccess = false; - } - return isSuccess; - } - - public static string GetMD5(string encypStr) - { - try - { - var md5 = MD5.Create(); - var bs = md5.ComputeHash(Encoding.UTF8.GetBytes(encypStr)); - var sb = new StringBuilder(); - foreach (byte b in bs) - { - sb.Append(b.ToString("X2")); } - //所有字符转为大写 - return sb.ToString().ToLower(); } - catch (Exception e) + else { - Console.Error.WriteLine(e.StackTrace); - return null; + result = new ServiceResult { IsSucceed = false, StatusCode = (int)ServiceStatusCode.RequestError, Message = "Request error" }; + isSuccess = false; } + return isSuccess; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.ApiGateway/CustomExceptionFilterAttribute.cs b/src/Surging.ApiGateway/CustomExceptionFilterAttribute.cs index afbfb8862..6a50bdec3 100644 --- a/src/Surging.ApiGateway/CustomExceptionFilterAttribute.cs +++ b/src/Surging.ApiGateway/CustomExceptionFilterAttribute.cs @@ -10,11 +10,32 @@ namespace Surging.ApiGateway { + /// + /// Defines the + /// public class CustomExceptionFilterAttribute : ExceptionFilterAttribute { + #region 字段 + + /// + /// Defines the _hostingEnvironment + /// private readonly IHostingEnvironment _hostingEnvironment; + + /// + /// Defines the _modelMetadataProvider + /// private readonly IModelMetadataProvider _modelMetadataProvider; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The hostingEnvironment + /// The modelMetadataProvider public CustomExceptionFilterAttribute( IHostingEnvironment hostingEnvironment, IModelMetadataProvider modelMetadataProvider) @@ -23,16 +44,25 @@ public CustomExceptionFilterAttribute( _modelMetadataProvider = modelMetadataProvider; } + #endregion 构造函数 + + #region 方法 + + /// + /// The OnException + /// + /// The context public override void OnException(ExceptionContext context) { if (!_hostingEnvironment.IsDevelopment()) { return; } - var result = ServiceResult.Create(false,errorMessage: context.Exception.Message); + var result = ServiceResult.Create(false, errorMessage: context.Exception.Message); result.StatusCode = 400; - context.Result =new JsonResult(result); + context.Result = new JsonResult(result); } - } -} + #endregion 方法 + } +} \ No newline at end of file diff --git a/src/Surging.ApiGateway/Models/CacheEndpointParam.cs b/src/Surging.ApiGateway/Models/CacheEndpointParam.cs index 9936352cb..d2ac97a76 100644 --- a/src/Surging.ApiGateway/Models/CacheEndpointParam.cs +++ b/src/Surging.ApiGateway/Models/CacheEndpointParam.cs @@ -7,12 +7,28 @@ namespace Surging.ApiGateway.Models { + /// + /// Defines the + /// public class CacheEndpointParam { + #region 属性 + + /// + /// Gets or sets the CacheEndpoint + /// + public ConsistentHashNode CacheEndpoint { get; set; } + + /// + /// Gets or sets the CacheId + /// public string CacheId { get; set; } + /// + /// Gets or sets the Endpoint + /// public string Endpoint { get; set; } - public ConsistentHashNode CacheEndpoint { get; set; } + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.ApiGateway/Models/ErrorViewModel.cs b/src/Surging.ApiGateway/Models/ErrorViewModel.cs index efcef4c59..86269af37 100644 --- a/src/Surging.ApiGateway/Models/ErrorViewModel.cs +++ b/src/Surging.ApiGateway/Models/ErrorViewModel.cs @@ -2,10 +2,23 @@ namespace Surging.ApiGateway.Models { + /// + /// Defines the + /// public class ErrorViewModel { + #region + + /// + /// Gets or sets the RequestId + /// public string RequestId { get; set; } + /// + /// Gets a value indicating whether ShowRequestId + /// public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + + #endregion } } \ No newline at end of file diff --git a/src/Surging.ApiGateway/Program.cs b/src/Surging.ApiGateway/Program.cs index b0f8eb85c..83f90ae05 100644 --- a/src/Surging.ApiGateway/Program.cs +++ b/src/Surging.ApiGateway/Program.cs @@ -16,11 +16,19 @@ using System; using System.IO; - namespace Surging.ApiGateway { + /// + /// Defines the + /// public class Program { + #region 方法 + + /// + /// The Main + /// + /// The args public static void Main(string[] args) { var host = new WebHostBuilder() @@ -32,7 +40,8 @@ public static void Main(string[] args) .UseApplicationInsights() .Build(); host.Run(); - } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.ApiGateway/ServiceExceptionFilter.cs b/src/Surging.ApiGateway/ServiceExceptionFilter.cs index 854e1a555..33a504e1d 100644 --- a/src/Surging.ApiGateway/ServiceExceptionFilter.cs +++ b/src/Surging.ApiGateway/ServiceExceptionFilter.cs @@ -7,12 +7,23 @@ namespace Surging.ApiGateway { - public class ServiceExceptionFilter: ExceptionFilterAttribute + /// + /// Defines the + /// + public class ServiceExceptionFilter : ExceptionFilterAttribute { + #region 方法 + + /// + /// The OnException + /// + /// The context public override void OnException(RpcActionExecutedContext context) { if (context.Exception is CPlatformCommunicationException) - throw new Exception(context.Exception.Message,context.Exception); + throw new Exception(context.Exception.Message, context.Exception); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.ApiGateway/Startup.cs b/src/Surging.ApiGateway/Startup.cs index 56db402ae..a9921967c 100644 --- a/src/Surging.ApiGateway/Startup.cs +++ b/src/Surging.ApiGateway/Startup.cs @@ -10,32 +10,36 @@ using Surging.Core.ApiGateWay; using Surging.Core.ApiGateWay.Configurations; using Surging.Core.ApiGateWay.OAuth.Implementation.Configurations; +using Surging.Core.Caching; using Surging.Core.Caching.Configurations; using Surging.Core.Codec.MessagePack; using Surging.Core.Consul; using Surging.Core.Consul.Configurations; using Surging.Core.CPlatform; +using Surging.Core.CPlatform.Cache; using Surging.Core.CPlatform.Utilities; using Surging.Core.DotNetty; using Surging.Core.ProxyGenerator; using Surging.Core.System.Intercept; using Surging.Core.Zookeeper; -//using Surging.Core.Zookeeper; -using ZookeeperConfigInfo = Surging.Core.Zookeeper.Configurations.ConfigInfo; using System; -using ApiGateWayConfig = Surging.Core.ApiGateWay.AppConfig; -using Surging.Core.Caching; -using Surging.Core.CPlatform.Cache; using System.Linq; +using ApiGateWayConfig = Surging.Core.ApiGateWay.AppConfig; +using ZookeeperConfigInfo = Surging.Core.Zookeeper.Configurations.ConfigInfo; namespace Surging.ApiGateway { + /// + /// Defines the + /// public class Startup { - public IConfigurationRoot Configuration { get; } - - public IContainer ApplicationContainer { get; private set; } + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The env public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() @@ -47,45 +51,30 @@ public Startup(IHostingEnvironment env) Configuration = builder.Build(); } - public IServiceProvider ConfigureServices(IServiceCollection services) - { - return RegisterAutofac(services); - } + #endregion 构造函数 - private IServiceProvider RegisterAutofac(IServiceCollection services) - { - var registerConfig = ApiGateWayConfig.Register; - services.AddMvc(options => { - options.Filters.Add(typeof(CustomExceptionFilterAttribute)); - }).AddJsonOptions(options => { - options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; - options.SerializerSettings.ContractResolver = new DefaultContractResolver(); - }); - services.AddLogging(); - services.AddCors(); - var builder = new ContainerBuilder(); - builder.Populate(services); - builder.AddMicroService(option => - { - option.AddClient(); - option.AddCache(); - //option.UseZooKeeperManager(new ConfigInfo("127.0.0.1:2181")); - if(registerConfig.Provider== RegisterProvider.Consul) - option.UseConsulManager(new ConfigInfo(registerConfig.Address,enableChildrenMonitor:false)); - else if(registerConfig.Provider == RegisterProvider.Zookeeper) - option.UseZooKeeperManager(new ZookeeperConfigInfo(registerConfig.Address, enableChildrenMonitor: true)); - option.UseDotNettyTransport(); - option.AddApiGateWay(); - option.AddFilter(new ServiceExceptionFilter()); - //option.UseProtoBufferCodec(); - option.UseMessagePackCodec(); - builder.Register(m => new CPlatformContainer(ServiceLocator.Current)); - }); - ServiceLocator.Current = builder.Build(); - return new AutofacServiceProvider(ServiceLocator.Current); + #region 属性 - } - + /// + /// Gets the ApplicationContainer + /// + public IContainer ApplicationContainer { get; private set; } + + /// + /// Gets the Configuration + /// + public IConfigurationRoot Configuration { get; } + + #endregion 属性 + + #region 方法 + + /// + /// The Configure + /// + /// The app + /// The env + /// The loggerFactory public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(); @@ -131,5 +120,57 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF new { controller = "Services", action = "Path" }); }); } + + /// + /// The ConfigureServices + /// + /// The services + /// The + public IServiceProvider ConfigureServices(IServiceCollection services) + { + return RegisterAutofac(services); + } + + /// + /// The RegisterAutofac + /// + /// The services + /// The + private IServiceProvider RegisterAutofac(IServiceCollection services) + { + var registerConfig = ApiGateWayConfig.Register; + services.AddMvc(options => + { + options.Filters.Add(typeof(CustomExceptionFilterAttribute)); + }).AddJsonOptions(options => + { + options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; + options.SerializerSettings.ContractResolver = new DefaultContractResolver(); + }); + services.AddLogging(); + services.AddCors(); + var builder = new ContainerBuilder(); + builder.Populate(services); + builder.AddMicroService(option => + { + option.AddClient(); + option.AddCache(); + //option.UseZooKeeperManager(new ConfigInfo("127.0.0.1:2181")); + if (registerConfig.Provider == RegisterProvider.Consul) + option.UseConsulManager(new ConfigInfo(registerConfig.Address, enableChildrenMonitor: false)); + else if (registerConfig.Provider == RegisterProvider.Zookeeper) + option.UseZooKeeperManager(new ZookeeperConfigInfo(registerConfig.Address, enableChildrenMonitor: true)); + option.UseDotNettyTransport(); + option.AddApiGateWay(); + option.AddFilter(new ServiceExceptionFilter()); + //option.UseProtoBufferCodec(); + option.UseMessagePackCodec(); + builder.Register(m => new CPlatformContainer(ServiceLocator.Current)); + }); + ServiceLocator.Current = builder.Build(); + return new AutofacServiceProvider(ServiceLocator.Current); + } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Abp/AbpModule.cs b/src/Surging.Core/Surging.Core.Abp/AbpModule.cs index ebf557889..baac39e33 100644 --- a/src/Surging.Core/Surging.Core.Abp/AbpModule.cs +++ b/src/Surging.Core/Surging.Core.Abp/AbpModule.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyModel; using Microsoft.Extensions.Logging; @@ -12,35 +13,65 @@ using System.Reflection; using System.Text.RegularExpressions; using Volo.Abp.Configuration; -using VoloAbpModule = Volo.Abp.Modularity; -using Microsoft.Extensions.Configuration; using Volo.Abp.DependencyInjection; -using VoloAbp= Volo.Abp; - +using VoloAbp = Volo.Abp; +using VoloAbpModule = Volo.Abp.Modularity; namespace Surging.Core.Abp { + /// + /// Defines the + /// [ExposeServices] public class AbpModule : KestrelHttpModule { + #region 字段 + + /// + /// Defines the _logger + /// private ILogger _logger; - private List _providers=new List(); - public override void Initialize(AppModuleContext context) + + /// + /// Defines the _providers + /// + private List _providers = new List(); + + #endregion 字段 + + #region 方法 + + /// + /// The Dispose + /// + public override void Dispose() { - _logger = context.ServiceProvoider.GetInstances>(); + base.Dispose(); + _providers = null; } + /// + /// The Initialize + /// + /// The context public override void Initialize(ApplicationInitializationContext context) { _providers.ForEach(p => p.Initialize(context.Builder.ApplicationServices)); } - public override void Dispose() + /// + /// The Initialize + /// + /// The context + public override void Initialize(AppModuleContext context) { - base.Dispose(); - _providers = null; + _logger = context.ServiceProvoider.GetInstances>(); } + /// + /// The RegisterBuilder + /// + /// The context public override void RegisterBuilder(ConfigurationContext context) { context.Services.AddSingleton(new DefaultConfigurationAccessor(context.Configuration)); @@ -56,15 +87,20 @@ public override void RegisterBuilder(ConfigurationContext context) }); } } + /// /// Inject dependent third-party components /// /// protected override void RegisterBuilder(ContainerBuilderWrapper builder) { - } + /// + /// The GetAbstractModules + /// + /// The assembly + /// The private static List GetAbstractModules(Assembly assembly) { var abstractModules = new List(); @@ -78,6 +114,25 @@ protected override void RegisterBuilder(ContainerBuilderWrapper builder) return abstractModules; } + /// + /// The GetAllAssemblyFiles + /// + /// The parentDir + /// The + private List GetAllAssemblyFiles(string parentDir) + { + var pattern = string.Format("^Volo.Abp.\\w*"); + Regex relatedRegex = new Regex(pattern, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase); + return + Directory.GetFiles(parentDir, "*.dll").Select(Path.GetFullPath).Where( + a => relatedRegex.IsMatch(a)).ToList(); + } + + /// + /// The GetAssemblies + /// + /// The virtualPaths + /// The private List GetAssemblies(params string[] virtualPaths) { var referenceAssemblies = new List(); @@ -96,6 +151,25 @@ private List GetAssemblies(params string[] virtualPaths) return referenceAssemblies; } + /// + /// The GetFilterAssemblies + /// + /// The assemblyNames + /// The + private string[] GetFilterAssemblies(string[] assemblyNames) + { + var pattern = string.Format("^Volo.Abp.\\w*"); + Regex relatedRegex = new Regex(pattern, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase); + return + assemblyNames.Where( + name => relatedRegex.IsMatch(name)).ToArray(); + } + + /// + /// The GetReferenceAssembly + /// + /// The virtualPaths + /// The private List GetReferenceAssembly(params string[] virtualPaths) { var refAssemblies = new List(); @@ -118,22 +192,6 @@ private List GetReferenceAssembly(params string[] virtualPaths) return refAssemblies; } - private List GetAllAssemblyFiles(string parentDir) - { - var pattern = string.Format("^Volo.Abp.\\w*"); - Regex relatedRegex = new Regex(pattern, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase); - return - Directory.GetFiles(parentDir, "*.dll").Select(Path.GetFullPath).Where( - a => relatedRegex.IsMatch(a)).ToList(); - } - - private string[] GetFilterAssemblies(string[] assemblyNames) - { - var pattern = string.Format("^Volo.Abp.\\w*"); - Regex relatedRegex = new Regex(pattern, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase); - return - assemblyNames.Where( - name => relatedRegex.IsMatch(name)).ToArray(); - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/Aggregation/ServicePartProvider.cs b/src/Surging.Core/Surging.Core.ApiGateWay/Aggregation/ServicePartProvider.cs index fbe1c4906..52db57138 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/Aggregation/ServicePartProvider.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/Aggregation/ServicePartProvider.cs @@ -1,12 +1,12 @@ -using Surging.Core.ProxyGenerator; +using Newtonsoft.Json.Linq; +using Surging.Core.ApiGateWay.Configurations; +using Surging.Core.ProxyGenerator; using System; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.Linq; using System.Text; using System.Threading.Tasks; -using System.Linq; -using System.Collections.Concurrent; -using Newtonsoft.Json.Linq; -using Surging.Core.ApiGateWay.Configurations; namespace Surging.Core.ApiGateWay.Aggregation { @@ -15,14 +15,41 @@ namespace Surging.Core.ApiGateWay.Aggregation /// public class ServicePartProvider : IServicePartProvider { - private readonly IServiceProxyProvider _serviceProxyProvider; + #region 字段 + + /// + /// Defines the _servicePartTypes + /// private readonly ConcurrentDictionary _servicePartTypes = new ConcurrentDictionary(); + + /// + /// Defines the _serviceProxyProvider + /// + private readonly IServiceProxyProvider _serviceProxyProvider; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serviceProxyProvider public ServicePartProvider(IServiceProxyProvider serviceProxyProvider) { _serviceProxyProvider = serviceProxyProvider; } + #endregion 构造函数 + + #region 方法 + + /// + /// The IsPart + /// + /// The routhPath + /// The public bool IsPart(string routhPath) { var servicePart = AppConfig.ServicePart; @@ -39,10 +66,14 @@ public bool IsPart(string routhPath) } } return partType != ServicePartType.None; - - } + /// + /// The Merge + /// + /// The routhPath + /// The param + /// The public async Task Merge(string routhPath, Dictionary param) { var partType = _servicePartTypes.GetValueOrDefault(routhPath); @@ -71,5 +102,7 @@ public async Task Merge(string routhPath, Dictionary par } return jObject; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/Aggregation/ServicePartType.cs b/src/Surging.Core/Surging.Core.ApiGateWay/Aggregation/ServicePartType.cs index fbb2b6838..44e6dfefa 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/Aggregation/ServicePartType.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/Aggregation/ServicePartType.cs @@ -4,10 +4,28 @@ namespace Surging.Core.ApiGateWay.Aggregation { - public enum ServicePartType + #region 枚举 + + /// + /// Defines the ServicePartType + /// + public enum ServicePartType { + /// + /// Defines the None + /// None, + + /// + /// Defines the Main + /// Main, + + /// + /// Defines the Section + /// Section } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/AppConfig.cs b/src/Surging.Core/Surging.Core.ApiGateWay/AppConfig.cs index 92659c9f3..d16a8e812 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/AppConfig.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/AppConfig.cs @@ -8,26 +8,65 @@ namespace Surging.Core.ApiGateWay { + /// + /// Defines the + /// public static class AppConfig { - public static IConfigurationRoot Configuration { get; set; } + #region 字段 + /// + /// Defines the _accessTokenExpireTimeSpan + /// + private static TimeSpan _accessTokenExpireTimeSpan = TimeSpan.FromMinutes(30); + /// + /// Defines the _authorizationRoutePath + /// + private static string _authorizationRoutePath; + + /// + /// Defines the _authorizationServiceKey + /// private static string _authorizationServiceKey; - public static string AuthorizationServiceKey + + /// + /// Defines the _cacheMode + /// + private static string _cacheMode = "MemoryCache"; + + /// + /// Defines the _tokenEndpointPath + /// + private static string _tokenEndpointPath = "oauth2/token"; + + #endregion 字段 + + #region 属性 + + /// + /// Gets or sets the AccessTokenExpireTimeSpan + /// + public static TimeSpan AccessTokenExpireTimeSpan { get { - return Configuration["AuthorizationServiceKey"] ?? _authorizationServiceKey; + int tokenExpireTime; + if (Configuration["AccessTokenExpireTimeSpan"] != null && int.TryParse(Configuration["AccessTokenExpireTimeSpan"], out tokenExpireTime)) + { + _accessTokenExpireTimeSpan = TimeSpan.FromMinutes(tokenExpireTime); + } + return _accessTokenExpireTimeSpan; } internal set { - - _authorizationServiceKey = value; + _accessTokenExpireTimeSpan = value; } } - private static string _authorizationRoutePath; + /// + /// Gets or sets the AuthorizationRoutePath + /// public static string AuthorizationRoutePath { get @@ -36,56 +75,74 @@ public static string AuthorizationRoutePath } internal set { - _authorizationRoutePath = value; } } - private static TimeSpan _accessTokenExpireTimeSpan = TimeSpan.FromMinutes(30); - public static TimeSpan AccessTokenExpireTimeSpan + /// + /// Gets or sets the AuthorizationServiceKey + /// + public static string AuthorizationServiceKey { get { - int tokenExpireTime; - if (Configuration["AccessTokenExpireTimeSpan"] != null && int.TryParse(Configuration["AccessTokenExpireTimeSpan"], out tokenExpireTime)) - { - _accessTokenExpireTimeSpan = TimeSpan.FromMinutes(tokenExpireTime); - } - return _accessTokenExpireTimeSpan; + return Configuration["AuthorizationServiceKey"] ?? _authorizationServiceKey; } internal set { - _accessTokenExpireTimeSpan = value; + _authorizationServiceKey = value; } } - private static string _tokenEndpointPath = "oauth2/token"; - - public static string TokenEndpointPath + /// + /// Gets the CacheMode + /// + public static string CacheMode { get { - return Configuration["TokenEndpointPath"] ?? _tokenEndpointPath; + return Configuration["CacheMode"] ?? _cacheMode; } - internal set + } + + /// + /// Gets or sets the Configuration + /// + public static IConfigurationRoot Configuration { get; set; } + + /// + /// Gets the Policy + /// + public static AccessPolicy Policy + { + get { - _tokenEndpointPath = value; + var result = new AccessPolicy(); + var section = Configuration.GetSection("AccessPolicy"); + if (section != null) + result = section.Get(); + return result; } } + /// + /// Gets the Register + /// public static Register Register { get { var result = new Register(); - var section= Configuration.GetSection("Register"); + var section = Configuration.GetSection("Register"); if (section != null) - result= section.Get(); + result = section.Get(); return result; } - } + /// + /// Gets the ServicePart + /// public static ServicePart ServicePart { get @@ -98,27 +155,21 @@ public static ServicePart ServicePart } } - public static AccessPolicy Policy + /// + /// Gets or sets the TokenEndpointPath + /// + public static string TokenEndpointPath { get { - var result = new AccessPolicy(); - var section = Configuration.GetSection("AccessPolicy"); - if (section != null) - result = section.Get(); - return result; + return Configuration["TokenEndpointPath"] ?? _tokenEndpointPath; } - } - - private static string _cacheMode = "MemoryCache"; - - public static string CacheMode - { - get + internal set { - return Configuration["CacheMode"] ?? _cacheMode; + _tokenEndpointPath = value; } - } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/Configurations/AccessPolicy.cs b/src/Surging.Core/Surging.Core.ApiGateWay/Configurations/AccessPolicy.cs index cf3f76589..0671b2737 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/Configurations/AccessPolicy.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/Configurations/AccessPolicy.cs @@ -4,16 +4,38 @@ namespace Surging.Core.ApiGateWay.Configurations { - public class AccessPolicy + /// + /// Defines the + /// + public class AccessPolicy { - public string[] Origins { get; set; } + #region 属性 + /// + /// Gets or sets a value indicating whether AllowAnyHeader + /// public bool AllowAnyHeader { get; set; } + /// + /// Gets or sets a value indicating whether AllowAnyMethod + /// public bool AllowAnyMethod { get; set; } + /// + /// Gets or sets a value indicating whether AllowAnyOrigin + /// public bool AllowAnyOrigin { get; set; } + /// + /// Gets or sets a value indicating whether AllowCredentials + /// public bool AllowCredentials { get; set; } + + /// + /// Gets or sets the Origins + /// + public string[] Origins { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/Configurations/Register.cs b/src/Surging.Core/Surging.Core.ApiGateWay/Configurations/Register.cs index c1d52a7b9..60ec1965c 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/Configurations/Register.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/Configurations/Register.cs @@ -5,10 +5,23 @@ namespace Surging.Core.ApiGateWay { - public class Register + /// + /// Defines the + /// + public class Register { - public RegisterProvider Provider { get; set; } = RegisterProvider.Consul; + #region 属性 + /// + /// Gets or sets the Address + /// public string Address { get; set; } = "127.0.0.1:8500"; + + /// + /// Gets or sets the Provider + /// + public RegisterProvider Provider { get; set; } = RegisterProvider.Consul; + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/Configurations/RegisterProvider.cs b/src/Surging.Core/Surging.Core.ApiGateWay/Configurations/RegisterProvider.cs index 5a1010337..9c4b551dd 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/Configurations/RegisterProvider.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/Configurations/RegisterProvider.cs @@ -4,9 +4,23 @@ namespace Surging.Core.ApiGateWay.Configurations { - public enum RegisterProvider + #region 枚举 + + /// + /// Defines the RegisterProvider + /// + public enum RegisterProvider { + /// + /// Defines the Consul + /// Consul, + + /// + /// Defines the Zookeeper + /// Zookeeper, } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/Configurations/ServiceAggregation.cs b/src/Surging.Core/Surging.Core.ApiGateWay/Configurations/ServiceAggregation.cs index b5397609a..6b4349594 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/Configurations/ServiceAggregation.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/Configurations/ServiceAggregation.cs @@ -4,14 +4,33 @@ namespace Surging.Core.ApiGateWay.Configurations { - public class ServiceAggregation + /// + /// Defines the + /// + public class ServiceAggregation { - public string RoutePath { get; set; } + #region 属性 - public string ServiceKey { get; set; } + /// + /// Gets or sets the Key + /// + public string Key { get; set; } + /// + /// Gets or sets the Params + /// public Dictionary Params { get; set; } - public string Key { get; set; } + /// + /// Gets or sets the RoutePath + /// + public string RoutePath { get; set; } + + /// + /// Gets or sets the ServiceKey + /// + public string ServiceKey { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/Configurations/ServicePart.cs b/src/Surging.Core/Surging.Core.ApiGateWay/Configurations/ServicePart.cs index 88bf1d54d..ca8f3057c 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/Configurations/ServicePart.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/Configurations/ServicePart.cs @@ -4,12 +4,28 @@ namespace Surging.Core.ApiGateWay.Configurations { - public class ServicePart + /// + /// Defines the + /// + public class ServicePart { - public string MainPath { get; set; } = "part/service/aggregation"; + #region 属性 + /// + /// Gets or sets a value indicating whether EnableAuthorization + /// public bool EnableAuthorization { get; set; } - public List Services { get; set; } + /// + /// Gets or sets the MainPath + /// + public string MainPath { get; set; } = "part/service/aggregation"; + + /// + /// Gets or sets the Services + /// + public List Services { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/Configurations/Services.cs b/src/Surging.Core/Surging.Core.ApiGateWay/Configurations/Services.cs index 2b666a6fd..0cd76648c 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/Configurations/Services.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/Configurations/Services.cs @@ -4,9 +4,23 @@ namespace Surging.Core.ApiGateWay.Configurations { - public class Services + /// + /// Defines the + /// + public class Services { - public List serviceAggregation { get; set; } + #region 属性 + + /// + /// Gets or sets the serviceAggregation + /// + public List serviceAggregation { get; set; } + + /// + /// Gets or sets the UrlMapping + /// public string UrlMapping { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/ContainerBuilderExtensions.cs b/src/Surging.Core/Surging.Core.ApiGateWay/ContainerBuilderExtensions.cs index 61cdf2c22..847e5cd78 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/ContainerBuilderExtensions.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/ContainerBuilderExtensions.cs @@ -14,15 +14,20 @@ namespace Surging.Core.ApiGateWay { - public static class ContainerBuilderExtensions + /// + /// Defines the + /// + public static class ContainerBuilderExtensions { + #region 方法 + /// /// 添加网关中间件 /// /// 服务构建者 /// /// 服务构建者 - public static IServiceBuilder AddApiGateWay(this IServiceBuilder builder, ConfigInfo config=null) + public static IServiceBuilder AddApiGateWay(this IServiceBuilder builder, ConfigInfo config = null) { var services = builder.Services; services.RegisterType().As().SingleInstance(); @@ -47,5 +52,7 @@ public static IServiceBuilder AddApiGateWay(this IServiceBuilder builder, Config }).As().SingleInstance(); return builder; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/IServicePartProvider.cs b/src/Surging.Core/Surging.Core.ApiGateWay/IServicePartProvider.cs index 1a14758fe..3f2d41057 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/IServicePartProvider.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/IServicePartProvider.cs @@ -5,10 +5,32 @@ namespace Surging.Core.ApiGateWay { - public interface IServicePartProvider + #region 接口 + + /// + /// Defines the + /// + public interface IServicePartProvider { + #region 方法 + + /// + /// The IsPart + /// + /// The routhPath + /// The bool IsPart(string routhPath); + /// + /// The Merge + /// + /// The routhPath + /// The param + /// The Task Merge(string routhPath, Dictionary param); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/IAuthorizationServerProvider.cs b/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/IAuthorizationServerProvider.cs index d61fe3523..f86a13fee 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/IAuthorizationServerProvider.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/IAuthorizationServerProvider.cs @@ -5,12 +5,38 @@ namespace Surging.Core.ApiGateWay.OAuth { + #region 接口 + + /// + /// Defines the + /// public interface IAuthorizationServerProvider { + #region 方法 + + /// + /// The GenerateTokenCredential + /// + /// The parameters + /// The Task GenerateTokenCredential(Dictionary parameters); + /// + /// The GetPayloadString + /// + /// The token + /// The + string GetPayloadString(string token); + + /// + /// The ValidateClientAuthentication + /// + /// The token + /// The Task ValidateClientAuthentication(string token); - string GetPayloadString(string token); + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/AuthorizationServerProvider.cs b/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/AuthorizationServerProvider.cs index fd48fc044..908fbf981 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/AuthorizationServerProvider.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/AuthorizationServerProvider.cs @@ -1,30 +1,59 @@ using Newtonsoft.Json; +using Surging.Core.Caching; using Surging.Core.CPlatform; +using Surging.Core.CPlatform.Cache; using Surging.Core.CPlatform.Routing; using Surging.Core.ProxyGenerator; using System; using System.Collections.Generic; +using System.Linq; using System.Security.Cryptography; using System.Text; -using System.Threading.Tasks; -using System.Linq; -using Surging.Core.Caching; using System.Text.RegularExpressions; -using Surging.Core.CPlatform.Cache; +using System.Threading.Tasks; namespace Surging.Core.ApiGateWay.OAuth { /// /// 授权服务提供者 /// - public class AuthorizationServerProvider: IAuthorizationServerProvider + public class AuthorizationServerProvider : IAuthorizationServerProvider { + #region 字段 + + /// + /// Defines the _cacheProvider + /// + private readonly ICacheProvider _cacheProvider; + + /// + /// Defines the _serviceProvider + /// + private readonly CPlatformContainer _serviceProvider; + + /// + /// Defines the _serviceProxyProvider + /// private readonly IServiceProxyProvider _serviceProxyProvider; + + /// + /// Defines the _serviceRouteProvider + /// private readonly IServiceRouteProvider _serviceRouteProvider; - private readonly CPlatformContainer _serviceProvider; - private readonly ICacheProvider _cacheProvider; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The configInfo + /// The serviceProxyProvider + /// The serviceRouteProvider + /// The serviceProvider public AuthorizationServerProvider(ConfigInfo configInfo, IServiceProxyProvider serviceProxyProvider - ,IServiceRouteProvider serviceRouteProvider + , IServiceRouteProvider serviceRouteProvider , CPlatformContainer serviceProvider) { _serviceProvider = serviceProvider; @@ -33,35 +62,53 @@ public AuthorizationServerProvider(ConfigInfo configInfo, IServiceProxyProvider _cacheProvider = CacheContainer.GetService(AppConfig.CacheMode); } + #endregion 构造函数 + + #region 方法 + + /// + /// The GenerateTokenCredential + /// + /// The parameters + /// The public async Task GenerateTokenCredential(Dictionary parameters) { string result = null; - var payload = await _serviceProxyProvider.Invoke(parameters,AppConfig.AuthorizationRoutePath, AppConfig.AuthorizationServiceKey); - if (payload!=null && !payload.Equals("null") ) + var payload = await _serviceProxyProvider.Invoke(parameters, AppConfig.AuthorizationRoutePath, AppConfig.AuthorizationServiceKey); + if (payload != null && !payload.Equals("null")) { var jwtHeader = JsonConvert.SerializeObject(new JWTSecureDataHeader() { TimeStamp = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") }); var base64Payload = ConverBase64String(JsonConvert.SerializeObject(payload)); var encodedString = $"{ConverBase64String(jwtHeader)}.{base64Payload}"; var route = await _serviceRouteProvider.GetRouteByPath(AppConfig.AuthorizationRoutePath); var signature = HMACSHA256(encodedString, route.ServiceDescriptor.Token); - result= $"{encodedString}.{signature}"; - _cacheProvider.Add(base64Payload, result,AppConfig.AccessTokenExpireTimeSpan); + result = $"{encodedString}.{signature}"; + _cacheProvider.Add(base64Payload, result, AppConfig.AccessTokenExpireTimeSpan); } return result; } + /// + /// The GetPayloadString + /// + /// The token + /// The public string GetPayloadString(string token) { - string result = null; + string result = null; var jwtToken = token.Split('.'); if (jwtToken.Length == 3) { - - result = Encoding.UTF8.GetString(Convert.FromBase64String(jwtToken[1])); + result = Encoding.UTF8.GetString(Convert.FromBase64String(jwtToken[1])); } return result; } + /// + /// The ValidateClientAuthentication + /// + /// The token + /// The public async Task ValidateClientAuthentication(string token) { bool isSuccess = false; @@ -73,14 +120,25 @@ public async Task ValidateClientAuthentication(string token) return isSuccess; } + /// + /// The ConverBase64String + /// + /// The str + /// The private string ConverBase64String(string str) { return Convert.ToBase64String(Encoding.UTF8.GetBytes(str)); } + /// + /// The HMACSHA256 + /// + /// The message + /// The secret + /// The private string HMACSHA256(string message, string secret) { - secret = secret ?? ""; + secret = secret ?? ""; byte[] keyByte = Encoding.UTF8.GetBytes(secret); byte[] messageBytes = Encoding.UTF8.GetBytes(message); using (var hmacsha256 = new HMACSHA256(keyByte)) @@ -89,5 +147,7 @@ private string HMACSHA256(string message, string secret) return Convert.ToBase64String(hashmessage); } } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/Configurations/ConfigInfo.cs b/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/Configurations/ConfigInfo.cs index dc2a64073..32a016eaa 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/Configurations/ConfigInfo.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/Configurations/ConfigInfo.cs @@ -4,27 +4,57 @@ namespace Surging.Core.ApiGateWay.OAuth { + /// + /// Defines the + /// public class ConfigInfo { - public ConfigInfo(string authorizationRoutePath):this(authorizationRoutePath,null, TimeSpan.FromMinutes(30)) - { + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The authorizationRoutePath + public ConfigInfo(string authorizationRoutePath) : this(authorizationRoutePath, null, TimeSpan.FromMinutes(30)) + { } - - public ConfigInfo(string authorizationRoutePath,string authorizationServiceKey, TimeSpan accessTokenExpireTimeSpan) + + /// + /// Initializes a new instance of the class. + /// + /// The authorizationRoutePath + /// The authorizationServiceKey + /// The accessTokenExpireTimeSpan + public ConfigInfo(string authorizationRoutePath, string authorizationServiceKey, TimeSpan accessTokenExpireTimeSpan) { AuthorizationServiceKey = authorizationServiceKey; AuthorizationRoutePath = authorizationRoutePath; AccessTokenExpireTimeSpan = accessTokenExpireTimeSpan; } - public string AuthorizationServiceKey { get; set; } + + #endregion 构造函数 + + #region 属性 + /// + /// Gets or sets the AccessTokenExpireTimeSpan + /// token 有效期 + /// + public TimeSpan AccessTokenExpireTimeSpan { get; set; } = TimeSpan.FromMinutes(30); + + /// + /// Gets or sets the AuthorizationRoutePath /// 授权服务路由地址 /// public string AuthorizationRoutePath { get; set; } + /// - /// token 有效期 + /// Gets or sets the AuthorizationServiceKey /// - public TimeSpan AccessTokenExpireTimeSpan { get; set; } = TimeSpan.FromMinutes(30); - }; -} + public string AuthorizationServiceKey { get; set; } + + #endregion 属性 + } + +; +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/Configurations/GatewayConfigurationExtensions.cs b/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/Configurations/GatewayConfigurationExtensions.cs index 8c1e7244d..679fb52a1 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/Configurations/GatewayConfigurationExtensions.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/Configurations/GatewayConfigurationExtensions.cs @@ -8,23 +8,22 @@ namespace Surging.Core.ApiGateWay.OAuth.Implementation.Configurations { + /// + /// Defines the + /// public static class GatewayConfigurationExtensions { - public static IConfigurationBuilder AddGatewayFile(this IConfigurationBuilder builder, string path) - { - return AddGatewayFile(builder, provider: null, path: path, optional: false, reloadOnChange: false); - } - - public static IConfigurationBuilder AddGatewayFile(this IConfigurationBuilder builder, string path, bool optional) - { - return AddGatewayFile(builder, provider: null, path: path, optional: optional, reloadOnChange: false); - } - - public static IConfigurationBuilder AddGatewayFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange) - { - return AddGatewayFile(builder, provider: null, path: path, optional: optional, reloadOnChange: reloadOnChange); - } + #region 方法 + /// + /// The AddGatewayFile + /// + /// The builder + /// The provider + /// The path + /// The optional + /// The reloadOnChange + /// The public static IConfigurationBuilder AddGatewayFile(this IConfigurationBuilder builder, IFileProvider provider, string path, bool optional, bool reloadOnChange) { Check.NotNull(builder, "builder"); @@ -45,5 +44,43 @@ public static IConfigurationBuilder AddGatewayFile(this IConfigurationBuilder bu AppConfig.Configuration = builder.Build(); return builder; } + + /// + /// The AddGatewayFile + /// + /// The builder + /// The path + /// The + public static IConfigurationBuilder AddGatewayFile(this IConfigurationBuilder builder, string path) + { + return AddGatewayFile(builder, provider: null, path: path, optional: false, reloadOnChange: false); + } + + /// + /// The AddGatewayFile + /// + /// The builder + /// The path + /// The optional + /// The + public static IConfigurationBuilder AddGatewayFile(this IConfigurationBuilder builder, string path, bool optional) + { + return AddGatewayFile(builder, provider: null, path: path, optional: optional, reloadOnChange: false); + } + + /// + /// The AddGatewayFile + /// + /// The builder + /// The path + /// The optional + /// The reloadOnChange + /// The + public static IConfigurationBuilder AddGatewayFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange) + { + return AddGatewayFile(builder, provider: null, path: path, optional: optional, reloadOnChange: reloadOnChange); + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/Configurations/GatewayConfigurationProvider.cs b/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/Configurations/GatewayConfigurationProvider.cs index 253ad1b5f..ec05b04f8 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/Configurations/GatewayConfigurationProvider.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/Configurations/GatewayConfigurationProvider.cs @@ -7,14 +7,35 @@ namespace Surging.Core.ApiGateWay.OAuth.Implementation.Configurations { - public class GatewayConfigurationProvider : FileConfigurationProvider + /// + /// Defines the + /// + public class GatewayConfigurationProvider : FileConfigurationProvider { - public GatewayConfigurationProvider(GatewayConfigurationSource source) : base(source) { } + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The source + public GatewayConfigurationProvider(GatewayConfigurationSource source) : base(source) + { + } + + #endregion 构造函数 + + #region 方法 + + /// + /// The Load + /// + /// The stream public override void Load(Stream stream) { var parser = new JsonConfigurationParser(); this.Data = parser.Parse(stream, null); } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/Configurations/GatewayConfigurationSource.cs b/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/Configurations/GatewayConfigurationSource.cs index 11026b5a4..8eee7e36d 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/Configurations/GatewayConfigurationSource.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/Configurations/GatewayConfigurationSource.cs @@ -5,14 +5,33 @@ namespace Surging.Core.ApiGateWay.OAuth.Implementation.Configurations { - public class GatewayConfigurationSource : FileConfigurationSource + /// + /// Defines the + /// + public class GatewayConfigurationSource : FileConfigurationSource { + #region 属性 + + /// + /// Gets or sets the ConfigurationKeyPrefix + /// public string ConfigurationKeyPrefix { get; set; } + #endregion 属性 + + #region 方法 + + /// + /// The Build + /// + /// The builder + /// The public override IConfigurationProvider Build(IConfigurationBuilder builder) { FileProvider = FileProvider ?? builder.GetFileProvider(); return new GatewayConfigurationProvider(this); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/EncryptMode.cs b/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/EncryptMode.cs index e7eb58077..8f252e8f7 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/EncryptMode.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/EncryptMode.cs @@ -4,8 +4,18 @@ namespace Surging.Core.ApiGateWay.OAuth { + #region 枚举 + + /// + /// Defines the EncryptMode + /// public enum EncryptMode { + /// + /// Defines the HS256 + /// HS256 } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/JWTSecureDataHeader.cs b/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/JWTSecureDataHeader.cs index 3c77f2593..18d8eb055 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/JWTSecureDataHeader.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/JWTSecureDataHeader.cs @@ -4,12 +4,28 @@ namespace Surging.Core.ApiGateWay.OAuth { - public class JWTSecureDataHeader + /// + /// Defines the + /// + public class JWTSecureDataHeader { - public JWTSecureDataType Type { get; set; } + #region 属性 + /// + /// Gets or sets the EncryptMode + /// public EncryptMode EncryptMode { get; set; } + /// + /// Gets or sets the TimeStamp + /// public string TimeStamp { get; set; } + + /// + /// Gets or sets the Type + /// + public JWTSecureDataType Type { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/JWTSecureDataType.cs b/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/JWTSecureDataType.cs index a10513bb6..cd8696c6e 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/JWTSecureDataType.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/OAuth/Implementation/JWTSecureDataType.cs @@ -4,8 +4,18 @@ namespace Surging.Core.ApiGateWay.OAuth { + #region 枚举 + + /// + /// Defines the JWTSecureDataType + /// public enum JWTSecureDataType { + /// + /// Defines the JWT + /// JWT } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/IFaultTolerantProvider.cs b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/IFaultTolerantProvider.cs index 01bec651a..d6f6eeddf 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/IFaultTolerantProvider.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/IFaultTolerantProvider.cs @@ -6,12 +6,38 @@ namespace Surging.Core.ApiGateWay.ServiceDiscovery { + #region 接口 + + /// + /// Defines the + /// public interface IFaultTolerantProvider { - Task> GetCommandDescriptor(params string [] serviceIds); + #region 方法 + + /// + /// The GetCommandDescriptor + /// + /// The serviceIds + /// The + Task> GetCommandDescriptor(params string[] serviceIds); + /// + /// The GetCommandDescriptorByAddress + /// + /// The address + /// The Task> GetCommandDescriptorByAddress(string address); + /// + /// The SetCommandDescriptorByAddress + /// + /// The model + /// The Task SetCommandDescriptorByAddress(ServiceCommandDescriptor model); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/IServiceCacheProvider.cs b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/IServiceCacheProvider.cs index ab0bb4528..729ff9613 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/IServiceCacheProvider.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/IServiceCacheProvider.cs @@ -6,16 +6,55 @@ namespace Surging.Core.ApiGateWay.ServiceDiscovery { + #region 接口 + + /// + /// Defines the + /// public interface IServiceCacheProvider { - Task> GetServiceDescriptorAsync(); + #region 方法 + + /// + /// The DelCacheEndpointAsync + /// + /// The cacheId + /// The endpoint + /// The + Task DelCacheEndpointAsync(string cacheId, string endpoint); + /// + /// The GetCacheEndpointAsync + /// + /// The cacheId + /// The Task> GetCacheEndpointAsync(string cacheId); - Task GetCacheEndpointAsync(string cacheId,string endpoint); + /// + /// The GetCacheEndpointAsync + /// + /// The cacheId + /// The endpoint + /// The + Task GetCacheEndpointAsync(string cacheId, string endpoint); - Task DelCacheEndpointAsync(string cacheId, string endpoint); + /// + /// The GetServiceDescriptorAsync + /// + /// The + Task> GetServiceDescriptorAsync(); + /// + /// The SetCacheEndpointByEndpoint + /// + /// The cacheId + /// The endpoint + /// The cacheEndpoint + /// The Task SetCacheEndpointByEndpoint(string cacheId, string endpoint, CacheEndpoint cacheEndpoint); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/IServiceDiscoveryProvider.cs b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/IServiceDiscoveryProvider.cs index 69b10d7c6..7bce406ad 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/IServiceDiscoveryProvider.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/IServiceDiscoveryProvider.cs @@ -8,12 +8,39 @@ namespace Surging.Core.ApiGateWay.ServiceDiscovery { - public interface IServiceDiscoveryProvider + #region 接口 + + /// + /// Defines the + /// + public interface IServiceDiscoveryProvider { + #region 方法 + + /// + /// The EditServiceToken + /// + /// The address + /// The + Task EditServiceToken(AddressModel address); + + /// + /// The GetAddressAsync + /// + /// The condition + /// The Task> GetAddressAsync(string condition = null); + /// + /// The GetServiceDescriptorAsync + /// + /// The address + /// The condition + /// The Task> GetServiceDescriptorAsync(string address, string condition = null); - - Task EditServiceToken(AddressModel address); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/IServiceRegisterProvider.cs b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/IServiceRegisterProvider.cs index 417ff1ad0..b2af0dc00 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/IServiceRegisterProvider.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/IServiceRegisterProvider.cs @@ -6,9 +6,24 @@ namespace Surging.Core.ApiGateWay.ServiceDiscovery { + #region 接口 + + /// + /// Defines the + /// public interface IServiceRegisterProvider { + #region 方法 + /// + /// The GetAddressAsync + /// + /// The condition + /// The Task> GetAddressAsync(string condition = null); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/IServiceSubscribeProvider.cs b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/IServiceSubscribeProvider.cs index 9eb2a2c08..d6b3c6d75 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/IServiceSubscribeProvider.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/IServiceSubscribeProvider.cs @@ -7,10 +7,32 @@ namespace Surging.Core.ApiGateWay.ServiceDiscovery { + #region 接口 + + /// + /// Defines the + /// public interface IServiceSubscribeProvider { + #region 方法 + + /// + /// The GetAddressAsync + /// + /// The condition + /// The Task> GetAddressAsync(string condition = null); + /// + /// The GetServiceDescriptorAsync + /// + /// The address + /// The condition + /// The Task> GetServiceDescriptorAsync(string address, string condition = null); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/Implementation/FaultTolerantProvider.cs b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/Implementation/FaultTolerantProvider.cs index bc0d391c0..1b61fccfd 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/Implementation/FaultTolerantProvider.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/Implementation/FaultTolerantProvider.cs @@ -1,10 +1,10 @@ -using System; +using Surging.Core.CPlatform.Support; +using Surging.Core.System; +using System; using System.Collections.Generic; +using System.Linq; using System.Text; using System.Threading.Tasks; -using Surging.Core.CPlatform.Support; -using Surging.Core.System; -using System.Linq; namespace Surging.Core.ApiGateWay.ServiceDiscovery.Implementation { @@ -13,20 +13,39 @@ namespace Surging.Core.ApiGateWay.ServiceDiscovery.Implementation /// public class FaultTolerantProvider : ServiceBase, IFaultTolerantProvider { + #region 方法 + + /// + /// The GetCommandDescriptor + /// + /// The serviceIds + /// The public async Task> GetCommandDescriptor(params string[] serviceIds) { - return await GetService().GetServiceCommandsAsync(serviceIds); + return await GetService().GetServiceCommandsAsync(serviceIds); } + /// + /// The GetCommandDescriptorByAddress + /// + /// The address + /// The public async Task> GetCommandDescriptorByAddress(string address) { var services = await GetService().GetServiceDescriptorAsync(address); - return await GetService().GetServiceCommandsAsync(services.Select(p=>p.Id).ToArray()); + return await GetService().GetServiceCommandsAsync(services.Select(p => p.Id).ToArray()); } + /// + /// The SetCommandDescriptorByAddress + /// + /// The model + /// The public async Task SetCommandDescriptorByAddress(ServiceCommandDescriptor model) { await GetService().SetServiceCommandsAsync(new ServiceCommandDescriptor[] { model }); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/Implementation/ServiceAddressModel.cs b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/Implementation/ServiceAddressModel.cs index 3a96a1d48..6a1ce3851 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/Implementation/ServiceAddressModel.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/Implementation/ServiceAddressModel.cs @@ -5,10 +5,23 @@ namespace Surging.Core.ApiGateWay.ServiceDiscovery.Implementation { - public class ServiceAddressModel + /// + /// Defines the + /// + public class ServiceAddressModel { + #region 属性 + + /// + /// Gets or sets the Address + /// public AddressModel Address { get; set; } - public bool IsHealth { get; set; } + /// + /// Gets or sets a value indicating whether IsHealth + /// + public bool IsHealth { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/Implementation/ServiceCacheProvider.cs b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/Implementation/ServiceCacheProvider.cs index db4db9afa..8929a0ffa 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/Implementation/ServiceCacheProvider.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/Implementation/ServiceCacheProvider.cs @@ -1,11 +1,11 @@ -using System; +using Surging.Core.CPlatform.Cache; +using Surging.Core.CPlatform.Serialization; +using Surging.Core.System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using Surging.Core.CPlatform.Cache; -using Surging.Core.CPlatform.Serialization; -using Surging.Core.System; namespace Surging.Core.ApiGateWay.ServiceDiscovery.Implementation { @@ -14,32 +14,40 @@ namespace Surging.Core.ApiGateWay.ServiceDiscovery.Implementation /// public class ServiceCacheProvider : ServiceBase, IServiceCacheProvider { + #region 字段 + + /// + /// Defines the _serializer + /// private readonly ISerializer _serializer; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serializer public ServiceCacheProvider(ISerializer serializer) { _serializer = serializer; } - public async Task> GetServiceDescriptorAsync() - { - return await GetService().GetCacheDescriptorAsync(); - } - public async Task> GetCacheEndpointAsync(string cacheId) - { - return await GetService().GetCacheEndpointAsync(cacheId); - } + #endregion 构造函数 - public async Task GetCacheEndpointAsync(string cacheId, string endpoint) - { - return await GetService().GetCacheEndpointAsync(cacheId, endpoint); - } + #region 方法 - public async Task SetCacheEndpointByEndpoint(string cacheId, string endpoint, CacheEndpoint cacheEndpoint) + /// + /// The DelCacheEndpointAsync + /// + /// The cacheId + /// The endpoint + /// The + public async Task DelCacheEndpointAsync(string cacheId, string endpoint) { var model = await GetService().GetAsync(cacheId); - - var cacheEndpoints = model.CacheEndpoint.Where(p => p.ToString() != cacheEndpoint.ToString()).ToList(); - cacheEndpoints.Add(cacheEndpoint); + var cacheEndpoints = model.CacheEndpoint.Where(p => p.ToString() != endpoint).ToList(); model.CacheEndpoint = cacheEndpoints; var caches = new ServiceCache[] { model }; var descriptors = caches.Where(cache => cache != null).Select(cache => new ServiceCacheDescriptor @@ -54,11 +62,49 @@ public async Task SetCacheEndpointByEndpoint(string cacheId, string endpoint, Ca await GetService().SetCachesAsync(descriptors); } + /// + /// The GetCacheEndpointAsync + /// + /// The cacheId + /// The + public async Task> GetCacheEndpointAsync(string cacheId) + { + return await GetService().GetCacheEndpointAsync(cacheId); + } - public async Task DelCacheEndpointAsync(string cacheId, string endpoint) + /// + /// The GetCacheEndpointAsync + /// + /// The cacheId + /// The endpoint + /// The + public async Task GetCacheEndpointAsync(string cacheId, string endpoint) + { + return await GetService().GetCacheEndpointAsync(cacheId, endpoint); + } + + /// + /// The GetServiceDescriptorAsync + /// + /// The + public async Task> GetServiceDescriptorAsync() + { + return await GetService().GetCacheDescriptorAsync(); + } + + /// + /// The SetCacheEndpointByEndpoint + /// + /// The cacheId + /// The endpoint + /// The cacheEndpoint + /// The + public async Task SetCacheEndpointByEndpoint(string cacheId, string endpoint, CacheEndpoint cacheEndpoint) { var model = await GetService().GetAsync(cacheId); - var cacheEndpoints = model.CacheEndpoint.Where(p => p.ToString() != endpoint).ToList(); + + var cacheEndpoints = model.CacheEndpoint.Where(p => p.ToString() != cacheEndpoint.ToString()).ToList(); + cacheEndpoints.Add(cacheEndpoint); model.CacheEndpoint = cacheEndpoints; var caches = new ServiceCache[] { model }; var descriptors = caches.Where(cache => cache != null).Select(cache => new ServiceCacheDescriptor @@ -72,5 +118,7 @@ public async Task DelCacheEndpointAsync(string cacheId, string endpoint) }); await GetService().SetCachesAsync(descriptors); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/Implementation/ServiceDiscoveryProvider.cs b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/Implementation/ServiceDiscoveryProvider.cs index 57cde3b61..ebc2ceef6 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/Implementation/ServiceDiscoveryProvider.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/Implementation/ServiceDiscoveryProvider.cs @@ -1,12 +1,11 @@ - -using System.Collections.Generic; +using Surging.Core.CPlatform; +using Surging.Core.CPlatform.Address; using Surging.Core.CPlatform.Routing; -using System.Threading.Tasks; -using Surging.Core.System; using Surging.Core.CPlatform.Runtime.Client.HealthChecks; -using Surging.Core.CPlatform; -using Surging.Core.CPlatform.Address; +using Surging.Core.System; +using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; namespace Surging.Core.ApiGateWay.ServiceDiscovery.Implementation { @@ -15,30 +14,24 @@ namespace Surging.Core.ApiGateWay.ServiceDiscovery.Implementation /// public class ServiceDiscoveryProvider : ServiceBase, IServiceDiscoveryProvider { - public ServiceDiscoveryProvider() - { + #region 构造函数 - } - public async Task> GetAddressAsync(string condition = null) + /// + /// Initializes a new instance of the class. + /// + public ServiceDiscoveryProvider() { - var result = new List(); - var addresses = await GetService().GetAddressAsync(condition); - foreach (var address in addresses) - { - result.Add(new ServiceAddressModel - { - Address = address, - IsHealth = await GetService().IsHealth(address) - }); - } - return result; } - public async Task> GetServiceDescriptorAsync(string address, string condition = null) - { - return await GetService().GetServiceDescriptorAsync(address, condition); - } + #endregion 构造函数 + + #region 方法 + /// + /// The EditServiceToken + /// + /// The address + /// The public async Task EditServiceToken(AddressModel address) { var routes = await GetService().GetRoutesAsync(address.ToString()); @@ -56,8 +49,39 @@ public async Task EditServiceToken(AddressModel address) }); await GetService().ClearAsync(); await GetService().SetRoutesAsync(serviceRoutes); + } + + /// + /// The GetAddressAsync + /// + /// The condition + /// The + public async Task> GetAddressAsync(string condition = null) + { + var result = new List(); + var addresses = await GetService().GetAddressAsync(condition); + foreach (var address in addresses) + { + result.Add(new ServiceAddressModel + { + Address = address, + IsHealth = await GetService().IsHealth(address) + }); + } + return result; + } + /// + /// The GetServiceDescriptorAsync + /// + /// The address + /// The condition + /// The + public async Task> GetServiceDescriptorAsync(string address, string condition = null) + { + return await GetService().GetServiceDescriptorAsync(address, condition); } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/Implementation/ServiceRegisterProvider.cs b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/Implementation/ServiceRegisterProvider.cs index b73623b3e..d8a0ad7a9 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/Implementation/ServiceRegisterProvider.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/Implementation/ServiceRegisterProvider.cs @@ -14,7 +14,31 @@ namespace Surging.Core.ApiGateWay.ServiceDiscovery.Implementation /// public class ServiceRegisterProvider : ServiceBase, IServiceRegisterProvider { - public async Task> GetAddressAsync(string condition = null) + #region 方法 + + /// + /// The ConvertAddressModel + /// + /// The connection + /// The + public AddressModel ConvertAddressModel(string connection) + { + var address = connection.Split(":"); + if (address.Length > 1) + { + int port; + int.TryParse(address[1], out port); + return new IpAddressModel(address[0], port); + } + return null; + } + + /// + /// The GetAddressAsync + /// + /// The condition + /// The + public async Task> GetAddressAsync(string condition = null) { var result = new List(); var registerConfig = AppConfig.Register; @@ -23,14 +47,12 @@ public async Task> GetAddressAsync(string cond { foreach (var address in addresses) { - var addr = ConvertAddressModel(address); result.Add(new ServiceAddressModel { Address = addr, IsHealth = await GetService().IsHealth(addr) }); - } } else @@ -50,16 +72,6 @@ public async Task> GetAddressAsync(string cond return result; } - public AddressModel ConvertAddressModel(string connection) - { - var address = connection.Split(":"); - if (address.Length > 1) - { - int port; - int.TryParse(address[1], out port); - return new IpAddressModel(address[0], port); - } - return null; - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/Implementation/ServiceSubscribeProvider.cs b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/Implementation/ServiceSubscribeProvider.cs index e5947ea0f..4434d2b48 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/Implementation/ServiceSubscribeProvider.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceDiscovery/Implementation/ServiceSubscribeProvider.cs @@ -1,10 +1,10 @@ -using Surging.Core.System; +using Surging.Core.CPlatform; +using Surging.Core.CPlatform.Runtime.Client; +using Surging.Core.System; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; -using Surging.Core.CPlatform.Runtime.Client; -using Surging.Core.CPlatform; namespace Surging.Core.ApiGateWay.ServiceDiscovery.Implementation { @@ -13,6 +13,13 @@ namespace Surging.Core.ApiGateWay.ServiceDiscovery.Implementation /// public class ServiceSubscribeProvider : ServiceBase, IServiceSubscribeProvider { + #region 方法 + + /// + /// The GetAddressAsync + /// + /// The condition + /// The public async Task> GetAddressAsync(string condition = null) { var result = new List(); @@ -27,9 +34,17 @@ public async Task> GetAddressAsync(string condi return result; } + /// + /// The GetServiceDescriptorAsync + /// + /// The address + /// The condition + /// The public async Task> GetServiceDescriptorAsync(string address, string condition = null) { return await GetService().GetServiceDescriptorAsync(address, condition); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceResult.cs b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceResult.cs index 9440871a3..f95d389f8 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceResult.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceResult.cs @@ -8,14 +8,20 @@ namespace Surging.Core.ApiGateWay /// 自定义结果对象 /// /// 需要返回的类型 - public class ServiceResult : ServiceResult { + #region 属性 + /// + /// Gets or sets the Entity /// 数据集 /// public T Entity { get; set; } + #endregion 属性 + + #region 方法 + /// /// 生成自定义服务数据集 /// @@ -47,34 +53,19 @@ public static ServiceResult Create(bool successd, T entity) Entity = entity }; } + + #endregion 方法 } + /// + /// Defines the + /// public class ServiceResult { - /// - /// 生成错误信息 - /// - /// 返回客户端的消息 - /// 返回服务数据集 - public static ServiceResult Error(string message) - { - return new ServiceResult() { Message = message, IsSucceed = false }; - } - - /// - /// 生成服务器数据集 - /// - /// 状态值(true:成功 false:失败) - /// 返回客户端的消息 - /// 错误信息 - /// 返回服务数据集 - public static ServiceResult Create(bool success, string successMessage = "", string errorMessage = "") - { - return new ServiceResult() { Message = success ? successMessage : errorMessage, IsSucceed = success }; - } + #region 构造函数 /// - /// 构造服务数据集 + /// Initializes a new instance of the class. /// public ServiceResult() { @@ -82,20 +73,54 @@ public ServiceResult() Message = string.Empty; } + #endregion 构造函数 + + #region 属性 + /// + /// Gets or sets a value indicating whether IsSucceed /// 状态值 /// - public bool IsSucceed { get; set; } /// - ///返回客户端的消息 + /// Gets or sets the Message + /// 返回客户端的消息 /// public string Message { get; set; } /// + /// Gets or sets the StatusCode /// 状态码 /// public int StatusCode { get; set; } + + #endregion 属性 + + #region 方法 + + /// + /// 生成服务器数据集 + /// + /// 状态值(true:成功 false:失败) + /// 返回客户端的消息 + /// 错误信息 + /// 返回服务数据集 + public static ServiceResult Create(bool success, string successMessage = "", string errorMessage = "") + { + return new ServiceResult() { Message = success ? successMessage : errorMessage, IsSucceed = success }; + } + + /// + /// 生成错误信息 + /// + /// 返回客户端的消息 + /// 返回服务数据集 + public static ServiceResult Error(string message) + { + return new ServiceResult() { Message = message, IsSucceed = false }; + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceStatusCode.cs b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceStatusCode.cs index 0d8f40cc9..2ecb14117 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/ServiceStatusCode.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/ServiceStatusCode.cs @@ -4,10 +4,28 @@ namespace Surging.Core.ApiGateWay { - public enum ServiceStatusCode + #region 枚举 + + /// + /// Defines the ServiceStatusCode + /// + public enum ServiceStatusCode { - Success=200, - RequestError =400, - AuthorizationFailed=401, + /// + /// Defines the Success + /// + Success = 200, + + /// + /// Defines the RequestError + /// + RequestError = 400, + + /// + /// Defines the AuthorizationFailed + /// + AuthorizationFailed = 401, } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ApiGateWay/Utilities/ServiceResult.cs b/src/Surging.Core/Surging.Core.ApiGateWay/Utilities/ServiceResult.cs index c2a277452..9bd04973c 100644 --- a/src/Surging.Core/Surging.Core.ApiGateWay/Utilities/ServiceResult.cs +++ b/src/Surging.Core/Surging.Core.ApiGateWay/Utilities/ServiceResult.cs @@ -8,14 +8,20 @@ namespace Surging.Core.ApiGateWay.Utilities /// 自定义错误结果对象 /// /// 需要返回的类型 - public class ServiceResult : ServiceResult { + #region 属性 + /// + /// Gets or sets the Entity /// 数据集 /// public T Entity { get; set; } + #endregion 属性 + + #region 方法 + /// /// 生成自定义服务数据集 /// @@ -47,20 +53,52 @@ public static ServiceResult Create(bool successd, T entity) Entity = entity }; } + + #endregion 方法 } + /// + /// Defines the + /// public class ServiceResult { + #region 构造函数 + /// - /// 生成错误信息 + /// Initializes a new instance of the class. /// - /// 返回客户端的消息 - /// 返回服务数据集 - public static ServiceResult Error(string message) + public ServiceResult() { - return new ServiceResult() { Message = message, IsSucceed = false }; + IsSucceed = false; + Message = string.Empty; } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets or sets the ErrorCode + /// 错误码 + /// + public int ErrorCode { get; set; } + + /// + /// Gets or sets a value indicating whether IsSucceed + /// 状态值 + /// + public bool IsSucceed { get; set; } + + /// + /// Gets or sets the Message + /// 返回客户端的消息 + /// + public string Message { get; set; } + + #endregion 属性 + + #region 方法 + /// /// 生成服务器数据集 /// @@ -74,28 +112,15 @@ public static ServiceResult Create(bool success, string successMessage = "", str } /// - /// 构造服务数据集 + /// 生成错误信息 /// - public ServiceResult() + /// 返回客户端的消息 + /// 返回服务数据集 + public static ServiceResult Error(string message) { - IsSucceed = false; - Message = string.Empty; + return new ServiceResult() { Message = message, IsSucceed = false }; } - /// - /// 状态值 - /// - - public bool IsSucceed { get; set; } - - /// - ///返回客户端的消息 - /// - public string Message { get; set; } - - /// - /// 错误码 - /// - public int ErrorCode { get; set; } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.AutoMapper/AppConfig.cs b/src/Surging.Core/Surging.Core.AutoMapper/AppConfig.cs index 893220630..f6879d370 100644 --- a/src/Surging.Core/Surging.Core.AutoMapper/AppConfig.cs +++ b/src/Surging.Core/Surging.Core.AutoMapper/AppConfig.cs @@ -13,9 +13,25 @@ namespace Surging.Core.AutoMapper { + /// + /// Defines the + /// public class AppConfig { + #region 字段 + /// + /// Defines the _referenceAssembly + /// + private static List _referenceAssembly = new List(); + + #endregion 字段 + + #region 属性 + + /// + /// Gets the Assemblies + /// public static IEnumerable Assemblies { get @@ -30,6 +46,14 @@ public static IEnumerable Assemblies } } + /// + /// Gets or sets the AssembliesStrings + /// + public static IEnumerable AssembliesStrings { get; internal set; } + + /// + /// Gets the Profiles + /// public static IEnumerable Profiles { get @@ -54,7 +78,6 @@ public static IEnumerable Profiles if (logger.IsEnabled(LogLevel.Warning)) logger.LogWarning($"构建profile失败,profile类型为{profileType.FullName}"); } - } } } @@ -62,54 +85,15 @@ public static IEnumerable Profiles } } - private static IEnumerable GetAllReferenceAssemblies() - { - var serviceEngine = ServiceLocator.GetService() as VirtualPathProviderServiceEngine; - string[] paths = null; - if (serviceEngine != null) - { - if (serviceEngine.ModuleServiceLocationFormats != null) - { - paths = GetPaths(serviceEngine.ModuleServiceLocationFormats); - } - } - var referenceAssemblies = paths == null ? GetReferenceAssembly() : GetReferenceAssembly(paths); - return referenceAssemblies; - } - - private static List _referenceAssembly = new List(); + #endregion 属性 - public static IEnumerable AssembliesStrings { get; internal set; } - - private static List GetReferenceAssembly(params string[] virtualPaths) - { - var refAssemblies = new List();//Assembly 通过此类能够载入操纵一个程序集,并获取程序集内部信息 - var rootPath = AppContext.BaseDirectory; - var existsPath = virtualPaths.Any();//判断是否有数据 - if (existsPath && !string.IsNullOrEmpty(CPlatformAppConfig.ServerOptions.RootPath)) - rootPath = CPlatformAppConfig.ServerOptions.RootPath; - var result = _referenceAssembly; - if (!result.Any() || existsPath) - { - var paths = virtualPaths.Select(m => Path.Combine(rootPath, m)).ToList(); - if (!existsPath) paths.Add(rootPath); - paths.ForEach(path => - { - var assemblyFiles = GetAllAssemblyFiles(path); - - foreach (var referencedAssemblyFile in assemblyFiles) - { - var referencedAssembly = Assembly.LoadFrom(referencedAssemblyFile); - if (!_referenceAssembly.Contains(referencedAssembly)) - _referenceAssembly.Add(referencedAssembly); - refAssemblies.Add(referencedAssembly); - } - result = existsPath ? refAssemblies : _referenceAssembly; - }); - } - return result; - } + #region 方法 + /// + /// The GetAllAssemblyFiles + /// + /// The parentDir + /// The private static List GetAllAssemblyFiles(string parentDir) { var notRelatedFile = CPlatformAppConfig.ServerOptions.NotRelatedAssemblyFiles; @@ -132,6 +116,30 @@ private static List GetAllAssemblyFiles(string parentDir) } } + /// + /// The GetAllReferenceAssemblies + /// + /// The + private static IEnumerable GetAllReferenceAssemblies() + { + var serviceEngine = ServiceLocator.GetService() as VirtualPathProviderServiceEngine; + string[] paths = null; + if (serviceEngine != null) + { + if (serviceEngine.ModuleServiceLocationFormats != null) + { + paths = GetPaths(serviceEngine.ModuleServiceLocationFormats); + } + } + var referenceAssemblies = paths == null ? GetReferenceAssembly() : GetReferenceAssembly(paths); + return referenceAssemblies; + } + + /// + /// The GetPaths + /// + /// The virtualPaths + /// The private static string[] GetPaths(params string[] virtualPaths) { var directories = new List(virtualPaths.Where(p => !string.IsNullOrEmpty(p))); @@ -155,5 +163,40 @@ private static string[] GetPaths(params string[] virtualPaths) return directories.Any() ? directories.Distinct().ToArray() : virPaths; } + /// + /// The GetReferenceAssembly + /// + /// The virtualPaths + /// The + private static List GetReferenceAssembly(params string[] virtualPaths) + { + var refAssemblies = new List();//Assembly 通过此类能够载入操纵一个程序集,并获取程序集内部信息 + var rootPath = AppContext.BaseDirectory; + var existsPath = virtualPaths.Any();//判断是否有数据 + if (existsPath && !string.IsNullOrEmpty(CPlatformAppConfig.ServerOptions.RootPath)) + rootPath = CPlatformAppConfig.ServerOptions.RootPath; + var result = _referenceAssembly; + if (!result.Any() || existsPath) + { + var paths = virtualPaths.Select(m => Path.Combine(rootPath, m)).ToList(); + if (!existsPath) paths.Add(rootPath); + paths.ForEach(path => + { + var assemblyFiles = GetAllAssemblyFiles(path); + + foreach (var referencedAssemblyFile in assemblyFiles) + { + var referencedAssembly = Assembly.LoadFrom(referencedAssemblyFile); + if (!_referenceAssembly.Contains(referencedAssembly)) + _referenceAssembly.Add(referencedAssembly); + refAssemblies.Add(referencedAssembly); + } + result = existsPath ? refAssemblies : _referenceAssembly; + }); + } + return result; + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.AutoMapper/AutoMapper/AutoMapperBootstrap.cs b/src/Surging.Core/Surging.Core.AutoMapper/AutoMapper/AutoMapperBootstrap.cs index e8d8362ad..7b7c3d8bf 100644 --- a/src/Surging.Core/Surging.Core.AutoMapper/AutoMapper/AutoMapperBootstrap.cs +++ b/src/Surging.Core/Surging.Core.AutoMapper/AutoMapper/AutoMapperBootstrap.cs @@ -6,12 +6,21 @@ namespace Surging.Core.AutoMapper { + /// + /// Defines the + /// public class AutoMapperBootstrap : IAutoMapperBootstrap { + #region 方法 + + /// + /// The Initialize + /// public void Initialize() { var logger = ServiceLocator.GetService>(); - Mapper.Initialize(config => { + Mapper.Initialize(config => + { if (AppConfig.Assemblies.Any()) { foreach (var assembly in AppConfig.Assemblies) @@ -29,9 +38,9 @@ public void Initialize() config.AddProfile(profile); } } - }); } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.AutoMapper/AutoMapper/IAutoMapperBootstrap.cs b/src/Surging.Core/Surging.Core.AutoMapper/AutoMapper/IAutoMapperBootstrap.cs index d2678015b..ddf3cae00 100644 --- a/src/Surging.Core/Surging.Core.AutoMapper/AutoMapper/IAutoMapperBootstrap.cs +++ b/src/Surging.Core/Surging.Core.AutoMapper/AutoMapper/IAutoMapperBootstrap.cs @@ -1,7 +1,21 @@ namespace Surging.Core.AutoMapper { + #region 接口 + + /// + /// Defines the + /// public interface IAutoMapperBootstrap { + #region 方法 + + /// + /// The Initialize + /// void Initialize(); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.AutoMapper/AutoMapper/MapperExtensions.cs b/src/Surging.Core/Surging.Core.AutoMapper/AutoMapper/MapperExtensions.cs index 86201636a..a35af2fd4 100644 --- a/src/Surging.Core/Surging.Core.AutoMapper/AutoMapper/MapperExtensions.cs +++ b/src/Surging.Core/Surging.Core.AutoMapper/AutoMapper/MapperExtensions.cs @@ -2,16 +2,37 @@ namespace Surging.Core.AutoMapper { + /// + /// Defines the + /// public static class MapperExtensions { + #region 方法 + + /// + /// The MapTo + /// + /// + /// The obj + /// The public static T MapTo(this object obj) where T : class { return Mapper.Map(obj); } + /// + /// The MapTo + /// + /// + /// + /// The obj + /// The entity + /// The public static TDestination MapTo(this TSource obj, TDestination entity) where TSource : class where TDestination : class { return Mapper.Map(obj, entity); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.AutoMapper/AutoMapperModule.cs b/src/Surging.Core/Surging.Core.AutoMapper/AutoMapperModule.cs index c757f9021..652655beb 100644 --- a/src/Surging.Core/Surging.Core.AutoMapper/AutoMapperModule.cs +++ b/src/Surging.Core/Surging.Core.AutoMapper/AutoMapperModule.cs @@ -5,15 +5,27 @@ namespace Surging.Core.AutoMapper { + /// + /// Defines the + /// public class AutoMapperModule : EnginePartModule { + #region 方法 + /// + /// The Initialize + /// + /// The context public override void Initialize(AppModuleContext context) { base.Initialize(context); context.ServiceProvoider.GetInstances().Initialize(); } + /// + /// The RegisterBuilder + /// + /// The builder protected override void RegisterBuilder(ContainerBuilderWrapper builder) { var configAssembliesStr = CPlatformAppConfig.GetSection("Automapper:Assemblies").Get(); @@ -24,6 +36,6 @@ protected override void RegisterBuilder(ContainerBuilderWrapper builder) builder.RegisterType().As(); } - + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.AutoMapper/ServiceHostBuilderExtensions.cs b/src/Surging.Core/Surging.Core.AutoMapper/ServiceHostBuilderExtensions.cs index c1f7b63c4..d2c00238e 100644 --- a/src/Surging.Core/Surging.Core.AutoMapper/ServiceHostBuilderExtensions.cs +++ b/src/Surging.Core/Surging.Core.AutoMapper/ServiceHostBuilderExtensions.cs @@ -3,8 +3,18 @@ namespace Surging.Core.AutoMapper { + /// + /// Defines the + /// public static class ServiceHostBuilderExtensions { + #region 方法 + + /// + /// The UserAutoMapper + /// + /// The hostBuilder + /// The public static IServiceHostBuilder UserAutoMapper(this IServiceHostBuilder hostBuilder) { return hostBuilder.MapServices(mapper => @@ -13,5 +23,7 @@ public static IServiceHostBuilder UserAutoMapper(this IServiceHostBuilder hostBu autoMapperBootstrap.Initialize(); }); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Address/AddressHelper.cs b/src/Surging.Core/Surging.Core.CPlatform/Address/AddressHelper.cs index 0e96ae8cd..71886eed1 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Address/AddressHelper.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Address/AddressHelper.cs @@ -6,8 +6,20 @@ namespace Surging.Core.CPlatform.Address { + /// + /// 地址操作者 + /// public class AddressHelper { + #region 方法 + + /// + /// 根据地址获取IP + /// + /// 地址 + /// + /// + /// public static string GetIpFromAddress(string address) { if (IsValidIp(address)) @@ -15,9 +27,24 @@ public static string GetIpFromAddress(string address) return address; } var ips = Dns.GetHostAddresses(address); - return ips[0].ToString(); + var ipRes = ips[0].ToString(); + foreach (var itemIP in ips) + { + if (IsValidIp(itemIP.ToString())) + { + ipRes = itemIP.ToString(); + break; + } + } + return ipRes; } + /// + /// 判断地址是不是IP + /// + /// 地址 + /// + /// public static bool IsValidIp(string address) { if (Regex.IsMatch(address, "[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}")) @@ -34,5 +61,7 @@ public static bool IsValidIp(string address) } return false; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Address/AddressModel.cs b/src/Surging.Core/Surging.Core.CPlatform/Address/AddressModel.cs index ce5591a8b..108a51662 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Address/AddressModel.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Address/AddressModel.cs @@ -9,25 +9,29 @@ namespace Surging.Core.CPlatform.Address /// public abstract class AddressModel { + #region 属性 + + /// + /// Gets or sets the ProcessorTime + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public decimal ProcessorTime { get; set; } + + #endregion 属性 + + #region 方法 + /// /// 创建终结点。 /// /// public abstract EndPoint CreateEndPoint(); - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public decimal ProcessorTime { get; set; } /// - /// 重写后的标识。 + /// The Equals /// - /// 一个字符串。 - public abstract override string ToString(); - - #region Equality members - - /// Determines whether the specified object is equal to the current object. - /// true if the specified object is equal to the current object; otherwise, false. /// The object to compare with the current object. + /// true if the specified object is equal to the current object; otherwise, false. public override bool Equals(object obj) { var model = obj as AddressModel; @@ -40,13 +44,23 @@ public override bool Equals(object obj) return model.ToString() == ToString(); } - /// Serves as the default hash function. + /// + /// The GetHashCode + /// /// A hash code for the current object. public override int GetHashCode() { return ToString().GetHashCode(); } + /// + /// 重写后的标识。 + /// + /// 一个字符串。 + public abstract override string ToString(); + + #endregion 方法 + public static bool operator ==(AddressModel model1, AddressModel model2) { return Equals(model1, model2); @@ -56,7 +70,5 @@ public override int GetHashCode() { return !Equals(model1, model2); } - - #endregion Equality members } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Address/IpAddressModel.cs b/src/Surging.Core/Surging.Core.CPlatform/Address/IpAddressModel.cs index b31420863..cbe1c00b7 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Address/IpAddressModel.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Address/IpAddressModel.cs @@ -10,17 +10,17 @@ namespace Surging.Core.CPlatform.Address /// public sealed class IpAddressModel : AddressModel { - #region Constructor + #region 캯 /// - /// ʼһµipַģʵ + /// Initializes a new instance of the class. /// public IpAddressModel() { } /// - /// ʼһµipַģʵ + /// Initializes a new instance of the class. /// /// ipַ /// ˿ڡ @@ -30,35 +30,49 @@ public IpAddressModel(string ip, int port) Port = port; } - #endregion Constructor + #endregion 캯 - #region Property + #region /// + /// Gets or sets the HttpPort + /// + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public int? HttpPort { get; set; } + + /// + /// Gets or sets the Ip /// ipַ /// public string Ip { get; set; } /// + /// Gets or sets the MqttPort + /// + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public int? MqttPort { get; set; } + + /// + /// Gets or sets the Port /// ˿ڡ /// public int Port { get; set; } + /// + /// Gets or sets the WanIp + /// [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string WanIp { get; set; } + /// + /// Gets or sets the WsPort + /// [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public int? WsPort { get; set; } - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public int? MqttPort { get; set; } + #endregion - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public int? HttpPort { get; set; } - - #endregion Property - - #region Overrides of AddressModel + #region /// /// ս㡣 @@ -69,12 +83,15 @@ public override EndPoint CreateEndPoint() return new IPEndPoint(IPAddress.Parse(AddressHelper.GetIpFromAddress(Ip)), Port); } - + /// + /// The ToString + /// + /// The public override string ToString() { - return string.Concat(new string[] { AddressHelper.GetIpFromAddress(Ip), ":" , Port.ToString() }); + return string.Concat(new string[] { AddressHelper.GetIpFromAddress(Ip), ":", Port.ToString() }); } - #endregion Overrides of AddressModel + #endregion } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/AppConfig.cs b/src/Surging.Core/Surging.Core.CPlatform/AppConfig.cs index c0833cf86..e200e63bd 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/AppConfig.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/AppConfig.cs @@ -12,16 +12,34 @@ namespace Surging.Core.CPlatform { - public class AppConfig + /// + /// Defines the + /// + public class AppConfig { #region 字段 - private static AddressSelectorMode _loadBalanceMode=AddressSelectorMode.Polling; - private static SurgingServerOptions _serverOptions=new SurgingServerOptions(); - #endregion + /// + /// Defines the _loadBalanceMode + /// + private static AddressSelectorMode _loadBalanceMode = AddressSelectorMode.Polling; + + /// + /// Defines the _serverOptions + /// + private static SurgingServerOptions _serverOptions = new SurgingServerOptions(); + + #endregion 字段 + + #region 属性 + + /// + /// Gets or sets the Configuration + /// public static IConfigurationRoot Configuration { get; internal set; } /// + /// Gets or sets the LoadBalanceMode /// 负载均衡模式 /// public static AddressSelectorMode LoadBalanceMode @@ -29,8 +47,8 @@ public static AddressSelectorMode LoadBalanceMode get { AddressSelectorMode mode = _loadBalanceMode; ; - if(Configuration !=null - && Configuration["AccessTokenExpireTimeSpan"]!=null + if (Configuration != null + && Configuration["AccessTokenExpireTimeSpan"] != null && !Enum.TryParse(Configuration["AccessTokenExpireTimeSpan"], out mode)) { mode = _loadBalanceMode; @@ -43,11 +61,9 @@ internal set } } - public static IConfigurationSection GetSection(string name) - { - return Configuration?.GetSection(name); - } - + /// + /// Gets or sets the ServerOptions + /// public static SurgingServerOptions ServerOptions { get @@ -59,5 +75,21 @@ internal set _serverOptions = value; } } + + #endregion 属性 + + #region 方法 + + /// + /// The GetSection + /// + /// The name + /// The + public static IConfigurationSection GetSection(string name) + { + return Configuration?.GetSection(name); + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/CPlatformContainer.cs b/src/Surging.Core/Surging.Core.CPlatform/CPlatformContainer.cs index 0c0c20266..766171347 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/CPlatformContainer.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/CPlatformContainer.cs @@ -9,79 +9,155 @@ namespace Surging.Core.CPlatform /// public class CPlatformContainer { - private IComponentContext _container; + #region 字段 + /// + /// Defines the _container + /// + private IComponentContext _container; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The container + public CPlatformContainer(IComponentContext container) + { + this._container = container; + } + + #endregion 构造函数 + + #region 属性 + + /// + /// Gets or sets the Current + /// public IComponentContext Current { get { return _container; } - internal set + internal set { _container = value; } } - public CPlatformContainer(IComponentContext container) - { - this._container = container; - } + #endregion 属性 - public bool IsRegistered() - { - return _container.IsRegistered(); - } + #region 方法 - public bool IsRegisteredWithKey(string serviceKey,Type serviceType) + /// + /// The GetInstancePerLifetimeScope + /// + /// The name + /// The type + /// The + public object GetInstancePerLifetimeScope(string name, Type type) { - if(!string.IsNullOrEmpty(serviceKey)) - return _container.IsRegisteredWithKey(serviceKey, serviceType); - else - return _container.IsRegistered(serviceType); + return string.IsNullOrEmpty(name) ? GetInstances(type) : _container.ResolveKeyed(name, type); } - - public bool IsRegistered(object serviceKey) + + /// + /// The GetInstances + /// + /// + /// The + public T GetInstances() where T : class { - return _container.IsRegisteredWithKey(serviceKey); + return _container.Resolve(); } - public T GetInstances(string name) where T : class + /// + /// The GetInstances + /// + /// + /// The name + /// The + public T GetInstances(string name) where T : class { - return _container.ResolveKeyed(name); } - public T GetInstances() where T : class + /// + /// The GetInstances + /// + /// The name + /// The type + /// The + public object GetInstances(string name, Type type) { - return _container.Resolve(); + // var appConfig = AppConfig.DefaultInstance; + var objInstance = ServiceResolver.Current.GetService(type, name); + if (objInstance == null) + { + objInstance = string.IsNullOrEmpty(name) ? GetInstances(type) : _container.ResolveKeyed(name, type); + ServiceResolver.Current.Register(name, objInstance, type); + } + return objInstance; } - public object GetInstances(Type type) + /// + /// The GetInstances + /// + /// The type + /// The + public object GetInstances(Type type) { return _container.Resolve(type); } + /// + /// The GetInstances + /// + /// + /// The type + /// The public T GetInstances(Type type) where T : class { return _container.Resolve(type) as T; } - public object GetInstancePerLifetimeScope(string name, Type type) + /// + /// The IsRegistered + /// + /// + /// The + public bool IsRegistered() { - return string.IsNullOrEmpty(name) ? GetInstances(type) : _container.ResolveKeyed(name, type); + return _container.IsRegistered(); } - public object GetInstances(string name,Type type) + /// + /// The IsRegistered + /// + /// + /// The serviceKey + /// The + public bool IsRegistered(object serviceKey) { - // var appConfig = AppConfig.DefaultInstance; - var objInstance = ServiceResolver.Current.GetService(type, name); - if (objInstance == null) - { - objInstance = string.IsNullOrEmpty(name) ? GetInstances(type) : _container.ResolveKeyed(name, type); - ServiceResolver.Current.Register(name, objInstance, type); - } - return objInstance; + return _container.IsRegisteredWithKey(serviceKey); } + + /// + /// The IsRegisteredWithKey + /// + /// The serviceKey + /// The serviceType + /// The + public bool IsRegisteredWithKey(string serviceKey, Type serviceType) + { + if (!string.IsNullOrEmpty(serviceKey)) + return _container.IsRegisteredWithKey(serviceKey, serviceType); + else + return _container.IsRegistered(serviceType); + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/CPlatformResource.Designer.cs b/src/Surging.Core/Surging.Core.CPlatform/CPlatformResource.Designer.cs index fe9c1a3a5..461da698e 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/CPlatformResource.Designer.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/CPlatformResource.Designer.cs @@ -8,128 +8,178 @@ // //------------------------------------------------------------------------------ -namespace Surging.Core.CPlatform { +namespace Surging.Core.CPlatform +{ using System; - - + /// - /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// 一个强类型的资源类,用于查找本地化的字符串等。 /// - // 此类是由 StronglyTypedResourceBuilder - // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 - // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen - // (以 /str 作为命令选项),或重新生成 VS 项目。 [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class CPlatformResource { - - private static global::System.Resources.ResourceManager resourceMan; - + internal class CPlatformResource + { + #region 字段 + + /// + /// Defines the resourceCulture + /// private static global::System.Globalization.CultureInfo resourceCulture; - + + /// + /// Defines the resourceMan + /// + private static global::System.Resources.ResourceManager resourceMan; + + #endregion + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal CPlatformResource() { + internal CPlatformResource() + { } - + + #endregion + + #region 属性 + /// - /// 返回此类使用的缓存的 ResourceManager 实例。 + /// Gets the ArgumentIsNullOrWhitespace + /// 查找类似 "{0}"参数不能为空 的本地化字符串。 /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Surging.Core.CPlatform.CPlatformResource", typeof(CPlatformResource).Assembly); - resourceMan = temp; - } - return resourceMan; + internal static string ArgumentIsNullOrWhitespace + { + get + { + return ResourceManager.GetString("ArgumentIsNullOrWhitespace", resourceCulture); } } - + /// - /// 使用此强类型资源类,为所有资源查找 + /// Gets or sets the Culture + /// 使用此强类型资源类,为所有资源查找 /// 重写当前线程的 CurrentUICulture 属性。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { + internal static global::System.Globalization.CultureInfo Culture + { + get + { return resourceCulture; } - set { + set + { resourceCulture = value; } } - - /// - /// 查找类似 "{0}"参数不能为空 的本地化字符串。 - /// - internal static string ArgumentIsNullOrWhitespace { - get { - return ResourceManager.GetString("ArgumentIsNullOrWhitespace", resourceCulture); - } - } - + /// - /// 查找类似 调用远程配置终结点发生错误: {0} - {1} 的本地化字符串。 + /// Gets the HttpException + /// 查找类似 调用远程配置终结点发生错误: {0} - {1} 的本地化字符串。 /// - internal static string HttpException { - get { + internal static string HttpException + { + get + { return ResourceManager.GetString("HttpException", resourceCulture); } } - + /// - /// 查找类似 {0} 不能以'{1}'结束 的本地化字符串。 + /// Gets the InvalidEndCharacter + /// 查找类似 {0} 不能以'{1}'结束 的本地化字符串。 /// - internal static string InvalidEndCharacter { - get { + internal static string InvalidEndCharacter + { + get + { return ResourceManager.GetString("InvalidEndCharacter", resourceCulture); } } - + /// - /// 查找类似 {0} 不能以'{1}'开始 的本地化字符串。 + /// Gets the InvalidStartCharacter + /// 查找类似 {0} 不能以'{1}'开始 的本地化字符串。 /// - internal static string InvalidStartCharacter { - get { + internal static string InvalidStartCharacter + { + get + { return ResourceManager.GetString("InvalidStartCharacter", resourceCulture); } } - + /// - /// 查找类似 不能解析JSON文本,行号 '{0}': '{1}'. 的本地化字符串。 + /// Gets the JSONParseException + /// 查找类似 不能解析JSON文本,行号 '{0}': '{1}'. 的本地化字符串。 /// - internal static string JSONParseException { - get { + internal static string JSONParseException + { + get + { return ResourceManager.GetString("JSONParseException", resourceCulture); } } - + /// - /// 查找类似 路由模板不能以'/'或'〜'字符开头。 的本地化字符串。 + /// Gets the ResourceManager + /// 返回此类使用的缓存的 ResourceManager 实例。 /// - internal static string TemplateRoute_InvalidRouteTemplate { - get { + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if (object.ReferenceEquals(resourceMan, null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Surging.Core.CPlatform.CPlatformResource", typeof(CPlatformResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Gets the TemplateRoute_InvalidRouteTemplate + /// 查找类似 路由模板不能以'/'或'〜'字符开头。 的本地化字符串。 + /// + internal static string TemplateRoute_InvalidRouteTemplate + { + get + { return ResourceManager.GetString("TemplateRoute_InvalidRouteTemplate", resourceCulture); } } - + /// - /// 查找类似 路由模板中有一个不完整的参数。 检查每个“{”字符是否具有匹配的'}'字符。 的本地化字符串。 + /// Gets the TemplateRoute_MismatchedParameter + /// 查找类似 路由模板中有一个不完整的参数。 检查每个“{”字符是否具有匹配的'}'字符。 的本地化字符串。 /// - internal static string TemplateRoute_MismatchedParameter { - get { + internal static string TemplateRoute_MismatchedParameter + { + get + { return ResourceManager.GetString("TemplateRoute_MismatchedParameter", resourceCulture); } } - + /// - /// 查找类似 此'{0}' JSON 令牌不支持,路径'{1}',行'{2}',位置'{3}' 的本地化字符串。 + /// Gets the UnsupportedJSONToken + /// 查找类似 此'{0}' JSON 令牌不支持,路径'{1}',行'{2}',位置'{3}' 的本地化字符串。 /// - internal static string UnsupportedJSONToken { - get { + internal static string UnsupportedJSONToken + { + get + { return ResourceManager.GetString("UnsupportedJSONToken", resourceCulture); } } + + #endregion } } diff --git a/src/Surging.Core/Surging.Core.CPlatform/Cache/CacheDescriptor.cs b/src/Surging.Core/Surging.Core.CPlatform/Cache/CacheDescriptor.cs index 109e76b9e..202ffcb85 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Cache/CacheDescriptor.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Cache/CacheDescriptor.cs @@ -5,112 +5,58 @@ namespace Surging.Core.CPlatform.Cache { - - /// - /// 服务描述符扩展方法。 - /// - public static class CacheDescriptorExtensions - { - - /// - /// 获取默认失效时间 。 - /// - /// 缓存描述符。 - /// 失效时间。 - public static int DefaultExpireTime(this CacheDescriptor descriptor) - { - return descriptor.GetMetadata("DefaultExpireTime", 60); - } - - /// - /// 设置默认失效时间。 - /// - /// 缓存描述述符。 - /// 失效时间。 - /// 缓存描述符。 - public static CacheDescriptor DefaultExpireTime(this CacheDescriptor descriptor, int defaultExpireTime) - { - descriptor.Metadatas["DefaultExpireTime"] = defaultExpireTime; - return descriptor; - } - - - /// - /// 获取连接超时时间。 - /// - /// 缓存描述述符。 - /// 失效时间。 - /// 缓存描述符。 - public static int ConnectTimeout(this CacheDescriptor descriptor) - { - return descriptor.GetMetadata("ConnectTimeout", 60); - } - - /// - /// 设置连接超时时间。 - /// - /// 缓存描述述符。 - /// 超时时间。 - /// 缓存描述符。 - public static CacheDescriptor ConnectTimeout(this CacheDescriptor descriptor, int connectTimeout) - { - descriptor.Metadatas["ConnectTimeout"] = connectTimeout; - return descriptor; - } - - } - /// /// 服务描述符。 /// [Serializable] public class CacheDescriptor { + #region 构造函数 + /// - /// 初始化一个新的服务描述符。 + /// Initializes a new instance of the class. /// public CacheDescriptor() { Metadatas = new Dictionary(StringComparer.OrdinalIgnoreCase); } + #endregion 构造函数 + + #region 属性 + /// + /// Gets or sets the Id /// 缓存Id。 /// public string Id { get; set; } - public string Prefix { get; set; } - /// - /// 类型 + /// Gets or sets the Metadatas + /// 元数据。 /// - public string Type { get; set; } + public IDictionary Metadatas { get; set; } /// - /// 元数据。 + /// Gets or sets the Prefix /// - public IDictionary Metadatas { get; set; } + public string Prefix { get; set; } /// - /// 获取一个元数据。 + /// Gets or sets the Type + /// 类型 /// - /// 元数据类型。 - /// 元数据名称。 - /// 如果指定名称的元数据不存在则返回这个参数。 - /// 元数据值。 - public T GetMetadata(string name, T def = default(T)) - { - if (!Metadatas.ContainsKey(name)) - return def; + public string Type { get; set; } - return (T)Metadatas[name]; - } + #endregion 属性 - #region Equality members + #region 方法 - /// Determines whether the specified object is equal to the current object. - /// true if the specified object is equal to the current object; otherwise, false. + /// + /// The Equals + /// /// The object to compare with the current object. + /// true if the specified object is equal to the current object; otherwise, false. public override bool Equals(object obj) { var model = obj as CacheDescriptor; @@ -138,13 +84,32 @@ public override bool Equals(object obj) }); } - /// Serves as the default hash function. + /// + /// The GetHashCode + /// /// A hash code for the current object. public override int GetHashCode() { return ToString().GetHashCode(); } + /// + /// 获取一个元数据。 + /// + /// 元数据类型。 + /// 元数据名称。 + /// 如果指定名称的元数据不存在则返回这个参数。 + /// 元数据值。 + public T GetMetadata(string name, T def = default(T)) + { + if (!Metadatas.ContainsKey(name)) + return def; + + return (T)Metadatas[name]; + } + + #endregion 方法 + public static bool operator ==(CacheDescriptor model1, CacheDescriptor model2) { return Equals(model1, model2); @@ -154,7 +119,59 @@ public override int GetHashCode() { return !Equals(model1, model2); } + } + + /// + /// 服务描述符扩展方法。 + /// + public static class CacheDescriptorExtensions + { + #region 方法 + + /// + /// 获取连接超时时间。 + /// + /// 缓存描述述符。 + /// 缓存描述符。 + public static int ConnectTimeout(this CacheDescriptor descriptor) + { + return descriptor.GetMetadata("ConnectTimeout", 60); + } + + /// + /// 设置连接超时时间。 + /// + /// 缓存描述述符。 + /// The connectTimeout + /// 缓存描述符。 + public static CacheDescriptor ConnectTimeout(this CacheDescriptor descriptor, int connectTimeout) + { + descriptor.Metadatas["ConnectTimeout"] = connectTimeout; + return descriptor; + } + + /// + /// 获取默认失效时间 。 + /// + /// 缓存描述符。 + /// 失效时间。 + public static int DefaultExpireTime(this CacheDescriptor descriptor) + { + return descriptor.GetMetadata("DefaultExpireTime", 60); + } + + /// + /// 设置默认失效时间。 + /// + /// 缓存描述述符。 + /// The defaultExpireTime + /// 缓存描述符。 + public static CacheDescriptor DefaultExpireTime(this CacheDescriptor descriptor, int defaultExpireTime) + { + descriptor.Metadatas["DefaultExpireTime"] = defaultExpireTime; + return descriptor; + } - #endregion Equality members + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Cache/CacheEndpoint.cs b/src/Surging.Core/Surging.Core.CPlatform/Cache/CacheEndpoint.cs index 3b7428924..163642d51 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Cache/CacheEndpoint.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Cache/CacheEndpoint.cs @@ -4,38 +4,34 @@ namespace Surging.Core.CPlatform.Cache { + /// + /// Defines the + /// public abstract class CacheEndpoint { + #region 属性 + /// + /// Gets or sets the Host /// 主机 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public string Host - { - get; - set; - } + public string Host { get; set; } /// + /// Gets or sets the Port /// 端口 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public int Port - { - get; - set; - } + public int Port { get; set; } - public abstract override string ToString(); + #endregion 属性 - #region Equality members + #region 方法 + /// + /// The Equals + /// + /// The obj + /// The public override bool Equals(object obj) { var model = obj as CacheEndpoint; @@ -48,11 +44,23 @@ public override bool Equals(object obj) return model.ToString() == ToString(); } + /// + /// The GetHashCode + /// + /// The public override int GetHashCode() { return ToString().GetHashCode(); } + /// + /// The ToString + /// + /// The + public abstract override string ToString(); + + #endregion 方法 + public static bool operator ==(CacheEndpoint model1, CacheEndpoint model2) { return Equals(model1, model2); @@ -62,7 +70,5 @@ public override int GetHashCode() { return !Equals(model1, model2); } - #endregion - } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Cache/ICacheNodeProvider.cs b/src/Surging.Core/Surging.Core.CPlatform/Cache/ICacheNodeProvider.cs index 251a2d959..53d729b4a 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Cache/ICacheNodeProvider.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Cache/ICacheNodeProvider.cs @@ -4,8 +4,23 @@ namespace Surging.Core.CPlatform.Cache { - public interface ICacheNodeProvider + #region 接口 + + /// + /// Defines the + /// + public interface ICacheNodeProvider { + #region 方法 + + /// + /// The GetServiceCaches + /// + /// The IEnumerable GetServiceCaches(); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Cache/ICacheProvider.cs b/src/Surging.Core/Surging.Core.CPlatform/Cache/ICacheProvider.cs index af1766bec..62200e77a 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Cache/ICacheProvider.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Cache/ICacheProvider.cs @@ -5,30 +5,166 @@ namespace Surging.Core.CPlatform.Cache { + #region 接口 + + /// + /// Defines the + /// public interface ICacheProvider { - Task ConnectionAsync(CacheEndpoint endpoint); + #region 属性 + + /// + /// Gets or sets the DefaultExpireTime + /// + long DefaultExpireTime { get; set; } + + /// + /// Gets or sets the KeySuffix + /// + string KeySuffix { get; set; } + + #endregion 属性 + + #region 方法 + + /// + /// The Add + /// + /// The key + /// The value void Add(string key, object value); - void AddAsync(string key, object value); + + /// + /// The Add + /// + /// The key + /// The value + /// The defaultExpire void Add(string key, object value, bool defaultExpire); - void AddAsync(string key, object value, bool defaultExpire); + + /// + /// The Add + /// + /// The key + /// The value + /// The numOfMinutes void Add(string key, object value, long numOfMinutes); - void AddAsync(string key, object value, long numOfMinutes); + + /// + /// The Add + /// + /// The key + /// The value + /// The timeSpan void Add(string key, object value, TimeSpan timeSpan); + + /// + /// The AddAsync + /// + /// The key + /// The value + void AddAsync(string key, object value); + + /// + /// The AddAsync + /// + /// The key + /// The value + /// The defaultExpire + void AddAsync(string key, object value, bool defaultExpire); + + /// + /// The AddAsync + /// + /// The key + /// The value + /// The numOfMinutes + void AddAsync(string key, object value, long numOfMinutes); + + /// + /// The AddAsync + /// + /// The key + /// The value + /// The timeSpan void AddAsync(string key, object value, TimeSpan timeSpan); + /// + /// The ConnectionAsync + /// + /// The endpoint + /// The + Task ConnectionAsync(CacheEndpoint endpoint); + + /// + /// The Get + /// + /// + /// The keys + /// The IDictionary Get(IEnumerable keys); - Task> GetAsync(IEnumerable keys); + + /// + /// The Get + /// + /// The key + /// The object Get(string key); - Task GetAsync(string key); + + /// + /// The Get + /// + /// + /// The key + /// The T Get(string key); + + /// + /// The GetAsync + /// + /// + /// The keys + /// The + Task> GetAsync(IEnumerable keys); + + /// + /// The GetAsync + /// + /// The key + /// The + Task GetAsync(string key); + + /// + /// The GetAsync + /// + /// + /// The key + /// The Task GetAsync(string key); + + /// + /// The GetCacheTryParse + /// + /// The key + /// The obj + /// The bool GetCacheTryParse(string key, out object obj); + + /// + /// The Remove + /// + /// The key void Remove(string key); + + /// + /// The RemoveAsync + /// + /// The key void RemoveAsync(string key); - long DefaultExpireTime { get; set; } - string KeySuffix { get; set; } - } -} + #endregion 方法 + } + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Cache/IServiceCacheFactory.cs b/src/Surging.Core/Surging.Core.CPlatform/Cache/IServiceCacheFactory.cs index 5b5b9a30c..a0d390935 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Cache/IServiceCacheFactory.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Cache/IServiceCacheFactory.cs @@ -5,8 +5,14 @@ namespace Surging.Core.CPlatform.Cache { - public interface IServiceCacheFactory + #region 接口 + + /// + /// Defines the + /// + public interface IServiceCacheFactory { + #region 方法 /// /// 根据服务路由描述符创建服务路由。 @@ -14,5 +20,9 @@ public interface IServiceCacheFactory /// 服务路由描述符。 /// 服务路由集合。 Task> CreateServiceCachesAsync(IEnumerable descriptors); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Cache/IServiceCacheManager.cs b/src/Surging.Core/Surging.Core.CPlatform/Cache/IServiceCacheManager.cs index 2b17a3b3e..5a3aa4611 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Cache/IServiceCacheManager.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Cache/IServiceCacheManager.cs @@ -8,54 +8,127 @@ namespace Surging.Core.CPlatform.Cache { + #region 接口 + + /// + /// Defines the + /// public interface IServiceCacheManager { + #region 事件 + + /// + /// Defines the Changed + /// + event EventHandler Changed; + + /// + /// Defines the Created + /// event EventHandler Created; + /// + /// Defines the Removed + /// event EventHandler Removed; - event EventHandler Changed; + #endregion 事件 + + #region 方法 + + /// + /// The ClearAsync + /// + /// The + Task ClearAsync(); + /// + /// The GetCachesAsync + /// + /// The Task> GetCachesAsync(); + /// + /// The RemveAddressAsync + /// + /// The endpoints + /// The + Task RemveAddressAsync(IEnumerable endpoints); + + /// + /// The SetCachesAsync + /// + /// The caches + /// The Task SetCachesAsync(IEnumerable caches); + /// + /// The SetCachesAsync + /// + /// The cacheDescriptors + /// The Task SetCachesAsync(IEnumerable cacheDescriptors); - Task RemveAddressAsync(IEnumerable endpoints); - - Task ClearAsync(); + #endregion 方法 } + #endregion 接口 + /// /// 服务命令管理者扩展方法。 /// public static class ServiceRouteManagerExtensions { + #region 方法 + + /// + /// The GetAsync + /// + /// The serviceCacheManager + /// The cacheId + /// The public static async Task GetAsync(this IServiceCacheManager serviceCacheManager, string cacheId) { return (await serviceCacheManager.GetCachesAsync()).SingleOrDefault(i => i.CacheDescriptor.Id == cacheId); } + /// + /// The GetCacheDescriptorAsync + /// + /// The serviceCacheManager + /// The public static async Task> GetCacheDescriptorAsync(this IServiceCacheManager serviceCacheManager) { var caches = await serviceCacheManager.GetCachesAsync(); return caches.Select(p => p.CacheDescriptor); } + /// + /// The GetCacheEndpointAsync + /// + /// The serviceCacheManager + /// The cacheId + /// The public static async Task> GetCacheEndpointAsync(this IServiceCacheManager serviceCacheManager, string cacheId) { var caches = await serviceCacheManager.GetCachesAsync(); return caches.Where(p => p.CacheDescriptor.Id == cacheId).Select(p => p.CacheEndpoint).FirstOrDefault(); } - - public static async Task GetCacheEndpointAsync(this IServiceCacheManager serviceCacheManager, string cacheId,string endpoint) + /// + /// The GetCacheEndpointAsync + /// + /// The serviceCacheManager + /// The cacheId + /// The endpoint + /// The + public static async Task GetCacheEndpointAsync(this IServiceCacheManager serviceCacheManager, string cacheId, string endpoint) { var caches = await serviceCacheManager.GetCachesAsync(); - var cache= caches.Where(p => p.CacheDescriptor.Id == cacheId).Select(p => p.CacheEndpoint).FirstOrDefault(); + var cache = caches.Where(p => p.CacheDescriptor.Id == cacheId).Select(p => p.CacheEndpoint).FirstOrDefault(); return cache.Where(p => p.ToString() == endpoint).FirstOrDefault(); } - } -} + #endregion 方法 + } +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Cache/Implementation/ServiceCacheManagerBase.cs b/src/Surging.Core/Surging.Core.CPlatform/Cache/Implementation/ServiceCacheManagerBase.cs index 3ef1134a3..414345a92 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Cache/Implementation/ServiceCacheManagerBase.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Cache/Implementation/ServiceCacheManagerBase.cs @@ -7,30 +7,67 @@ namespace Surging.Core.CPlatform.Cache.Implementation { - public class ServiceCacheEventArgs - { - public ServiceCacheEventArgs(ServiceCache cache) - { - Cache = cache; - } - - public ServiceCache Cache { get; private set; } - } - + /// + /// Defines the + /// public class ServiceCacheChangedEventArgs : ServiceCacheEventArgs { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The cache + /// The oldCache public ServiceCacheChangedEventArgs(ServiceCache cache, ServiceCache oldCache) : base(cache) { OldCache = oldCache; } - + + #endregion 构造函数 + + #region 属性 + + /// + /// Gets or sets the OldCache + /// public ServiceCache OldCache { get; set; } + + #endregion 属性 + } + + /// + /// Defines the + /// + public class ServiceCacheEventArgs + { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The cache + public ServiceCacheEventArgs(ServiceCache cache) + { + Cache = cache; + } + + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the Cache + /// + public ServiceCache Cache { get; private set; } + + #endregion 属性 } public abstract class ServiceCacheManagerBase : IServiceCacheManager { private readonly ISerializer _serializer; - private EventHandler _created; + private EventHandler _created; private EventHandler _removed; private EventHandler _changed; @@ -38,19 +75,19 @@ protected ServiceCacheManagerBase(ISerializer serializer) { _serializer = serializer; } - + public event EventHandler Created { add { _created += value; } remove { _created -= value; } } - + public event EventHandler Removed { add { _removed += value; } remove { _removed -= value; } } - + public event EventHandler Changed { add { _changed += value; } @@ -58,10 +95,10 @@ public event EventHandler Changed } public abstract Task ClearAsync(); + public abstract Task> GetCachesAsync(); - - public abstract Task RemveAddressAsync(IEnumerable endpoints); + public abstract Task RemveAddressAsync(IEnumerable endpoints); public virtual Task SetCachesAsync(IEnumerable caches) { @@ -75,7 +112,7 @@ public virtual Task SetCachesAsync(IEnumerable caches) Type = address.GetType().FullName, Value = _serializer.Serialize(address) }) ?? Enumerable.Empty(), - CacheDescriptor = cache.CacheDescriptor + CacheDescriptor = cache.CacheDescriptor }); return SetCachesAsync(descriptors); } @@ -109,4 +146,4 @@ protected void OnRemoved(params ServiceCacheEventArgs[] args) _removed(this, arg); } } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Cache/ServiceCache.cs b/src/Surging.Core/Surging.Core.CPlatform/Cache/ServiceCache.cs index df496091e..18174fc13 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Cache/ServiceCache.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Cache/ServiceCache.cs @@ -5,22 +5,34 @@ namespace Surging.Core.CPlatform.Cache { + /// + /// Defines the + /// public class ServiceCache { + #region 属性 + /// - /// 服务可用地址。 - /// - public IEnumerable CacheEndpoint { get; set; } - /// + /// Gets or sets the CacheDescriptor /// 服务描述符。 /// public CacheDescriptor CacheDescriptor { get; set; } - #region Equality members + /// + /// Gets or sets the CacheEndpoint + /// 服务可用地址。 + /// + public IEnumerable CacheEndpoint { get; set; } - /// Determines whether the specified object is equal to the current object. - /// true if the specified object is equal to the current object; otherwise, false. + #endregion 属性 + + #region 方法 + + /// + /// The Equals + /// /// The object to compare with the current object. + /// true if the specified object is equal to the current object; otherwise, false. public override bool Equals(object obj) { var model = obj as ServiceCache; @@ -36,13 +48,17 @@ public override bool Equals(object obj) return model.CacheEndpoint.Count() == CacheEndpoint.Count() && model.CacheEndpoint.All(addressModel => CacheEndpoint.Contains(addressModel)); } - /// Serves as the default hash function. + /// + /// The GetHashCode + /// /// A hash code for the current object. public override int GetHashCode() { return ToString().GetHashCode(); } + #endregion 方法 + public static bool operator ==(ServiceCache model1, ServiceCache model2) { return Equals(model1, model2); @@ -52,7 +68,5 @@ public override int GetHashCode() { return !Equals(model1, model2); } - - #endregion Equality members } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Cache/ServiceCacheDescriptor.cs b/src/Surging.Core/Surging.Core.CPlatform/Cache/ServiceCacheDescriptor.cs index c604b0513..6ad46ed8c 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Cache/ServiceCacheDescriptor.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Cache/ServiceCacheDescriptor.cs @@ -10,16 +10,24 @@ namespace Surging.Core.CPlatform.Cache /// public class CacheEndpointDescriptor { + #region 属性 + /// + /// Gets or sets the Type /// 地址类型。 /// public string Type { get; set; } /// + /// Gets or sets the Value /// 地址值。 /// public string Value { get; set; } + #endregion 属性 + + #region 方法 + /// /// 创建一个描述符。 /// @@ -35,17 +43,29 @@ public class CacheEndpointDescriptor Value = serializer.Serialize(address) }; } + + #endregion 方法 } + + /// + /// Defines the + /// public class ServiceCacheDescriptor { + #region 属性 + /// + /// Gets or sets the AddressDescriptors /// 服务地址描述符集合。 /// public IEnumerable AddressDescriptors { get; set; } /// + /// Gets or sets the CacheDescriptor /// 缓存描述符。 /// public CacheDescriptor CacheDescriptor { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/CommunicationProtocol.cs b/src/Surging.Core/Surging.Core.CPlatform/CommunicationProtocol.cs index 260b72d96..c446e9120 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/CommunicationProtocol.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/CommunicationProtocol.cs @@ -4,13 +4,43 @@ namespace Surging.Core.CPlatform { - public enum CommunicationProtocol + #region 枚举 + + /// + /// Defines the CommunicationProtocol + /// + public enum CommunicationProtocol { + /// + /// Defines the None + /// None, + + /// + /// Defines the Tcp + /// Tcp, + + /// + /// Defines the Http + /// Http, + + /// + /// Defines the WS + /// WS, + + /// + /// Defines the Mqtt + /// Mqtt, + + /// + /// Defines the Dns + /// Dns } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Configurations/CPlatformConfigurationExtensions.cs b/src/Surging.Core/Surging.Core.CPlatform/Configurations/CPlatformConfigurationExtensions.cs index 0442b6abd..244991689 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Configurations/CPlatformConfigurationExtensions.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Configurations/CPlatformConfigurationExtensions.cs @@ -8,29 +8,24 @@ namespace Surging.Core.CPlatform.Configurations { + /// + /// Defines the + /// public static class CacheConfigurationExtensionsstatic { - public static IConfigurationBuilder AddCPlatformFile(this IConfigurationBuilder builder, string path) - { - return AddCPlatformFile(builder, provider: null, path: path,basePath: null, optional: false, reloadOnChange: false); - } + #region 方法 - public static IConfigurationBuilder AddCPlatformFile(this IConfigurationBuilder builder, string path, bool optional) - { - return AddCPlatformFile(builder, provider: null, path: path,basePath: null, optional: optional, reloadOnChange: false); - } - - public static IConfigurationBuilder AddCPlatformFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange) - { - return AddCPlatformFile(builder, provider: null, path: path, basePath:null, optional: optional, reloadOnChange: reloadOnChange); - } - - public static IConfigurationBuilder AddCPlatformFile(this IConfigurationBuilder builder, string path, string basePath, bool optional, bool reloadOnChange) - { - return AddCPlatformFile(builder, provider: null, path: path, basePath:basePath, optional: optional, reloadOnChange: reloadOnChange); - } - - public static IConfigurationBuilder AddCPlatformFile(this IConfigurationBuilder builder, IFileProvider provider, string path,string basePath, bool optional, bool reloadOnChange) + /// + /// The AddCPlatformFile + /// + /// The builder + /// The provider + /// The path + /// The basePath + /// The optional + /// The reloadOnChange + /// The + public static IConfigurationBuilder AddCPlatformFile(this IConfigurationBuilder builder, IFileProvider provider, string path, string basePath, bool optional, bool reloadOnChange) { Check.NotNull(builder, "builder"); Check.CheckCondition(() => string.IsNullOrEmpty(path), "path"); @@ -41,7 +36,7 @@ public static IConfigurationBuilder AddCPlatformFile(this IConfigurationBuilder { provider = new PhysicalFileProvider(Path.GetDirectoryName(path)); path = Path.GetFileName(path); - } + } var source = new CPlatformConfigurationSource { FileProvider = provider, @@ -49,7 +44,7 @@ public static IConfigurationBuilder AddCPlatformFile(this IConfigurationBuilder Optional = optional, ReloadOnChange = reloadOnChange }; - + builder.Add(source); if (!string.IsNullOrEmpty(basePath)) builder.SetBasePath(basePath); @@ -61,5 +56,57 @@ public static IConfigurationBuilder AddCPlatformFile(this IConfigurationBuilder } return builder; } + + /// + /// The AddCPlatformFile + /// + /// The builder + /// The path + /// The + public static IConfigurationBuilder AddCPlatformFile(this IConfigurationBuilder builder, string path) + { + return AddCPlatformFile(builder, provider: null, path: path, basePath: null, optional: false, reloadOnChange: false); + } + + /// + /// The AddCPlatformFile + /// + /// The builder + /// The path + /// The optional + /// The + public static IConfigurationBuilder AddCPlatformFile(this IConfigurationBuilder builder, string path, bool optional) + { + return AddCPlatformFile(builder, provider: null, path: path, basePath: null, optional: optional, reloadOnChange: false); + } + + /// + /// The AddCPlatformFile + /// + /// The builder + /// The path + /// The optional + /// The reloadOnChange + /// The + public static IConfigurationBuilder AddCPlatformFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange) + { + return AddCPlatformFile(builder, provider: null, path: path, basePath: null, optional: optional, reloadOnChange: reloadOnChange); + } + + /// + /// The AddCPlatformFile + /// + /// The builder + /// The path + /// The basePath + /// The optional + /// The reloadOnChange + /// The + public static IConfigurationBuilder AddCPlatformFile(this IConfigurationBuilder builder, string path, string basePath, bool optional, bool reloadOnChange) + { + return AddCPlatformFile(builder, provider: null, path: path, basePath: basePath, optional: optional, reloadOnChange: reloadOnChange); + } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Configurations/CPlatformConfigurationProvider.cs b/src/Surging.Core/Surging.Core.CPlatform/Configurations/CPlatformConfigurationProvider.cs index a45528373..01e629dd1 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Configurations/CPlatformConfigurationProvider.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Configurations/CPlatformConfigurationProvider.cs @@ -4,15 +4,35 @@ namespace Surging.Core.CPlatform.Configurations { + /// + /// Defines the + /// public class CPlatformConfigurationProvider : FileConfigurationProvider { + #region 构造函数 - public CPlatformConfigurationProvider(CPlatformConfigurationSource source) : base(source) { } + /// + /// Initializes a new instance of the class. + /// + /// The source + public CPlatformConfigurationProvider(CPlatformConfigurationSource source) : base(source) + { + } + + #endregion 构造函数 + #region 方法 + + /// + /// The Load + /// + /// The stream public override void Load(Stream stream) { var parser = new JsonConfigurationParser(); this.Data = parser.Parse(stream, null); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Configurations/CPlatformConfigurationSource.cs b/src/Surging.Core/Surging.Core.CPlatform/Configurations/CPlatformConfigurationSource.cs index 4a6413edc..f310b11e2 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Configurations/CPlatformConfigurationSource.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Configurations/CPlatformConfigurationSource.cs @@ -2,14 +2,33 @@ namespace Surging.Core.CPlatform.Configurations { + /// + /// Defines the + /// public class CPlatformConfigurationSource : FileConfigurationSource { + #region 属性 + + /// + /// Gets or sets the ConfigurationKeyPrefix + /// public string ConfigurationKeyPrefix { get; set; } + #endregion 属性 + + #region 方法 + + /// + /// The Build + /// + /// The builder + /// The public override IConfigurationProvider Build(IConfigurationBuilder builder) { FileProvider = FileProvider ?? builder.GetFileProvider(); return new CPlatformConfigurationProvider(this); } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Configurations/IConfigurationWatchManager.cs b/src/Surging.Core/Surging.Core.CPlatform/Configurations/IConfigurationWatchManager.cs index 74c99ffcd..c9687c753 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Configurations/IConfigurationWatchManager.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Configurations/IConfigurationWatchManager.cs @@ -2,8 +2,23 @@ namespace Surging.Core.CPlatform.Configurations { - public interface IConfigurationWatchManager + #region 接口 + + /// + /// Defines the + /// + public interface IConfigurationWatchManager { + #region 方法 + + /// + /// The Register + /// + /// The watch void Register(ConfigurationWatch watch); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Configurations/ModulePackage.cs b/src/Surging.Core/Surging.Core.CPlatform/Configurations/ModulePackage.cs index 13b2414f9..24e040494 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Configurations/ModulePackage.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Configurations/ModulePackage.cs @@ -1,8 +1,22 @@ namespace Surging.Core.CPlatform.Configurations { + /// + /// Defines the + /// public class ModulePackage { + #region 属性 + + /// + /// Gets or sets the TypeName + /// public string TypeName { get; set; } + + /// + /// Gets or sets the Using + /// public string Using { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Configurations/ProtocolPortOptions.cs b/src/Surging.Core/Surging.Core.CPlatform/Configurations/ProtocolPortOptions.cs index df71a3aaf..79917cdde 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Configurations/ProtocolPortOptions.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Configurations/ProtocolPortOptions.cs @@ -1,11 +1,27 @@ namespace Surging.Core.CPlatform.Configurations { + /// + /// Defines the + /// public class ProtocolPortOptions { - public int MQTTPort { get; set; } - + #region 属性 + + /// + /// Gets or sets the HttpPort + /// public int? HttpPort { get; set; } + /// + /// Gets or sets the MQTTPort + /// + public int MQTTPort { get; set; } + + /// + /// Gets or sets the WSPort + /// public int WSPort { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Configurations/Remote/IConfigurationParser.cs b/src/Surging.Core/Surging.Core.CPlatform/Configurations/Remote/IConfigurationParser.cs index b0fce69da..18cf3f495 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Configurations/Remote/IConfigurationParser.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Configurations/Remote/IConfigurationParser.cs @@ -3,8 +3,25 @@ namespace Surging.Core.CPlatform.Configurations.Remote { + #region 接口 + + /// + /// Defines the + /// public interface IConfigurationParser { + #region 方法 + + /// + /// The Parse + /// + /// The input + /// The initialContext + /// The IDictionary Parse(Stream input, string initialContext); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Configurations/Remote/JsonConfigurationParser.cs b/src/Surging.Core/Surging.Core.CPlatform/Configurations/Remote/JsonConfigurationParser.cs index 6347a4b21..630bc27e3 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Configurations/Remote/JsonConfigurationParser.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Configurations/Remote/JsonConfigurationParser.cs @@ -10,14 +10,43 @@ namespace Surging.Core.CPlatform.Configurations.Remote { - public class JsonConfigurationParser : IConfigurationParser + /// + /// Defines the + /// + public class JsonConfigurationParser : IConfigurationParser { - private readonly IDictionary _data = new SortedDictionary(StringComparer.OrdinalIgnoreCase); + #region 字段 + + /// + /// Defines the _context + /// private readonly Stack _context = new Stack(); + + /// + /// Defines the _data + /// + private readonly IDictionary _data = new SortedDictionary(StringComparer.OrdinalIgnoreCase); + + /// + /// Defines the _currentPath + /// private string _currentPath; + /// + /// Defines the _reader + /// private JsonTextReader _reader; - + + #endregion 字段 + + #region 方法 + + /// + /// The Parse + /// + /// The input + /// The initialContext + /// The public IDictionary Parse(Stream input, string initialContext) { try @@ -53,9 +82,100 @@ public IDictionary Parse(Stream input, string initialContext) errorLine), e); } + } + + /// + /// The GetParameters + /// + /// The text + /// The + private static List GetParameters(string text) + { + var matchVale = new List(); + string Reg = @"(?<=\${)[^\${}]*(?=})"; + string key = string.Empty; + foreach (Match m in Regex.Matches(text, Reg)) + { + matchVale.Add(m.Value); + } + return matchVale; + } + /// + /// The ReadLines + /// + /// The streamReader + /// The + private static IEnumerable ReadLines(StreamReader streamReader) + { + string line; + do + { + line = streamReader.ReadLine(); + yield return line; + } while (line != null); } + /// + /// The RetrieveErrorContext + /// + /// The e + /// The fileContent + /// The + private static string RetrieveErrorContext(JsonReaderException e, IEnumerable fileContent) + { + string errorLine; + if (e.LineNumber >= 2) + { + var errorContext = fileContent.Skip(e.LineNumber - 2).Take(2).ToList(); + errorLine = errorContext[0].Trim() + Environment.NewLine + errorContext[1].Trim(); + } + else + { + var possibleLineContent = fileContent.Skip(e.LineNumber - 1).FirstOrDefault(); + errorLine = possibleLineContent ?? string.Empty; + } + + return errorLine; + } + + /// + /// The EnterContext + /// + /// The context + private void EnterContext(string context) + { + _context.Push(context); + _currentPath = ConfigurationPath.Combine(_context.Reverse()); + } + + /// + /// The ExitContext + /// + private void ExitContext() + { + _context.Pop(); + _currentPath = ConfigurationPath.Combine(_context.Reverse()); + } + + /// + /// The VisitArray + /// + /// The array + private void VisitArray(JArray array) + { + for (int index = 0; index < array.Count; index++) + { + EnterContext(index.ToString()); + VisitToken(array[index]); + ExitContext(); + } + } + + /// + /// The VisitJObject + /// + /// The jObject private void VisitJObject(JObject jObject) { foreach (var property in jObject.Properties()) @@ -66,11 +186,30 @@ private void VisitJObject(JObject jObject) } } + /// + /// The VisitPrimitive + /// + /// The data + private void VisitPrimitive(JToken data) + { + var key = _currentPath; + Check.CheckCondition(() => _data.ContainsKey(key), "key"); + _data[key] = EnvironmentHelper.GetEnvironmentVariable(data.ToString()); + } + + /// + /// The VisitProperty + /// + /// The property private void VisitProperty(JProperty property) { VisitToken(property.Value); } + /// + /// The VisitToken + /// + /// The token private void VisitToken(JToken token) { switch (token.Type) @@ -103,72 +242,6 @@ private void VisitToken(JToken token) } } - private void VisitArray(JArray array) - { - for (int index = 0; index < array.Count; index++) - { - EnterContext(index.ToString()); - VisitToken(array[index]); - ExitContext(); - } - } - - private void VisitPrimitive(JToken data) - { - var key = _currentPath; - Check.CheckCondition(() => _data.ContainsKey(key), "key"); - _data[key] = EnvironmentHelper.GetEnvironmentVariable(data.ToString()); - } - - private void EnterContext(string context) - { - _context.Push(context); - _currentPath = ConfigurationPath.Combine(_context.Reverse()); - } - - private void ExitContext() - { - _context.Pop(); - _currentPath = ConfigurationPath.Combine(_context.Reverse()); - } - - private static IEnumerable ReadLines(StreamReader streamReader) - { - string line; - do - { - line = streamReader.ReadLine(); - yield return line; - } while (line != null); - } - - private static string RetrieveErrorContext(JsonReaderException e, IEnumerable fileContent) - { - string errorLine; - if (e.LineNumber >= 2) - { - var errorContext = fileContent.Skip(e.LineNumber - 2).Take(2).ToList(); - errorLine = errorContext[0].Trim() + Environment.NewLine + errorContext[1].Trim(); - } - else - { - var possibleLineContent = fileContent.Skip(e.LineNumber - 1).FirstOrDefault(); - errorLine = possibleLineContent ?? string.Empty; - } - - return errorLine; - } - - private static List GetParameters(string text) - { - var matchVale = new List(); - string Reg = @"(?<=\${)[^\${}]*(?=})"; - string key = string.Empty; - foreach (Match m in Regex.Matches(text, Reg)) - { - matchVale.Add(m.Value); - } - return matchVale; - } + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Configurations/Remote/RemoteConfigurationEvents.cs b/src/Surging.Core/Surging.Core.CPlatform/Configurations/Remote/RemoteConfigurationEvents.cs index eb78fa0d7..8fdd5b49b 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Configurations/Remote/RemoteConfigurationEvents.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Configurations/Remote/RemoteConfigurationEvents.cs @@ -5,14 +5,40 @@ namespace Surging.Core.CPlatform.Configurations.Remote { - public class RemoteConfigurationEvents + /// + /// Defines the + /// + public class RemoteConfigurationEvents { - public Action OnSendingRequest { get; set; } = msg => { }; + #region 属性 + /// + /// Gets or sets the OnDataParsed + /// public Func, IDictionary> OnDataParsed { get; set; } = data => data; - - public virtual void SendingRequest(HttpRequestMessage msg) => OnSendingRequest(msg); + /// + /// Gets or sets the OnSendingRequest + /// + public Action OnSendingRequest { get; set; } = msg => { }; + + #endregion 属性 + + #region 方法 + + /// + /// The DataParsed + /// + /// The data + /// The public virtual IDictionary DataParsed(IDictionary data) => OnDataParsed(data); + + /// + /// The SendingRequest + /// + /// The msg + public virtual void SendingRequest(HttpRequestMessage msg) => OnSendingRequest(msg); + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Configurations/Remote/RemoteConfigurationExtensions.cs b/src/Surging.Core/Surging.Core.CPlatform/Configurations/Remote/RemoteConfigurationExtensions.cs index 0a4913640..188f2f2df 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Configurations/Remote/RemoteConfigurationExtensions.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Configurations/Remote/RemoteConfigurationExtensions.cs @@ -10,6 +10,30 @@ namespace Surging.Core.CPlatform.Configurations.Remote /// public static class RemoteConfigurationExtensions { + #region 方法 + + /// + /// The AddRemoteSource + /// + /// The builder + /// The source + /// The + public static IConfigurationBuilder AddRemoteSource(this IConfigurationBuilder builder, RemoteConfigurationSource source) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + builder.Add(source); + return builder; + } + /// /// Adds a remote configuration source to . /// @@ -54,7 +78,7 @@ public static IConfigurationBuilder AddRemoteSource(this IConfigurationBuilder b /// /// The to add to. /// The remote uri to - /// Whether the remote configuration source is optional. + /// Whether the remote configuration source is optional. /// Events that get add /// The . public static IConfigurationBuilder AddRemoteSource(this IConfigurationBuilder builder, Uri configurationUri, bool optional, RemoteConfigurationEvents events) @@ -83,21 +107,7 @@ public static IConfigurationBuilder AddRemoteSource(this IConfigurationBuilder b return builder.AddRemoteSource(source); } - - public static IConfigurationBuilder AddRemoteSource(this IConfigurationBuilder builder, RemoteConfigurationSource source) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - if (source == null) - { - throw new ArgumentNullException(nameof(source)); - } - - builder.Add(source); - return builder; - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Configurations/Remote/RemoteConfigurationProvider.cs b/src/Surging.Core/Surging.Core.CPlatform/Configurations/Remote/RemoteConfigurationProvider.cs index 34cf71104..beb6de372 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Configurations/Remote/RemoteConfigurationProvider.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Configurations/Remote/RemoteConfigurationProvider.cs @@ -8,30 +8,59 @@ namespace Surging.Core.CPlatform.Configurations.Remote { - class RemoteConfigurationProvider : ConfigurationProvider + /// + /// Defines the + /// + internal class RemoteConfigurationProvider : ConfigurationProvider { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The source public RemoteConfigurationProvider(RemoteConfigurationSource source) { Check.NotNull(source, "source"); if (!string.IsNullOrEmpty(source.ConfigurationKeyPrefix)) { Check.CheckCondition(() => source.ConfigurationKeyPrefix.Trim().StartsWith(":"), CPlatformResource.InvalidStartCharacter, "source.ConfigurationKeyPrefix", ":"); - Check.CheckCondition(() => source.ConfigurationKeyPrefix.Trim().EndsWith(":"), CPlatformResource.InvalidEndCharacter, "source.ConfigurationKeyPrefix",":"); + Check.CheckCondition(() => source.ConfigurationKeyPrefix.Trim().EndsWith(":"), CPlatformResource.InvalidEndCharacter, "source.ConfigurationKeyPrefix", ":"); } Source = source; Backchannel = new HttpClient(source.BackchannelHttpHandler ?? new HttpClientHandler()); Backchannel.DefaultRequestHeaders.UserAgent.ParseAdd("获取CacheConfiugration信息"); Backchannel.Timeout = source.BackchannelTimeout; - Backchannel.MaxResponseContentBufferSize = 1024 * 1024 * 10; + Backchannel.MaxResponseContentBufferSize = 1024 * 1024 * 10; Parser = source.Parser ?? new JsonConfigurationParser(); } - public RemoteConfigurationSource Source { get; } + #endregion 构造函数 - public IConfigurationParser Parser { get; } + #region 属性 + /// + /// Gets the Backchannel + /// public HttpClient Backchannel { get; } - + + /// + /// Gets the Parser + /// + public IConfigurationParser Parser { get; } + + /// + /// Gets the Source + /// + public RemoteConfigurationSource Source { get; } + + #endregion 属性 + + #region 方法 + + /// + /// The Load + /// public override void Load() { var requestMessage = new HttpRequestMessage(HttpMethod.Get, Source.ConfigurationUri); @@ -68,5 +97,7 @@ public override void Load() } } } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Configurations/Remote/RemoteConfigurationSource.cs b/src/Surging.Core/Surging.Core.CPlatform/Configurations/Remote/RemoteConfigurationSource.cs index 14df78fbc..7fafa9945 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Configurations/Remote/RemoteConfigurationSource.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Configurations/Remote/RemoteConfigurationSource.cs @@ -4,54 +4,75 @@ namespace Surging.Core.CPlatform.Configurations.Remote { - public class RemoteConfigurationSource : IConfigurationSource + /// + /// Defines the + /// + public class RemoteConfigurationSource : IConfigurationSource { + #region 属性 + /// - /// The uri to call to fetch + /// Gets or sets the BackchannelHttpHandler + /// The HttpMessageHandler used to communicate with remote configuration provider. /// - public Uri ConfigurationUri { get; set; } + public HttpMessageHandler BackchannelHttpHandler { get; set; } /// - /// Determines if the remote source is optional + /// Gets or sets the BackchannelTimeout + /// Gets or sets timeout value in milliseconds for back channel communications with the remote identity provider. /// - public bool Optional { get; set; } + public TimeSpan BackchannelTimeout { get; set; } = TimeSpan.FromSeconds(60); /// - /// The HttpMessageHandler used to communicate with remote configuration provider. + /// Gets or sets the ConfigurationKeyPrefix + /// If provided, keys loaded from endpoint will be prefixed with the provided value /// - public HttpMessageHandler BackchannelHttpHandler { get; set; } + public string ConfigurationKeyPrefix { get; set; } /// - /// Gets or sets timeout value in milliseconds for back channel communications with the remote identity provider. + /// Gets or sets the ConfigurationUri + /// The uri to call to fetch /// - /// - /// The back channel timeout. - /// - public TimeSpan BackchannelTimeout { get; set; } = TimeSpan.FromSeconds(60); + public Uri ConfigurationUri { get; set; } /// - /// Parser for parsing the returned data into the required configuration source + /// Gets or sets the Events + /// Events providing hooks into the remote call /// - public IConfigurationParser Parser { get; set; } + public RemoteConfigurationEvents Events { get; set; } = new RemoteConfigurationEvents(); /// + /// Gets or sets the MediaType /// The accept header used to create a MediaTypeWithQualityHeaderValue /// public string MediaType { get; set; } = "application/json"; /// - /// Events providing hooks into the remote call + /// Gets or sets a value indicating whether Optional + /// Determines if the remote source is optional /// - public RemoteConfigurationEvents Events { get; set; } = new RemoteConfigurationEvents(); + public bool Optional { get; set; } /// - /// If provided, keys loaded from endpoint will be prefixed with the provided value + /// Gets or sets the Parser + /// Parser for parsing the returned data into the required configuration source /// - public string ConfigurationKeyPrefix { get; set; } + public IConfigurationParser Parser { get; set; } + + #endregion 属性 + + #region 方法 + /// + /// The Build + /// + /// The builder + /// The public IConfigurationProvider Build(IConfigurationBuilder builder) { return new RemoteConfigurationProvider(this); } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Configurations/SurgingServerOptions.cs b/src/Surging.Core/Surging.Core.CPlatform/Configurations/SurgingServerOptions.cs index 177fd5750..a83622155 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Configurations/SurgingServerOptions.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Configurations/SurgingServerOptions.cs @@ -5,47 +5,118 @@ namespace Surging.Core.CPlatform.Configurations { - public partial class SurgingServerOptions: ServiceCommand + /// + /// Defines the + /// + public partial class SurgingServerOptions : ServiceCommand { - public string Ip { get; set; } - - public string MappingIP { get; set; } + #region 属性 - public int MappingPort { get; set; } + /// + /// Gets or sets the DisconnTimeInterval + /// + public int DisconnTimeInterval { get; set; } = 60; - public string WanIp { get; set; } + /// + /// Gets or sets a value indicating whether EnableRouteWatch + /// + public bool EnableRouteWatch { get; set; } - public bool IsModulePerLifetimeScope { get; set; } + /// + /// Gets or sets the Ip + /// + public string Ip { get; set; } - public double WatchInterval { get; set; } = 20d; + /// + /// Gets or sets the IpEndpoint + /// + public IPEndPoint IpEndpoint { get; set; } - public int DisconnTimeInterval { get; set; } = 60; + /// + /// Gets or sets a value indicating whether IsModulePerLifetimeScope + /// + public bool IsModulePerLifetimeScope { get; set; } + /// + /// Gets or sets a value indicating whether Libuv + /// public bool Libuv { get; set; } = false; - public int SoBacklog { get; set; } = 8192; + /// + /// Gets or sets the MappingIP + /// + public string MappingIP { get; set; } - public bool EnableRouteWatch { get; set; } + /// + /// Gets or sets the MappingPort + /// + public int MappingPort { get; set; } - public IPEndPoint IpEndpoint { get; set; } + /// + /// Gets or sets the NotRelatedAssemblyFiles + /// + public string NotRelatedAssemblyFiles { get; set; } + /// + /// Gets or sets the Packages + /// public List Packages { get; set; } = new List(); - public CommunicationProtocol Protocol { get; set; } - public string RootPath { get; set; } + /// + /// Gets or sets the Port + /// + public int Port { get; set; } - public string WebRootPath { get; set; } = AppContext.BaseDirectory; + /// + /// Gets or sets the Ports + /// + public ProtocolPortOptions Ports { get; set; } = new ProtocolPortOptions(); - public int Port { get; set; } + /// + /// Gets or sets the Protocol + /// + public CommunicationProtocol Protocol { get; set; } + + /// + /// Gets or sets the RelatedAssemblyFiles + /// + public string RelatedAssemblyFiles { get; set; } = ""; + /// + /// Gets or sets a value indicating whether ReloadOnChange + /// public bool ReloadOnChange { get; set; } = false; - public ProtocolPortOptions Ports { get; set; } = new ProtocolPortOptions(); + /// + /// Gets or sets the RootPath + /// + public string RootPath { get; set; } + + /// + /// Gets or sets the SoBacklog + /// + public int SoBacklog { get; set; } = 8192; + /// + /// Gets or sets the Token + /// public string Token { get; set; } = "True"; - public string NotRelatedAssemblyFiles { get; set; } + /// + /// Gets or sets the WanIp + /// + public string WanIp { get; set; } - public string RelatedAssemblyFiles { get; set; } = ""; + /// + /// Gets or sets the WatchInterval + /// + public double WatchInterval { get; set; } = 20d; + + /// + /// Gets or sets the WebRootPath + /// + public string WebRootPath { get; set; } = AppContext.BaseDirectory; + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Configurations/Watch/ConfigurationWatch.cs b/src/Surging.Core/Surging.Core.CPlatform/Configurations/Watch/ConfigurationWatch.cs index 02f8f7b62..0e9e3e80f 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Configurations/Watch/ConfigurationWatch.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Configurations/Watch/ConfigurationWatch.cs @@ -5,12 +5,30 @@ namespace Surging.Core.CPlatform.Configurations.Watch { + /// + /// Defines the + /// public abstract class ConfigurationWatch { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// protected ConfigurationWatch() { } + #endregion 构造函数 + + #region 方法 + + /// + /// The Process + /// + /// The public abstract Task Process(); + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Configurations/Watch/ConfigurationWatchManager.cs b/src/Surging.Core/Surging.Core.CPlatform/Configurations/Watch/ConfigurationWatchManager.cs index c0eecd38a..bbda838cf 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Configurations/Watch/ConfigurationWatchManager.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Configurations/Watch/ConfigurationWatchManager.cs @@ -7,13 +7,37 @@ namespace Surging.Core.CPlatform.Configurations.Watch { - public class ConfigurationWatchManager: IConfigurationWatchManager + /// + /// Defines the + /// + public class ConfigurationWatchManager : IConfigurationWatchManager { - internal HashSet dataWatches = - new HashSet(); - private readonly Timer _timer; + #region 字段 + + /// + /// Defines the _logger + /// private readonly ILogger _logger; + /// + /// Defines the _timer + /// + private readonly Timer _timer; + + /// + /// Defines the dataWatches + /// + internal HashSet dataWatches = + new HashSet(); + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The logger public ConfigurationWatchManager(ILogger logger) { _logger = logger; @@ -24,7 +48,14 @@ public ConfigurationWatchManager(ILogger logger) }, null, timeSpan, timeSpan); } - public HashSet DataWatches + #endregion 构造函数 + + #region 属性 + + /// + /// Gets or sets the DataWatches + /// + public HashSet DataWatches { get { @@ -36,33 +67,47 @@ public HashSet DataWatches } } + #endregion 属性 + + #region 方法 + + /// + /// The Register + /// + /// The watch public void Register(ConfigurationWatch watch) { lock (dataWatches) { - if( !dataWatches.Contains(watch)) - dataWatches.Add(watch); + if (!dataWatches.Contains(watch)) + dataWatches.Add(watch); } } + /// + /// The Watching + /// + /// The private async Task Watching() - { + { foreach (var watch in dataWatches) { try { - var task= watch.Process(); + var task = watch.Process(); if (!task.IsCompletedSuccessfully) await task; else task.GetAwaiter().GetResult(); } - catch(Exception ex) - { + catch (Exception ex) + { if (_logger.IsEnabled(LogLevel.Error)) _logger.LogError($"message:{ex.Message},Source:{ex.Source},Trace:{ex.StackTrace}"); } } } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/ContainerBuilderExtensions.cs b/src/Surging.Core/Surging.Core.CPlatform/ContainerBuilderExtensions.cs index d69804165..4bf685891 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/ContainerBuilderExtensions.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/ContainerBuilderExtensions.cs @@ -50,259 +50,423 @@ namespace Surging.Core.CPlatform { + #region 接口 + /// /// 服务构建者。 /// public interface IServiceBuilder { + #region 属性 + /// + /// Gets or sets the Services /// 服务集合。 /// ContainerBuilder Services { get; set; } + + #endregion 属性 } + #endregion 接口 + /// - /// 默认服务构建者。 + /// Defines the /// - internal sealed class ServiceBuilder : IServiceBuilder + public static class ContainerBuilderExtensions { - public ServiceBuilder(ContainerBuilder services) - { - if (services == null) - throw new ArgumentNullException(nameof(services)); - Services = services; - } - - #region Implementation of IServiceBuilder + #region 字段 /// - /// 服务集合。 + /// Defines the _modules /// - public ContainerBuilder Services { get; set; } - - #endregion Implementation of IServiceBuilder - } - - public static class ContainerBuilderExtensions - { - private static List _referenceAssembly = new List(); private static List _modules = new List(); /// - /// 添加Json序列化支持。 + /// Defines the _referenceAssembly /// - /// 服务构建者。 - /// 服务构建者。 - public static IServiceBuilder AddJsonSerialization(this IServiceBuilder builder) - { - var services = builder.Services; - services.RegisterType(typeof(JsonSerializer)).As(typeof(ISerializer)).SingleInstance(); - services.RegisterType(typeof(StringByteArraySerializer)).As(typeof(ISerializer)).SingleInstance(); - services.RegisterType(typeof(StringObjectSerializer)).As(typeof(ISerializer)).SingleInstance(); - return builder; - } - #region RouteManager + private static List _referenceAssembly = new List(); + + #endregion 字段 + + #region 方法 /// - /// 设置服务路由管理者。 + /// 添加客户端运行时服务。 /// - /// 服务路由管理者实现。 /// 服务构建者。 /// 服务构建者。 - public static IServiceBuilder UseRouteManager(this IServiceBuilder builder) where T : class, IServiceRouteManager + public static IServiceBuilder AddClientRuntime(this IServiceBuilder builder) { - builder.Services.RegisterType(typeof(T)).As(typeof(IServiceRouteManager)).SingleInstance(); - return builder; + var services = builder.Services; + services.RegisterType(typeof(DefaultHealthCheckService)).As(typeof(IHealthCheckService)).SingleInstance(); + services.RegisterType(typeof(DefaultAddressResolver)).As(typeof(IAddressResolver)).SingleInstance(); + services.RegisterType(typeof(RemoteInvokeService)).As(typeof(IRemoteInvokeService)).SingleInstance(); + return builder.UseAddressSelector().AddRuntime().AddClusterSupport(); } /// - /// 设置服务路由管理者。 + /// 添加集群支持 /// - /// 服务构建者。 - /// 服务路由管理者实例工厂。 + /// 服务构建者 /// 服务构建者。 - public static IServiceBuilder UseRouteManager(this IServiceBuilder builder, Func factory) + public static IServiceBuilder AddClusterSupport(this IServiceBuilder builder) { - builder.Services.RegisterAdapter(factory).InstancePerLifetimeScope(); + var services = builder.Services; + services.RegisterType(typeof(ServiceCommandProvider)).As(typeof(IServiceCommandProvider)).SingleInstance(); + services.RegisterType(typeof(BreakeRemoteInvokeService)).As(typeof(IBreakeRemoteInvokeService)).SingleInstance(); + services.RegisterType(typeof(FailoverInjectionInvoker)).AsImplementedInterfaces() + .Named(StrategyType.Injection.ToString(), typeof(IClusterInvoker)).SingleInstance(); + services.RegisterType(typeof(FailoverHandoverInvoker)).AsImplementedInterfaces() + .Named(StrategyType.Failover.ToString(), typeof(IClusterInvoker)).SingleInstance(); return builder; } /// - /// 设置服务订阅管理者。 + /// Configuration Watch /// - /// 服务构建者。 - /// 服务订阅管理者实例工厂。 - /// 服务构建者。 - public static IServiceBuilder UseSubscribeManager(this IServiceBuilder builder, Func factory) + /// + /// 服务构建者 + public static IServiceBuilder AddConfigurationWatch(this IServiceBuilder builder) { - builder.Services.RegisterAdapter(factory).InstancePerLifetimeScope(); + var services = builder.Services; + services.RegisterType(typeof(ConfigurationWatchManager)).As(typeof(IConfigurationWatchManager)).SingleInstance(); return builder; } /// - /// 设置服务命令管理者。 + /// 添加核心服务。 /// - /// 服务构建者。 - /// 服务命令管理者实例工厂。 + /// 服务集合。 /// 服务构建者。 - public static IServiceBuilder UseCommandManager(this IServiceBuilder builder, Func factory) + public static IServiceBuilder AddCoreService(this ContainerBuilder services) { - builder.Services.RegisterAdapter(factory).InstancePerLifetimeScope(); - return builder; + Check.NotNull(services, "services"); + //注册服务ID生成实例 + services.RegisterType().As().SingleInstance(); + services.Register(p => new CPlatformContainer(p)); + //注册默认的类型转换 + services.RegisterType(typeof(DefaultTypeConvertibleProvider)).As(typeof(ITypeConvertibleProvider)).SingleInstance(); + //注册默认的类型转换服务 + services.RegisterType(typeof(DefaultTypeConvertibleService)).As(typeof(ITypeConvertibleService)).SingleInstance(); + //注册权限过滤 + services.RegisterType(typeof(AuthorizationAttribute)).As(typeof(IAuthorizationFilter)).SingleInstance(); + //注册基本过滤 + services.RegisterType(typeof(AuthorizationAttribute)).As(typeof(IFilter)).SingleInstance(); + //注册服务器路由接口 + services.RegisterType(typeof(DefaultServiceRouteProvider)).As(typeof(IServiceRouteProvider)).SingleInstance(); + //注册服务路由工厂 + services.RegisterType(typeof(DefaultServiceRouteFactory)).As(typeof(IServiceRouteFactory)).SingleInstance(); + //注册服务订阅工厂 + services.RegisterType(typeof(DefaultServiceSubscriberFactory)).As(typeof(IServiceSubscriberFactory)).SingleInstance(); + //注册服务token生成接口 + services.RegisterType(typeof(ServiceTokenGenerator)).As(typeof(IServiceTokenGenerator)).SingleInstance(); + //注册哈希一致性算法 + services.RegisterType(typeof(HashAlgorithm)).As(typeof(IHashAlgorithm)).SingleInstance(); + //注册组件生命周期接口 + services.RegisterType(typeof(ServiceEngineLifetime)).As(typeof(IServiceEngineLifetime)).SingleInstance(); + //注册服务心跳管理 + services.RegisterType(typeof(DefaultServiceHeartbeatManager)).As(typeof(IServiceHeartbeatManager)).SingleInstance(); + return new ServiceBuilder(services) + .AddJsonSerialization() + .UseJsonCodec(); } /// - /// 设置缓存管理者。 + /// The AddFilter /// - /// 服务构建者。 - /// 缓存管理者实例工厂。 - /// 服务构建者。 - public static IServiceBuilder UseCacheManager(this IServiceBuilder builder, Func factory) + /// The builder + /// The filter + /// The + public static IServiceBuilder AddFilter(this IServiceBuilder builder, IFilter filter) { - builder.Services.RegisterAdapter(factory).InstancePerLifetimeScope(); + var services = builder.Services; + services.Register(p => filter).As(typeof(IFilter)).SingleInstance(); + if (typeof(IExceptionFilter).IsAssignableFrom(filter.GetType())) + { + var exceptionFilter = filter as IExceptionFilter; + services.Register(p => exceptionFilter).As(typeof(IExceptionFilter)).SingleInstance(); + } + else if (typeof(IAuthorizationFilter).IsAssignableFrom(filter.GetType())) + { + var exceptionFilter = filter as IAuthorizationFilter; + services.Register(p => exceptionFilter).As(typeof(IAuthorizationFilter)).SingleInstance(); + } return builder; } /// - /// 设置服务路由管理者。 + /// 添加Json序列化支持。 /// /// 服务构建者。 - /// 服务路由管理者实例。 /// 服务构建者。 - public static IServiceBuilder UseRouteManager(this IServiceBuilder builder, IServiceRouteManager instance) + public static IServiceBuilder AddJsonSerialization(this IServiceBuilder builder) { - builder.Services.RegisterInstance(instance); + var services = builder.Services; + services.RegisterType(typeof(JsonSerializer)).As(typeof(ISerializer)).SingleInstance(); + services.RegisterType(typeof(StringByteArraySerializer)).As(typeof(ISerializer)).SingleInstance(); + services.RegisterType(typeof(StringObjectSerializer)).As(typeof(ISerializer)).SingleInstance(); return builder; } /// - /// 设置mqtt服务路由管理者。 + /// 添加微服务 /// - /// mqtt服务构建者。 - /// mqtt服务路由管理者实例工厂。 - /// 服务构建者。 - public static IServiceBuilder UseMqttRouteManager(this IServiceBuilder builder, Func factory) + /// + /// + public static void AddMicroService(this ContainerBuilder builder, Action option) { - builder.Services.RegisterAdapter(factory).InstancePerLifetimeScope(); - return builder; + option.Invoke(builder.AddCoreService()); } - #endregion RouteManager - /// - /// 设置共享文件路由管理者。 + /// 添加关联服务运行时 /// /// 服务构建者。 - /// 文件路径。 /// 服务构建者。 - public static IServiceBuilder UseSharedFileRouteManager(this IServiceBuilder builder, string filePath) - { - return builder.UseRouteManager(provider => - new SharedFileServiceRouteManager( - filePath, - provider.GetRequiredService>(), - provider.GetRequiredService(), - provider.GetRequiredService>())); - } - - public static IServiceBuilder UseSharedFileRouteManager(this IServiceBuilder builder, string ip, string port) + public static IServiceBuilder AddRelateServiceRuntime(this IServiceBuilder builder) { - return builder.UseRouteManager(provider => - new SharedFileServiceRouteManager( - ip, - provider.GetRequiredService>(), - provider.GetRequiredService(), - provider.GetRequiredService>())); + var services = builder.Services; + services.RegisterType(typeof(DefaultHealthCheckService)).As(typeof(IHealthCheckService)).SingleInstance(); + services.RegisterType(typeof(DefaultAddressResolver)).As(typeof(IAddressResolver)).SingleInstance(); + services.RegisterType(typeof(RemoteInvokeService)).As(typeof(IRemoteInvokeService)).SingleInstance(); + return builder.UseAddressSelector().AddClusterSupport(); } - #region AddressSelector /// - /// 使用轮询的地址选择器。 + /// The AddServiceEngine /// - /// 服务构建者。 - /// 服务构建者。 - public static IServiceBuilder UsePollingAddressSelector(this IServiceBuilder builder) + /// The builder + /// The engine + /// The + public static IServiceBuilder AddServiceEngine(this IServiceBuilder builder, Type engine) { - builder.Services.RegisterType(typeof(PollingAddressSelector)) - .Named(AddressSelectorMode.Polling.ToString(), typeof(IAddressSelector)).SingleInstance(); + var services = builder.Services; + services.RegisterType(engine).As(typeof(IServiceEngine)).SingleInstance(); + builder.Services.RegisterType(typeof(DefaultServiceEngineBuilder)).As(typeof(IServiceEngineBuilder)).SingleInstance(); return builder; } /// - /// 使用压力最小优先分配轮询的地址选择器。 + /// 添加服务运行时服务。 /// /// 服务构建者。 /// 服务构建者。 - public static IServiceBuilder UseFairPollingAddressSelector(this IServiceBuilder builder) + public static IServiceBuilder AddServiceRuntime(this IServiceBuilder builder) { - builder.Services.RegisterType(typeof(FairPollingAdrSelector)) - .Named(AddressSelectorMode.FairPolling.ToString(), typeof(IAddressSelector)).SingleInstance(); - return builder; + builder.Services.RegisterType(typeof(DefaultServiceEntryLocate)).As(typeof(IServiceEntryLocate)).SingleInstance(); + builder.Services.RegisterType(typeof(DefaultServiceExecutor)).As(typeof(IServiceExecutor)) + .Named(CommunicationProtocol.Tcp.ToString()).SingleInstance(); + + return builder.RegisterServices().RegisterRepositories().RegisterServiceBus().RegisterModules().RegisterInstanceByConstraint().AddRuntime(); } /// - /// 使用哈希的地址选择器。 + /// The GetDataContractName /// - /// 服务构建者。 - /// 服务构建者。 - public static IServiceBuilder UseHashAlgorithmAddressSelector(this IServiceBuilder builder) + /// The builder + /// The virtualPaths + /// The + public static IEnumerable GetDataContractName(this IServiceBuilder builder, params string[] virtualPaths) { - builder.Services.RegisterType(typeof(HashAlgorithmAdrSelector)) - .Named(AddressSelectorMode.HashAlgorithm.ToString(), typeof(IAddressSelector)).SingleInstance(); - return builder; + var namespaces = new List(); + var assemblies = builder.GetInterfaceService(virtualPaths) + .Select(p => p.Assembly) + .Union(GetSystemModules()) + .Distinct() + .ToList(); + + assemblies.ForEach(assembly => + { + namespaces.AddRange(assembly.GetTypes().Where(t => t.GetCustomAttribute() != null).Select(n => n.Namespace)); + }); + return namespaces; } /// - /// 使用随机的地址选择器。 + /// The GetInterfaceService /// - /// 服务构建者。 - /// 服务构建者。 - public static IServiceBuilder UseRandomAddressSelector(this IServiceBuilder builder) + /// The builder + /// The virtualPaths + /// The + public static List GetInterfaceService(this IServiceBuilder builder, params string[] virtualPaths) { - builder.Services.RegisterType(typeof(RandomAddressSelector)) - .Named(AddressSelectorMode.Random.ToString(), typeof(IAddressSelector)).SingleInstance(); - return builder; + var types = new List(); + var referenceAssemblies = GetReferenceAssembly(virtualPaths); + referenceAssemblies.ForEach(p => + { + types.AddRange(p.GetTypes().Where(t => typeof(IServiceKey).GetTypeInfo().IsAssignableFrom(t) && t.IsInterface)); + }); + return types; } /// - /// 设置服务地址选择器。 + /// The RegisterInstanceByConstraint /// - /// 服务构建者。 - /// 地址选择器实例。 - /// 服务构建者。 - public static IServiceBuilder UseAddressSelector(this IServiceBuilder builder) + /// The builder + /// The virtualPaths + /// The + public static IServiceBuilder RegisterInstanceByConstraint(this IServiceBuilder builder, params string[] virtualPaths) { - return builder.UseRandomAddressSelector().UsePollingAddressSelector().UseFairPollingAddressSelector().UseHashAlgorithmAddressSelector(); - } + var services = builder.Services; + var referenceAssemblies = GetReferenceAssembly(virtualPaths); - #endregion AddressSelector + foreach (var assembly in referenceAssemblies) + { + services.RegisterAssemblyTypes(assembly) + .Where(t => typeof(ISingletonDependency).GetTypeInfo().IsAssignableFrom(t)).AsImplementedInterfaces().AsSelf().SingleInstance(); - #region Configuration Watch + services.RegisterAssemblyTypes(assembly) + .Where(t => typeof(ITransientDependency).GetTypeInfo().IsAssignableFrom(t)).AsImplementedInterfaces().AsSelf().InstancePerDependency(); + } + return builder; + } /// - /// Configuration Watch + /// 依赖注入组件模块程序集 /// /// - /// 服务构建者 - public static IServiceBuilder AddConfigurationWatch(this IServiceBuilder builder) + /// + /// 返回注册模块信息 + public static IServiceBuilder RegisterModules(this IServiceBuilder builder, params string[] virtualPaths) { var services = builder.Services; - services.RegisterType(typeof(ConfigurationWatchManager)).As(typeof(IConfigurationWatchManager)).SingleInstance(); - return builder; - } - #endregion - - #region Codec Factory - - /// - /// 使用编解码器。 - /// + var referenceAssemblies = GetAssemblies(virtualPaths); + if (builder == null) throw new ArgumentNullException("builder"); + //从surgingSettings.json取到packages + var packages = ConvertDictionary(AppConfig.ServerOptions.Packages); + foreach (var moduleAssembly in referenceAssemblies) + { + GetAbstractModules(moduleAssembly).ForEach(p => + { + services.RegisterModule(p); + if (packages.ContainsKey(p.TypeName)) + { + var useModules = packages[p.TypeName]; + if (useModules.AsSpan().IndexOf(p.ModuleName) >= 0) + p.Enable = true; + else + p.Enable = false; + } + _modules.Add(p); + }); + } + builder.Services.Register(provider => new ModuleProvider( + _modules, virtualPaths, provider.Resolve>(), provider.Resolve() + )).As().SingleInstance(); + return builder; + } + + /// + /// The RegisterRepositories + /// + /// The builder + /// 虚拟路径 + /// 返回注册模块信息 + public static IServiceBuilder RegisterRepositories(this IServiceBuilder builder, params string[] virtualPaths) + { + var services = builder.Services; + var referenceAssemblies = GetAssemblies(virtualPaths); + + foreach (var assembly in referenceAssemblies) + { + services.RegisterAssemblyTypes(assembly) + .Where(t => typeof(BaseRepository).GetTypeInfo().IsAssignableFrom(t)); + } + return builder; + } + + /// + /// 依赖注入事件总线模块程序集 + /// + /// + /// + /// 返回注册模块信息 + public static IServiceBuilder RegisterServiceBus + (this IServiceBuilder builder, params string[] virtualPaths) + { + var services = builder.Services; + var referenceAssemblies = GetAssemblies(virtualPaths); + + foreach (var assembly in referenceAssemblies) + { + services.RegisterAssemblyTypes(assembly) + .Where(t => typeof(IIntegrationEventHandler).GetTypeInfo().IsAssignableFrom(t)).AsImplementedInterfaces().SingleInstance(); + services.RegisterAssemblyTypes(assembly) + .Where(t => typeof(IIntegrationEventHandler).IsAssignableFrom(t)).SingleInstance(); + } + return builder; + } + + /// + /// 依赖注入业务模块程序集 + /// + /// ioc容器 + /// The virtualPaths + /// 返回注册模块信息 + public static IServiceBuilder RegisterServices(this IServiceBuilder builder, params string[] virtualPaths) + { + try + { + var services = builder.Services; + var referenceAssemblies = GetAssemblies(virtualPaths); + foreach (var assembly in referenceAssemblies) + { + services.RegisterAssemblyTypes(assembly) + //注入继承IServiceKey接口的所有接口 + .Where(t => typeof(IServiceKey).GetTypeInfo().IsAssignableFrom(t) && t.IsInterface) + .AsImplementedInterfaces(); + services.RegisterAssemblyTypes(assembly) + //注入实现IServiceBehavior接口并ModuleName为空的类,作为接口实现类 + .Where(t => typeof(IServiceBehavior).GetTypeInfo().IsAssignableFrom(t) && t.GetTypeInfo().GetCustomAttribute() == null).AsImplementedInterfaces(); + + var types = assembly.GetTypes().Where(t => typeof(IServiceBehavior).GetTypeInfo().IsAssignableFrom(t) && t.GetTypeInfo().GetCustomAttribute() != null); + foreach (var type in types) + { + var module = type.GetTypeInfo().GetCustomAttribute(); + //对ModuleName不为空的对象,找到第一个继承IServiceKey的接口并注入接口及实现 + var interfaceObj = type.GetInterfaces() + .FirstOrDefault(t => typeof(IServiceKey).GetTypeInfo().IsAssignableFrom(t)); + if (interfaceObj != null) + { + services.RegisterType(type).AsImplementedInterfaces().Named(module.ModuleName, interfaceObj); + services.RegisterType(type).Named(module.ModuleName, type); + } + } + } + return builder; + } + catch (Exception ex) + { + if (ex is System.Reflection.ReflectionTypeLoadException) + { + var typeLoadException = ex as ReflectionTypeLoadException; + var loaderExceptions = typeLoadException.LoaderExceptions; + throw loaderExceptions[0]; + } + throw ex; + } + } + + /// + /// 设置服务地址选择器。 + /// /// 服务构建者。 - /// /// 服务构建者。 - public static IServiceBuilder UseCodec(this IServiceBuilder builder, ITransportMessageCodecFactory codecFactory) + public static IServiceBuilder UseAddressSelector(this IServiceBuilder builder) { - builder.Services.RegisterInstance(codecFactory).SingleInstance(); + return builder.UseRandomAddressSelector().UsePollingAddressSelector().UseFairPollingAddressSelector().UseHashAlgorithmAddressSelector(); + } + + /// + /// 设置缓存管理者。 + /// + /// 服务构建者。 + /// 缓存管理者实例工厂。 + /// 服务构建者。 + public static IServiceBuilder UseCacheManager(this IServiceBuilder builder, Func factory) + { + builder.Services.RegisterAdapter(factory).InstancePerLifetimeScope(); return builder; } @@ -330,345 +494,215 @@ public static IServiceBuilder UseCodec(this IServiceBuilder builder, Func - /// 使用Json编解码器。 + /// 使用编解码器。 /// /// 服务构建者。 + /// /// 服务构建者。 - public static IServiceBuilder UseJsonCodec(this IServiceBuilder builder) + public static IServiceBuilder UseCodec(this IServiceBuilder builder, ITransportMessageCodecFactory codecFactory) { - return builder.UseCodec(); + builder.Services.RegisterInstance(codecFactory).SingleInstance(); + return builder; } /// - /// 添加客户端运行时服务。 + /// 设置服务命令管理者。 /// /// 服务构建者。 + /// 服务命令管理者实例工厂。 /// 服务构建者。 - public static IServiceBuilder AddClientRuntime(this IServiceBuilder builder) + public static IServiceBuilder UseCommandManager(this IServiceBuilder builder, Func factory) { - var services = builder.Services; - services.RegisterType(typeof(DefaultHealthCheckService)).As(typeof(IHealthCheckService)).SingleInstance(); - services.RegisterType(typeof(DefaultAddressResolver)).As(typeof(IAddressResolver)).SingleInstance(); - services.RegisterType(typeof(RemoteInvokeService)).As(typeof(IRemoteInvokeService)).SingleInstance(); - return builder.UseAddressSelector().AddRuntime().AddClusterSupport(); + builder.Services.RegisterAdapter(factory).InstancePerLifetimeScope(); + return builder; } /// - /// 添加集群支持 + /// 使用压力最小优先分配轮询的地址选择器。 /// - /// 服务构建者 + /// 服务构建者。 /// 服务构建者。 - public static IServiceBuilder AddClusterSupport(this IServiceBuilder builder) - { - var services = builder.Services; - services.RegisterType(typeof(ServiceCommandProvider)).As(typeof(IServiceCommandProvider)).SingleInstance(); - services.RegisterType(typeof(BreakeRemoteInvokeService)).As(typeof(IBreakeRemoteInvokeService)).SingleInstance(); - services.RegisterType(typeof(FailoverInjectionInvoker)).AsImplementedInterfaces() - .Named(StrategyType.Injection.ToString(), typeof(IClusterInvoker)).SingleInstance(); - services.RegisterType(typeof(FailoverHandoverInvoker)).AsImplementedInterfaces() - .Named(StrategyType.Failover.ToString(), typeof(IClusterInvoker)).SingleInstance(); - return builder; - } - - public static IServiceBuilder AddFilter(this IServiceBuilder builder, IFilter filter) - { - var services = builder.Services; - services.Register(p => filter).As(typeof(IFilter)).SingleInstance(); - if (typeof(IExceptionFilter).IsAssignableFrom(filter.GetType())) - { - var exceptionFilter = filter as IExceptionFilter; - services.Register(p => exceptionFilter).As(typeof(IExceptionFilter)).SingleInstance(); - } - else if (typeof(IAuthorizationFilter).IsAssignableFrom(filter.GetType())) - { - var exceptionFilter = filter as IAuthorizationFilter; - services.Register(p => exceptionFilter).As(typeof(IAuthorizationFilter)).SingleInstance(); - } - return builder; - } - - public static IServiceBuilder AddServiceEngine(this IServiceBuilder builder, Type engine) + public static IServiceBuilder UseFairPollingAddressSelector(this IServiceBuilder builder) { - var services = builder.Services; - services.RegisterType(engine).As(typeof(IServiceEngine)).SingleInstance(); - builder.Services.RegisterType(typeof(DefaultServiceEngineBuilder)).As(typeof(IServiceEngineBuilder)).SingleInstance(); + builder.Services.RegisterType(typeof(FairPollingAdrSelector)) + .Named(AddressSelectorMode.FairPolling.ToString(), typeof(IAddressSelector)).SingleInstance(); return builder; } /// - /// 添加服务运行时服务。 + /// 使用哈希的地址选择器。 /// /// 服务构建者。 /// 服务构建者。 - public static IServiceBuilder AddServiceRuntime(this IServiceBuilder builder) + public static IServiceBuilder UseHashAlgorithmAddressSelector(this IServiceBuilder builder) { - builder.Services.RegisterType(typeof(DefaultServiceEntryLocate)).As(typeof(IServiceEntryLocate)).SingleInstance(); - builder.Services.RegisterType(typeof(DefaultServiceExecutor)).As(typeof(IServiceExecutor)) - .Named(CommunicationProtocol.Tcp.ToString()).SingleInstance(); - - return builder.RegisterServices().RegisterRepositories().RegisterServiceBus().RegisterModules().RegisterInstanceByConstraint().AddRuntime(); + builder.Services.RegisterType(typeof(HashAlgorithmAdrSelector)) + .Named(AddressSelectorMode.HashAlgorithm.ToString(), typeof(IAddressSelector)).SingleInstance(); + return builder; } /// - /// 添加关联服务运行时 + /// 使用Json编解码器。 /// /// 服务构建者。 /// 服务构建者。 - public static IServiceBuilder AddRelateServiceRuntime(this IServiceBuilder builder) + public static IServiceBuilder UseJsonCodec(this IServiceBuilder builder) { - var services = builder.Services; - services.RegisterType(typeof(DefaultHealthCheckService)).As(typeof(IHealthCheckService)).SingleInstance(); - services.RegisterType(typeof(DefaultAddressResolver)).As(typeof(IAddressResolver)).SingleInstance(); - services.RegisterType(typeof(RemoteInvokeService)).As(typeof(IRemoteInvokeService)).SingleInstance(); - return builder.UseAddressSelector().AddClusterSupport(); + return builder.UseCodec(); } /// - /// 添加核心服务。 + /// 设置mqtt服务路由管理者。 /// - /// 服务集合。 + /// mqtt服务构建者。 + /// mqtt服务路由管理者实例工厂。 /// 服务构建者。 - public static IServiceBuilder AddCoreService(this ContainerBuilder services) + public static IServiceBuilder UseMqttRouteManager(this IServiceBuilder builder, Func factory) { - Check.NotNull(services, "services"); - //注册服务ID生成实例 - services.RegisterType().As().SingleInstance(); - services.Register(p => new CPlatformContainer(p)); - //注册默认的类型转换 - services.RegisterType(typeof(DefaultTypeConvertibleProvider)).As(typeof(ITypeConvertibleProvider)).SingleInstance(); - //注册默认的类型转换服务 - services.RegisterType(typeof(DefaultTypeConvertibleService)).As(typeof(ITypeConvertibleService)).SingleInstance(); - //注册权限过滤 - services.RegisterType(typeof(AuthorizationAttribute)).As(typeof(IAuthorizationFilter)).SingleInstance(); - //注册基本过滤 - services.RegisterType(typeof(AuthorizationAttribute)).As(typeof(IFilter)).SingleInstance(); - //注册服务器路由接口 - services.RegisterType(typeof(DefaultServiceRouteProvider)).As(typeof(IServiceRouteProvider)).SingleInstance(); - //注册服务路由工厂 - services.RegisterType(typeof(DefaultServiceRouteFactory)).As(typeof(IServiceRouteFactory)).SingleInstance(); - //注册服务订阅工厂 - services.RegisterType(typeof(DefaultServiceSubscriberFactory)).As(typeof(IServiceSubscriberFactory)).SingleInstance(); - //注册服务token生成接口 - services.RegisterType(typeof(ServiceTokenGenerator)).As(typeof(IServiceTokenGenerator)).SingleInstance(); - //注册哈希一致性算法 - services.RegisterType(typeof(HashAlgorithm)).As(typeof(IHashAlgorithm)).SingleInstance(); - //注册组件生命周期接口 - services.RegisterType(typeof(ServiceEngineLifetime)).As(typeof(IServiceEngineLifetime)).SingleInstance(); - //注册服务心跳管理 - services.RegisterType(typeof(DefaultServiceHeartbeatManager)).As(typeof(IServiceHeartbeatManager)).SingleInstance(); - return new ServiceBuilder(services) - .AddJsonSerialization() - .UseJsonCodec(); - + builder.Services.RegisterAdapter(factory).InstancePerLifetimeScope(); + return builder; } - public static IServiceBuilder RegisterInstanceByConstraint(this IServiceBuilder builder, params string[] virtualPaths) + /// + /// 使用轮询的地址选择器。 + /// + /// 服务构建者。 + /// 服务构建者。 + public static IServiceBuilder UsePollingAddressSelector(this IServiceBuilder builder) { - var services = builder.Services; - var referenceAssemblies = GetReferenceAssembly(virtualPaths); - - foreach (var assembly in referenceAssemblies) - { - services.RegisterAssemblyTypes(assembly) - .Where(t => typeof(ISingletonDependency).GetTypeInfo().IsAssignableFrom(t)).AsImplementedInterfaces().AsSelf().SingleInstance(); - - services.RegisterAssemblyTypes(assembly) - .Where(t => typeof(ITransientDependency).GetTypeInfo().IsAssignableFrom(t)).AsImplementedInterfaces().AsSelf().InstancePerDependency(); - } + builder.Services.RegisterType(typeof(PollingAddressSelector)) + .Named(AddressSelectorMode.Polling.ToString(), typeof(IAddressSelector)).SingleInstance(); return builder; - } - private static IServiceBuilder AddRuntime(this IServiceBuilder builder) + /// + /// 使用随机的地址选择器。 + /// + /// 服务构建者。 + /// 服务构建者。 + public static IServiceBuilder UseRandomAddressSelector(this IServiceBuilder builder) { - var services = builder.Services; - - services.RegisterType(typeof(ClrServiceEntryFactory)).As(typeof(IClrServiceEntryFactory)).SingleInstance(); - - services.Register(provider => - { - try - { - var assemblys = GetReferenceAssembly(); - var types = assemblys.SelectMany(i => i.ExportedTypes).ToArray(); - return new AttributeServiceEntryProvider(types, provider.Resolve(), - provider.Resolve>(), provider.Resolve()); - } - finally - { - builder = null; - } - }).As(); - builder.Services.RegisterType(typeof(DefaultServiceEntryManager)).As(typeof(IServiceEntryManager)).SingleInstance(); + builder.Services.RegisterType(typeof(RandomAddressSelector)) + .Named(AddressSelectorMode.Random.ToString(), typeof(IAddressSelector)).SingleInstance(); return builder; } - /// - /// 添加微服务 - /// - /// - /// - public static void AddMicroService(this ContainerBuilder builder, Action option) + /// + /// 设置服务路由管理者。 + /// + /// 服务路由管理者实现。 + /// 服务构建者。 + /// 服务构建者。 + public static IServiceBuilder UseRouteManager(this IServiceBuilder builder) where T : class, IServiceRouteManager { - option.Invoke(builder.AddCoreService()); + builder.Services.RegisterType(typeof(T)).As(typeof(IServiceRouteManager)).SingleInstance(); + return builder; } - /// . - /// 依赖注入业务模块程序集 + /// + /// 设置服务路由管理者。 /// - /// ioc容器 - /// 返回注册模块信息 - public static IServiceBuilder RegisterServices(this IServiceBuilder builder, params string[] virtualPaths) + /// 服务构建者。 + /// 服务路由管理者实例工厂。 + /// 服务构建者。 + public static IServiceBuilder UseRouteManager(this IServiceBuilder builder, Func factory) { - try - { - var services = builder.Services; - var referenceAssemblies = GetAssemblies(virtualPaths); - foreach (var assembly in referenceAssemblies) - { - services.RegisterAssemblyTypes(assembly) - //注入继承IServiceKey接口的所有接口 - .Where(t => typeof(IServiceKey).GetTypeInfo().IsAssignableFrom(t) && t.IsInterface) - .AsImplementedInterfaces(); - services.RegisterAssemblyTypes(assembly) - //注入实现IServiceBehavior接口并ModuleName为空的类,作为接口实现类 - .Where(t => typeof(IServiceBehavior).GetTypeInfo().IsAssignableFrom(t) && t.GetTypeInfo().GetCustomAttribute() == null).AsImplementedInterfaces(); - - var types = assembly.GetTypes().Where(t => typeof(IServiceBehavior).GetTypeInfo().IsAssignableFrom(t) && t.GetTypeInfo().GetCustomAttribute() != null); - foreach (var type in types) - { - var module = type.GetTypeInfo().GetCustomAttribute(); - //对ModuleName不为空的对象,找到第一个继承IServiceKey的接口并注入接口及实现 - var interfaceObj = type.GetInterfaces() - .FirstOrDefault(t => typeof(IServiceKey).GetTypeInfo().IsAssignableFrom(t)); - if (interfaceObj != null) - { - services.RegisterType(type).AsImplementedInterfaces().Named(module.ModuleName, interfaceObj); - services.RegisterType(type).Named(module.ModuleName, type); - } - } - - } - return builder; - } - catch (Exception ex) - { - if (ex is System.Reflection.ReflectionTypeLoadException) - { - var typeLoadException = ex as ReflectionTypeLoadException; - var loaderExceptions = typeLoadException.LoaderExceptions; - throw loaderExceptions[0]; - } - throw ex; - } + builder.Services.RegisterAdapter(factory).InstancePerLifetimeScope(); + return builder; } /// - /// 依赖注入事件总线模块程序集 + /// 设置服务路由管理者。 /// - /// - /// - /// 返回注册模块信息 - public static IServiceBuilder RegisterServiceBus - (this IServiceBuilder builder, params string[] virtualPaths) + /// 服务构建者。 + /// 服务路由管理者实例。 + /// 服务构建者。 + public static IServiceBuilder UseRouteManager(this IServiceBuilder builder, IServiceRouteManager instance) { - var services = builder.Services; - var referenceAssemblies = GetAssemblies(virtualPaths); - - foreach (var assembly in referenceAssemblies) - { - services.RegisterAssemblyTypes(assembly) - .Where(t => typeof(IIntegrationEventHandler).GetTypeInfo().IsAssignableFrom(t)).AsImplementedInterfaces().SingleInstance(); - services.RegisterAssemblyTypes(assembly) - .Where(t => typeof(IIntegrationEventHandler).IsAssignableFrom(t)).SingleInstance(); - } + builder.Services.RegisterInstance(instance); return builder; } - /// - /// 依赖注入仓储模块程序集 - /// - /// IOC容器 - /// 虚拟路径 - /// 返回注册模块信息 - public static IServiceBuilder RegisterRepositories(this IServiceBuilder builder, params string[] virtualPaths) + /// + /// 设置共享文件路由管理者。 + /// + /// 服务构建者。 + /// 文件路径。 + /// 服务构建者。 + public static IServiceBuilder UseSharedFileRouteManager(this IServiceBuilder builder, string filePath) { - var services = builder.Services; - var referenceAssemblies = GetAssemblies(virtualPaths); - - foreach (var assembly in referenceAssemblies) - { - services.RegisterAssemblyTypes(assembly) - .Where(t => typeof(BaseRepository).GetTypeInfo().IsAssignableFrom(t)); - } - return builder; + return builder.UseRouteManager(provider => + new SharedFileServiceRouteManager( + filePath, + provider.GetRequiredService>(), + provider.GetRequiredService(), + provider.GetRequiredService>())); } - /// - /// 依赖注入组件模块程序集 + /// + /// The UseSharedFileRouteManager /// - /// - /// - /// 返回注册模块信息 - public static IServiceBuilder RegisterModules(this IServiceBuilder builder, params string[] virtualPaths) + /// The builder + /// The ip + /// The port + /// The + public static IServiceBuilder UseSharedFileRouteManager(this IServiceBuilder builder, string ip, string port) { - var services = builder.Services; - var referenceAssemblies = GetAssemblies(virtualPaths); - if (builder == null) throw new ArgumentNullException("builder"); - //从surgingSettings.json取到packages - var packages = ConvertDictionary(AppConfig.ServerOptions.Packages); - foreach (var moduleAssembly in referenceAssemblies) - { - GetAbstractModules(moduleAssembly).ForEach(p => - { - services.RegisterModule(p); - if (packages.ContainsKey(p.TypeName)) - { - var useModules = packages[p.TypeName]; - if (useModules.AsSpan().IndexOf(p.ModuleName) >= 0) - p.Enable = true; - else - p.Enable = false; - } - _modules.Add(p); - }); - } - builder.Services.Register(provider => new ModuleProvider( - _modules, virtualPaths, provider.Resolve>(), provider.Resolve() - )).As().SingleInstance(); - return builder; + return builder.UseRouteManager(provider => + new SharedFileServiceRouteManager( + ip, + provider.GetRequiredService>(), + provider.GetRequiredService(), + provider.GetRequiredService>())); } - public static List GetInterfaceService(this IServiceBuilder builder, params string[] virtualPaths) + /// + /// 设置服务订阅管理者。 + /// + /// 服务构建者。 + /// 服务订阅管理者实例工厂。 + /// 服务构建者。 + public static IServiceBuilder UseSubscribeManager(this IServiceBuilder builder, Func factory) { - var types = new List(); - var referenceAssemblies = GetReferenceAssembly(virtualPaths); - referenceAssemblies.ForEach(p => - { - types.AddRange(p.GetTypes().Where(t => typeof(IServiceKey).GetTypeInfo().IsAssignableFrom(t) && t.IsInterface)); - }); - return types; + builder.Services.RegisterAdapter(factory).InstancePerLifetimeScope(); + return builder; } - public static IEnumerable GetDataContractName(this IServiceBuilder builder, params string[] virtualPaths) + /// + /// The AddRuntime + /// + /// The builder + /// The + private static IServiceBuilder AddRuntime(this IServiceBuilder builder) { - var namespaces = new List(); - var assemblies = builder.GetInterfaceService(virtualPaths) - .Select(p => p.Assembly) - .Union(GetSystemModules()) - .Distinct() - .ToList(); + var services = builder.Services; - assemblies.ForEach(assembly => + services.RegisterType(typeof(ClrServiceEntryFactory)).As(typeof(IClrServiceEntryFactory)).SingleInstance(); + + services.Register(provider => { - namespaces.AddRange(assembly.GetTypes().Where(t => t.GetCustomAttribute() != null).Select(n => n.Namespace)); - }); - return namespaces; + try + { + var assemblys = GetReferenceAssembly(); + var types = assemblys.SelectMany(i => i.ExportedTypes).ToArray(); + return new AttributeServiceEntryProvider(types, provider.Resolve(), + provider.Resolve>(), provider.Resolve()); + } + finally + { + builder = null; + } + }).As(); + builder.Services.RegisterType(typeof(DefaultServiceEntryManager)).As(typeof(IServiceEntryManager)).SingleInstance(); + return builder; } + /// + /// The ConvertDictionary + /// + /// The list + /// The private static IDictionary ConvertDictionary(List list) { var result = new Dictionary(); @@ -679,50 +713,57 @@ private static IDictionary ConvertDictionary(List return result; } - private static List GetReferenceAssembly(params string[] virtualPaths) + /// + /// 获取抽象模块(查找继承AbstractModule类的对象并创建实例) + /// + /// + /// + private static List GetAbstractModules(Assembly assembly) { - var refAssemblies = new List(); - var rootPath = AppContext.BaseDirectory; - var existsPath = virtualPaths.Any(); - if (existsPath && !string.IsNullOrEmpty(AppConfig.ServerOptions.RootPath)) - rootPath = AppConfig.ServerOptions.RootPath; - var result = _referenceAssembly; - if (!result.Any() || existsPath) + var abstractModules = new List(); + Type[] arrayModule = + assembly.GetTypes().Where( + t => t.IsSubclassOf(typeof(AbstractModule))).ToArray(); + foreach (var moduleType in arrayModule) { - var paths = virtualPaths.Select(m => Path.Combine(rootPath, m)).ToList(); - if (!existsPath) paths.Add(rootPath); - paths.ForEach(path => - { - var assemblyFiles = GetAllAssemblyFiles(path); - - foreach (var referencedAssemblyFile in assemblyFiles) - { - var referencedAssembly = Assembly.LoadFrom(referencedAssemblyFile); - if (!_referenceAssembly.Contains(referencedAssembly)) - _referenceAssembly.Add(referencedAssembly); - refAssemblies.Add(referencedAssembly); - } - result = existsPath ? refAssemblies : _referenceAssembly; - }); + var abstractModule = (AbstractModule)Activator.CreateInstance(moduleType); + abstractModules.Add(abstractModule); } - return result; + return abstractModules; } - private static List GetSystemModules() + /// + /// The GetAllAssemblyFiles + /// + /// The parentDir + /// The + private static List GetAllAssemblyFiles(string parentDir) { - var assemblies = new List(); - var referenceAssemblies = GetReferenceAssembly(); - foreach (var referenceAssembly in referenceAssemblies) + var notRelatedFile = AppConfig.ServerOptions.NotRelatedAssemblyFiles; + var relatedFile = AppConfig.ServerOptions.RelatedAssemblyFiles; + var pattern = string.Format("^Microsoft.\\w*|^System.\\w*|^Netty.\\w*|^Autofac.\\w*{0}", + string.IsNullOrEmpty(notRelatedFile) ? "" : $"|{notRelatedFile}"); + Regex notRelatedRegex = new Regex(pattern, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase); + Regex relatedRegex = new Regex(relatedFile, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase); + if (!string.IsNullOrEmpty(relatedFile)) { - var abstractModules = GetAbstractModules(referenceAssembly); - if (abstractModules.Any(p => p.GetType().IsSubclassOf(typeof(SystemModule)))) - { - assemblies.Add(referenceAssembly); - } + return + Directory.GetFiles(parentDir, "*.dll").Select(Path.GetFullPath).Where( + a => !notRelatedRegex.IsMatch(a) && relatedRegex.IsMatch(a)).ToList(); + } + else + { + return + Directory.GetFiles(parentDir, "*.dll").Select(Path.GetFullPath).Where( + a => !notRelatedRegex.IsMatch(Path.GetFileName(a))).ToList(); } - return assemblies; } + /// + /// The GetAssemblies + /// + /// The virtualPaths + /// The private static List GetAssemblies(params string[] virtualPaths) { var referenceAssemblies = new List(); @@ -743,24 +784,10 @@ private static List GetAssemblies(params string[] virtualPaths) } /// - /// 获取抽象模块(查找继承AbstractModule类的对象并创建实例) + /// The GetFilterAssemblies /// - /// - /// - private static List GetAbstractModules(Assembly assembly) - { - var abstractModules = new List(); - Type[] arrayModule = - assembly.GetTypes().Where( - t => t.IsSubclassOf(typeof(AbstractModule))).ToArray(); - foreach (var moduleType in arrayModule) - { - var abstractModule = (AbstractModule)Activator.CreateInstance(moduleType); - abstractModules.Add(abstractModule); - } - return abstractModules; - } - + /// The assemblyNames + /// The private static string[] GetFilterAssemblies(string[] assemblyNames) { var notRelatedFile = AppConfig.ServerOptions.NotRelatedAssemblyFiles; @@ -783,26 +810,90 @@ private static string[] GetFilterAssemblies(string[] assemblyNames) } } - private static List GetAllAssemblyFiles(string parentDir) + /// + /// The GetReferenceAssembly + /// + /// The virtualPaths + /// The + private static List GetReferenceAssembly(params string[] virtualPaths) { - var notRelatedFile = AppConfig.ServerOptions.NotRelatedAssemblyFiles; - var relatedFile = AppConfig.ServerOptions.RelatedAssemblyFiles; - var pattern = string.Format("^Microsoft.\\w*|^System.\\w*|^Netty.\\w*|^Autofac.\\w*{0}", - string.IsNullOrEmpty(notRelatedFile) ? "" : $"|{notRelatedFile}"); - Regex notRelatedRegex = new Regex(pattern, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase); - Regex relatedRegex = new Regex(relatedFile, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase); - if (!string.IsNullOrEmpty(relatedFile)) + var refAssemblies = new List(); + var rootPath = AppContext.BaseDirectory; + var existsPath = virtualPaths.Any(); + if (existsPath && !string.IsNullOrEmpty(AppConfig.ServerOptions.RootPath)) + rootPath = AppConfig.ServerOptions.RootPath; + var result = _referenceAssembly; + if (!result.Any() || existsPath) { - return - Directory.GetFiles(parentDir, "*.dll").Select(Path.GetFullPath).Where( - a => !notRelatedRegex.IsMatch(a) && relatedRegex.IsMatch(a)).ToList(); + var paths = virtualPaths.Select(m => Path.Combine(rootPath, m)).ToList(); + if (!existsPath) paths.Add(rootPath); + paths.ForEach(path => + { + var assemblyFiles = GetAllAssemblyFiles(path); + + foreach (var referencedAssemblyFile in assemblyFiles) + { + var referencedAssembly = Assembly.LoadFrom(referencedAssemblyFile); + if (!_referenceAssembly.Contains(referencedAssembly)) + _referenceAssembly.Add(referencedAssembly); + refAssemblies.Add(referencedAssembly); + } + result = existsPath ? refAssemblies : _referenceAssembly; + }); } - else + return result; + } + + /// + /// The GetSystemModules + /// + /// The + private static List GetSystemModules() + { + var assemblies = new List(); + var referenceAssemblies = GetReferenceAssembly(); + foreach (var referenceAssembly in referenceAssemblies) { - return - Directory.GetFiles(parentDir, "*.dll").Select(Path.GetFullPath).Where( - a => !notRelatedRegex.IsMatch(Path.GetFileName(a))).ToList(); + var abstractModules = GetAbstractModules(referenceAssembly); + if (abstractModules.Any(p => p.GetType().IsSubclassOf(typeof(SystemModule)))) + { + assemblies.Add(referenceAssembly); + } } + return assemblies; + } + + #endregion 方法 + } + + /// + /// 默认服务构建者。 + /// + internal sealed class ServiceBuilder : IServiceBuilder + { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The services + public ServiceBuilder(ContainerBuilder services) + { + if (services == null) + throw new ArgumentNullException(nameof(services)); + Services = services; } + + #endregion 构造函数 + + #region 属性 + + /// + /// Gets or sets the Services + /// 服务集合。 + /// + public ContainerBuilder Services { get; set; } + + #endregion 属性 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Convertibles/ITypeConvertibleProvider.cs b/src/Surging.Core/Surging.Core.CPlatform/Convertibles/ITypeConvertibleProvider.cs index 50b5a41de..60c31d3df 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Convertibles/ITypeConvertibleProvider.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Convertibles/ITypeConvertibleProvider.cs @@ -3,6 +3,8 @@ namespace Surging.Core.CPlatform.Convertibles { + #region 委托 + /// /// 类型转换。 /// @@ -11,15 +13,25 @@ namespace Surging.Core.CPlatform.Convertibles /// 转换之后的类型,如果无法转换则返回null。 public delegate object TypeConvertDelegate(object instance, Type conversionType); + #endregion 委托 + + #region 接口 + /// /// 一个抽象的类型转换提供程序。 /// public interface ITypeConvertibleProvider { + #region 方法 + /// /// 获取类型转换器。 /// /// 类型转换器集合。 IEnumerable GetConverters(); + + #endregion 方法 } + + #endregion 接口 } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Convertibles/ITypeConvertibleService.cs b/src/Surging.Core/Surging.Core.CPlatform/Convertibles/ITypeConvertibleService.cs index c3daa616e..09870b568 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Convertibles/ITypeConvertibleService.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Convertibles/ITypeConvertibleService.cs @@ -2,11 +2,15 @@ namespace Surging.Core.CPlatform.Convertibles { + #region 接口 + /// /// 一个抽象的类型转换服务。 /// public interface ITypeConvertibleService { + #region 方法 + /// /// 转换。 /// @@ -14,5 +18,9 @@ public interface ITypeConvertibleService /// 转换的类型。 /// 转换之后的类型,如果无法转换则返回null。 object Convert(object instance, Type conversionType); + + #endregion 方法 } + + #endregion 接口 } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Convertibles/Implementation/DefaultTypeConvertibleProvider.cs b/src/Surging.Core/Surging.Core.CPlatform/Convertibles/Implementation/DefaultTypeConvertibleProvider.cs index 8ace5e1ce..d2b7a11ef 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Convertibles/Implementation/DefaultTypeConvertibleProvider.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Convertibles/Implementation/DefaultTypeConvertibleProvider.cs @@ -11,14 +11,29 @@ namespace Surging.Core.CPlatform.Convertibles.Implementation /// public class DefaultTypeConvertibleProvider : ITypeConvertibleProvider { + #region 字段 + + /// + /// Defines the _serializer + /// private readonly ISerializer _serializer; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serializer public DefaultTypeConvertibleProvider(ISerializer serializer) { _serializer = serializer; } - #region Implementation of ITypeConvertibleProvider + #endregion 构造函数 + + #region 方法 /// /// 获取类型转换器。 @@ -36,10 +51,6 @@ public IEnumerable GetConverters() yield return ComplexTypeConvert; } - #endregion Implementation of ITypeConvertibleProvider - - #region Private Method - /// /// 枚举类型转换器 /// @@ -53,6 +64,20 @@ private static object EnumTypeConvert(object instance, Type conversionType) return Enum.Parse(conversionType, instance.ToString()); } + /// + /// GUID转换器 + /// + /// + /// + /// + private static object GuidTypeConvert(object instance, Type conversionType) + { + if (instance == null || conversionType != typeof(Guid)) + return null; + Guid.TryParse(instance.ToString(), out Guid result); + return result; + } + /// /// 简单类型转换器 /// @@ -84,20 +109,6 @@ private object ComplexTypeConvert(object instance, Type conversionType) } } - /// - /// GUID转换器 - /// - /// - /// - /// - private static object GuidTypeConvert(object instance, Type conversionType) - { - if (instance == null || conversionType != typeof(Guid)) - return null; - Guid.TryParse(instance.ToString(), out Guid result); - return result; - } - - #endregion Private Method + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Convertibles/Implementation/DefaultTypeConvertibleService.cs b/src/Surging.Core/Surging.Core.CPlatform/Convertibles/Implementation/DefaultTypeConvertibleService.cs index d092a1e5b..989aa10f4 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Convertibles/Implementation/DefaultTypeConvertibleService.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Convertibles/Implementation/DefaultTypeConvertibleService.cs @@ -12,15 +12,27 @@ namespace Surging.Core.CPlatform.Convertibles.Implementation /// public class DefaultTypeConvertibleService : ITypeConvertibleService { - #region Field + #region 字段 + /// + /// Defines the _converters + /// private readonly IEnumerable _converters; + + /// + /// Defines the _logger + /// private readonly ILogger _logger; - #endregion Field + #endregion 字段 - #region Constructor + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The providers + /// The logger public DefaultTypeConvertibleService(IEnumerable providers, ILogger logger) { _logger = logger; @@ -30,9 +42,9 @@ public DefaultTypeConvertibleService(IEnumerable provi _converters = providers.SelectMany(p => p.GetConverters()).ToArray(); } - #endregion Constructor + #endregion 构造函数 - #region Implementation of ITypeConvertibleService + #region 方法 /// /// 转换。 @@ -65,10 +77,10 @@ public object Convert(object instance, Type conversionType) var exception = new CPlatformException($"无法将实例:{instance}转换为{conversionType}。"); if (_logger.IsEnabled(LogLevel.Error)) - _logger.LogError(exception,$"将 {instance.GetType()} 转换成 {conversionType} 时发生了错误。"); + _logger.LogError(exception, $"将 {instance.GetType()} 转换成 {conversionType} 时发生了错误。"); throw exception; } - #endregion Implementation of ITypeConvertibleService + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/DependencyResolution/DependencyResolverExtensions.cs b/src/Surging.Core/Surging.Core.CPlatform/DependencyResolution/DependencyResolverExtensions.cs index 58af57a0a..a0c630333 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/DependencyResolution/DependencyResolverExtensions.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/DependencyResolution/DependencyResolverExtensions.cs @@ -9,38 +9,34 @@ namespace Surging.Core.CPlatform.DependencyResolution /// /// 扩展依赖注入IOC容器 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public static class DependencyResolverExtensions { - #region 公共方法 + #region 方法 /// - /// 通过KEY获取实例 + /// 获取实例 /// /// 目标类型 /// IOC对象容器 - /// 键 /// 返回实例 - public static T GetService(this IDependencyResolver resolver, object key) + public static T GetService(this IDependencyResolver resolver) { Check.NotNull(resolver, "resolver"); - - return (T)resolver.GetService(typeof(T), key); + return (T)resolver.GetService(typeof(T), null); } /// - /// 获取实例 + /// 通过KEY获取实例 /// /// 目标类型 /// IOC对象容器 + /// 键 /// 返回实例 - public static T GetService(this IDependencyResolver resolver) + public static T GetService(this IDependencyResolver resolver, object key) { Check.NotNull(resolver, "resolver"); - return (T)resolver.GetService(typeof(T), null); + + return (T)resolver.GetService(typeof(T), key); } /// @@ -57,28 +53,28 @@ public static object GetService(this IDependencyResolver resolver, Type type) } /// - /// 通过KEY获取集合 + /// 获取集合 /// /// 目标类型 /// IOC对象容器 - /// 键 - /// 返回实例 - public static IEnumerable GetServices(this IDependencyResolver resolver, object key) + /// 返回集合 + public static IEnumerable GetServices(this IDependencyResolver resolver) { Check.NotNull(resolver, "resolver"); - return resolver.GetServices(typeof(T), key).OfType(); + return resolver.GetServices(typeof(T), null).OfType(); } /// - /// 获取集合 + /// 通过KEY获取集合 /// /// 目标类型 /// IOC对象容器 - /// 返回集合 - public static IEnumerable GetServices(this IDependencyResolver resolver) + /// 键 + /// 返回实例 + public static IEnumerable GetServices(this IDependencyResolver resolver, object key) { Check.NotNull(resolver, "resolver"); - return resolver.GetServices(typeof(T), null).OfType(); + return resolver.GetServices(typeof(T), key).OfType(); } /// @@ -94,8 +90,6 @@ public static IEnumerable GetServices(this IDependencyResolver resolver, return resolver.GetServices(type, null); } - #endregion - /// /// 通过KEY和TYPE获取实例对象集合 /// @@ -103,10 +97,6 @@ public static IEnumerable GetServices(this IDependencyResolver resolver, /// 类型 /// 键 /// 返回实例对象集合 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// internal static IEnumerable GetServiceAsServices(this IDependencyResolver resolver, Type type, object key) { @@ -115,5 +105,7 @@ internal static IEnumerable GetServiceAsServices(this IDependencyResolve var service = resolver.GetService(type, key); return service == null ? Enumerable.Empty() : new[] { service }; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/DependencyResolution/IDependencyResolver.cs b/src/Surging.Core/Surging.Core.CPlatform/DependencyResolution/IDependencyResolver.cs index 9271502d6..9006c8505 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/DependencyResolution/IDependencyResolver.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/DependencyResolution/IDependencyResolver.cs @@ -4,25 +4,21 @@ namespace Surging.Core.CPlatform.DependencyResolution { + #region 接口 + /// /// 注入IOC容器接口 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public interface IDependencyResolver { + #region 方法 + /// /// 通过KEY和TYPE获取实例对象 /// /// 类型 /// 键 /// 返回实例对象 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// object GetService(Type type, object key); /// @@ -31,10 +27,10 @@ public interface IDependencyResolver /// 类型 /// 键 /// 返回实例对象 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// IEnumerable GetServices(Type type, object key); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/DependencyResolution/ServiceResolver.cs b/src/Surging.Core/Surging.Core.CPlatform/DependencyResolution/ServiceResolver.cs index 4f88321b5..51f89170c 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/DependencyResolution/ServiceResolver.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/DependencyResolution/ServiceResolver.cs @@ -10,69 +10,44 @@ namespace Surging.Core.CPlatform.DependencyResolution /// /// IOC容器对象 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public class ServiceResolver : IDependencyResolver { #region 字段 + + /// + /// Defines the _defaultInstance + /// private static readonly ServiceResolver _defaultInstance = new ServiceResolver(); - private readonly ConcurrentDictionary, object> _initializers = - new ConcurrentDictionary, object>(); - #endregion - #region 公共方法 /// - /// 注册对象添加到IOC容器 + /// Defines the _initializers /// - /// 键 - /// 值 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public virtual void Register(string key, object value) - { - DebugCheck.NotNull(value); - // DebugCheck.NotNull(key); + private readonly ConcurrentDictionary, object> _initializers = + new ConcurrentDictionary, object>(); - _initializers.GetOrAdd(ValueTuple.Create(value.GetType(), key), value); - var interFaces = value.GetType().GetTypeInfo().GetInterfaces(); - foreach (var interFace in interFaces) - { - _initializers.GetOrAdd(ValueTuple.Create(interFace, key), value); - } - } - - public virtual void Register(string key, object value,Type type) - { - DebugCheck.NotNull(value); - _initializers.GetOrAdd(ValueTuple.Create(type, key), value); - } + #endregion 字段 + + #region 属性 /// + /// Gets the Current /// 返回当前IOC容器 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public static ServiceResolver Current { get { return _defaultInstance; } } + #endregion 属性 + + #region 方法 + /// /// 通过KEY和TYPE获取实例对象 /// /// 类型 /// 键 /// 返回实例对象 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public virtual object GetService(Type type, object key) { object result; @@ -86,15 +61,41 @@ public virtual object GetService(Type type, object key) /// 类型 /// 键 /// 返回实例对象 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public IEnumerable GetServices(Type type, object key) { return this.GetServiceAsServices(type, key); } - #endregion - } -} + /// + /// 注册对象添加到IOC容器 + /// + /// 键 + /// 值 + public virtual void Register(string key, object value) + { + DebugCheck.NotNull(value); + // DebugCheck.NotNull(key); + + _initializers.GetOrAdd(ValueTuple.Create(value.GetType(), key), value); + var interFaces = value.GetType().GetTypeInfo().GetInterfaces(); + foreach (var interFace in interFaces) + { + _initializers.GetOrAdd(ValueTuple.Create(interFace, key), value); + } + } + + /// + /// The Register + /// + /// The key + /// The value + /// The type + public virtual void Register(string key, object value, Type type) + { + DebugCheck.NotNull(value); + _initializers.GetOrAdd(ValueTuple.Create(type, key), value); + } + + #endregion 方法 + } +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Engines/IServiceEngine.cs b/src/Surging.Core/Surging.Core.CPlatform/Engines/IServiceEngine.cs index 746244ccc..9d906ae20 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Engines/IServiceEngine.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Engines/IServiceEngine.cs @@ -4,7 +4,14 @@ namespace Surging.Core.CPlatform.Engines { - public interface IServiceEngine + #region 接口 + + /// + /// Defines the + /// + public interface IServiceEngine { } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Engines/IServiceEngineBuilder.cs b/src/Surging.Core/Surging.Core.CPlatform/Engines/IServiceEngineBuilder.cs index ce84c879a..a5be52e86 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Engines/IServiceEngineBuilder.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Engines/IServiceEngineBuilder.cs @@ -5,10 +5,30 @@ namespace Surging.Core.CPlatform.Engines { - public interface IServiceEngineBuilder + #region 接口 + + /// + /// Defines the + /// + public interface IServiceEngineBuilder { + #region 方法 + + /// + /// The Build + /// + /// The serviceContainer void Build(ContainerBuilder serviceContainer); - ValueTuple,IEnumerable>? ReBuild(ContainerBuilder serviceContainer); + /// + /// The ReBuild + /// + /// The serviceContainer + /// The + ValueTuple, IEnumerable>? ReBuild(ContainerBuilder serviceContainer); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Engines/IServiceEngineLifetime.cs b/src/Surging.Core/Surging.Core.CPlatform/Engines/IServiceEngineLifetime.cs index 9fe17d886..b29ae263b 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Engines/IServiceEngineLifetime.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Engines/IServiceEngineLifetime.cs @@ -5,19 +5,51 @@ namespace Surging.Core.CPlatform.Engines { - public interface IServiceEngineLifetime + #region 接口 + + /// + /// Defines the + /// + public interface IServiceEngineLifetime { + #region 属性 + + /// + /// Gets the ServiceEngineStarted + /// CancellationToken ServiceEngineStarted { get; } + /// + /// Gets the ServiceEngineStopped + /// + CancellationToken ServiceEngineStopped { get; } + + /// + /// Gets the ServiceEngineStopping + /// CancellationToken ServiceEngineStopping { get; } - CancellationToken ServiceEngineStopped { get; } + #endregion 属性 + #region 方法 - void StopApplication(); + /// + /// The NotifyStarted + /// + void NotifyStarted(); + /// + /// The NotifyStopped + /// void NotifyStopped(); - void NotifyStarted(); + /// + /// The StopApplication + /// + void StopApplication(); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Engines/Implementation/DefaultServiceEngineBuilder.cs b/src/Surging.Core/Surging.Core.CPlatform/Engines/Implementation/DefaultServiceEngineBuilder.cs index b4dd38421..78e465272 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Engines/Implementation/DefaultServiceEngineBuilder.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Engines/Implementation/DefaultServiceEngineBuilder.cs @@ -1,26 +1,64 @@ using Autofac; +using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.IO; -using System.Text; using System.Linq; -using Microsoft.Extensions.Logging; using System.Reflection; +using System.Text; namespace Surging.Core.CPlatform.Engines.Implementation { + /// + /// Defines the + /// public class DefaultServiceEngineBuilder : IServiceEngineBuilder { - private readonly VirtualPathProviderServiceEngine _serviceEngine; - private readonly ILogger _logger; + #region 字段 + + /// + /// Defines the _dic + /// private readonly Dictionary _dic = new Dictionary(); - private DateTime _lastBuildTime=DateTime.Now; + + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the _serviceEngine + /// + private readonly VirtualPathProviderServiceEngine _serviceEngine; + + /// + /// Defines the _lastBuildTime + /// + private DateTime _lastBuildTime = DateTime.Now; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serviceEngine + /// The logger public DefaultServiceEngineBuilder(IServiceEngine serviceEngine, ILogger logger) { _serviceEngine = serviceEngine as VirtualPathProviderServiceEngine; _logger = logger; } + #endregion 构造函数 + + #region 方法 + + /// + /// The Build + /// + /// The serviceContainer public void Build(ContainerBuilder serviceContainer) { var serviceBuilder = new ServiceBuilder(serviceContainer); @@ -50,9 +88,14 @@ public void Build(ContainerBuilder serviceContainer) } } + /// + /// The ReBuild + /// + /// The serviceContainer + /// The public ValueTuple, IEnumerable>? ReBuild(ContainerBuilder serviceContainer) { - ValueTuple, IEnumerable>? result = null ; + ValueTuple, IEnumerable>? result = null; var serviceBuilder = new ServiceBuilder(serviceContainer); var virtualPaths = new List(); string rootPath = string.IsNullOrEmpty(AppConfig.ServerOptions.RootPath) ? @@ -62,12 +105,11 @@ public void Build(ContainerBuilder serviceContainer) if (_serviceEngine.ModuleServiceLocationFormats != null) { var paths = GetPaths(_serviceEngine.ModuleServiceLocationFormats); - paths = paths?.Where(p => (Directory.GetLastWriteTime(Path.Combine(rootPath,p)) - _lastBuildTime).TotalSeconds > 0).ToArray(); - if (paths == null || paths.Length==0) return null; + paths = paths?.Where(p => (Directory.GetLastWriteTime(Path.Combine(rootPath, p)) - _lastBuildTime).TotalSeconds > 0).ToArray(); + if (paths == null || paths.Length == 0) return null; if (_logger.IsEnabled(LogLevel.Debug)) _logger.LogDebug($"准备加载路径${string.Join(',', paths)}下的业务模块。"); - serviceBuilder.RegisterServices(paths); serviceBuilder.RegisterRepositories(paths); serviceBuilder.RegisterServiceBus(paths); @@ -89,15 +131,19 @@ public void Build(ContainerBuilder serviceContainer) return result; } - private string [] GetPaths(params string [] virtualPaths) + /// + /// The GetPaths + /// + /// The virtualPaths + /// The + private string[] GetPaths(params string[] virtualPaths) { - var directories = new List(virtualPaths.Where(p=>!string.IsNullOrEmpty(p))) ; - string rootPath =string.IsNullOrEmpty(AppConfig.ServerOptions.RootPath)? - AppContext.BaseDirectory: AppConfig.ServerOptions.RootPath; - var virPaths = virtualPaths; + var directories = new List(virtualPaths.Where(p => !string.IsNullOrEmpty(p))); + string rootPath = string.IsNullOrEmpty(AppConfig.ServerOptions.RootPath) ? + AppContext.BaseDirectory : AppConfig.ServerOptions.RootPath; + var virPaths = virtualPaths; foreach (var virtualPath in virtualPaths) { - var path = Path.Combine(rootPath, virtualPath); if (_logger.IsEnabled(LogLevel.Debug)) _logger.LogDebug($"准备查找路径{path}下的目录。"); @@ -113,8 +159,10 @@ private string [] GetPaths(params string [] virtualPaths) directories.Remove(virtualPath); virPaths = null; } - } - return directories.Any() ? directories.Distinct().ToArray(): virPaths; + } + return directories.Any() ? directories.Distinct().ToArray() : virPaths; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Engines/Implementation/ServiceEngineLifetime.cs b/src/Surging.Core/Surging.Core.CPlatform/Engines/Implementation/ServiceEngineLifetime.cs index 33546dc80..ad345b033 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Engines/Implementation/ServiceEngineLifetime.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Engines/Implementation/ServiceEngineLifetime.cs @@ -6,24 +6,72 @@ namespace Surging.Core.CPlatform.Engines.Implementation { - public class ServiceEngineLifetime: IServiceEngineLifetime + /// + /// Defines the + /// + public class ServiceEngineLifetime : IServiceEngineLifetime { + #region 字段 + + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the _startedSource + /// private readonly CancellationTokenSource _startedSource = new CancellationTokenSource(); - private readonly CancellationTokenSource _stoppingSource = new CancellationTokenSource(); + + /// + /// Defines the _stoppedSource + /// private readonly CancellationTokenSource _stoppedSource = new CancellationTokenSource(); - private readonly ILogger _logger; + /// + /// Defines the _stoppingSource + /// + private readonly CancellationTokenSource _stoppingSource = new CancellationTokenSource(); + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The logger public ServiceEngineLifetime(ILogger logger) { _logger = logger; } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the ServiceEngineStarted + /// public CancellationToken ServiceEngineStarted => _startedSource.Token; + /// + /// Gets the ServiceEngineStopped + /// + public CancellationToken ServiceEngineStopped => _stoppedSource.Token; + + /// + /// Gets the ServiceEngineStopping + /// public CancellationToken ServiceEngineStopping => _stoppingSource.Token; - public CancellationToken ServiceEngineStopped => _stoppedSource.Token; + #endregion 属性 + + #region 方法 + /// + /// The NotifyStarted + /// public void NotifyStarted() { try @@ -37,7 +85,9 @@ public void NotifyStarted() } } - + /// + /// The NotifyStopped + /// public void NotifyStopped() { try @@ -51,6 +101,9 @@ public void NotifyStopped() } } + /// + /// The StopApplication + /// public void StopApplication() { lock (_stoppingSource) @@ -67,6 +120,10 @@ public void StopApplication() } } + /// + /// The ExecuteHandlers + /// + /// The cancel private void ExecuteHandlers(CancellationTokenSource cancel) { if (cancel.IsCancellationRequested) @@ -75,5 +132,7 @@ private void ExecuteHandlers(CancellationTokenSource cancel) } cancel.Cancel(throwOnFirstException: false); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Engines/Implementation/VirtualPathProviderServiceEngine .cs b/src/Surging.Core/Surging.Core.CPlatform/Engines/Implementation/VirtualPathProviderServiceEngine .cs index 29d9e6d11..a6a321afa 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Engines/Implementation/VirtualPathProviderServiceEngine .cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Engines/Implementation/VirtualPathProviderServiceEngine .cs @@ -4,10 +4,23 @@ namespace Surging.Core.CPlatform.Engines.Implementation { - public abstract class VirtualPathProviderServiceEngine: IServiceEngine + /// + /// Defines the + /// + public abstract class VirtualPathProviderServiceEngine : IServiceEngine { - public string[] ModuleServiceLocationFormats { get; set; } + #region 属性 + /// + /// Gets or sets the ComponentServiceLocationFormats + /// public string[] ComponentServiceLocationFormats { get; set; } + + /// + /// Gets or sets the ModuleServiceLocationFormats + /// + public string[] ModuleServiceLocationFormats { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/EventBus/Events/EventContext.cs b/src/Surging.Core/Surging.Core.CPlatform/EventBus/Events/EventContext.cs index 298a9177b..908886c3a 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/EventBus/Events/EventContext.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/EventBus/Events/EventContext.cs @@ -4,13 +4,28 @@ namespace Surging.Core.CPlatform.EventBus.Events { - public class EventContext: IntegrationEvent + /// + /// Defines the + /// + public class EventContext : IntegrationEvent { - + #region 属性 + + /// + /// Gets or sets the Content + /// public object Content { get; set; } + /// + /// Gets or sets the Count + /// public long Count { get; set; } + /// + /// Gets or sets the Type + /// public string Type { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/EventBus/Events/IIntegrationEventHandler.cs b/src/Surging.Core/Surging.Core.CPlatform/EventBus/Events/IIntegrationEventHandler.cs index 2ded3a2fd..be9a4ec8e 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/EventBus/Events/IIntegrationEventHandler.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/EventBus/Events/IIntegrationEventHandler.cs @@ -5,22 +5,60 @@ namespace Surging.Core.CPlatform.EventBus.Events { + #region 接口 + + /// + /// Defines the + /// + /// public interface IIntegrationEventHandler : IIntegrationEventHandler { + #region 方法 + + /// + /// The Handle + /// + /// The event + /// The Task Handle(TIntegrationEvent @event); + + #endregion 方法 + } + + /// + /// Defines the + /// + public interface IIntegrationEventHandler + { } + #endregion 接口 + + /// + /// Defines the + /// + /// public abstract class BaseIntegrationEventHandler : IIntegrationEventHandler { + #region 方法 + + /// + /// The Handle + /// + /// The event + /// The public abstract Task Handle(TIntegrationEvent @event); - public virtual async Task Handled(EventContext context) + /// + /// The Handled + /// + /// The context + /// The + public virtual async Task Handled(EventContext context) { await Task.CompletedTask; } - } - public interface IIntegrationEventHandler - { + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/EventBus/Events/IntegrationEvent.cs b/src/Surging.Core/Surging.Core.CPlatform/EventBus/Events/IntegrationEvent.cs index 4cdf6b9d7..e5f36a4e7 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/EventBus/Events/IntegrationEvent.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/EventBus/Events/IntegrationEvent.cs @@ -4,21 +4,46 @@ namespace Surging.Core.CPlatform.EventBus.Events { + /// + /// Defines the + /// public class IntegrationEvent { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public IntegrationEvent() { Id = Guid.NewGuid(); CreationDate = DateTime.UtcNow; } + /// + /// Initializes a new instance of the class. + /// + /// The integrationEvent public IntegrationEvent(IntegrationEvent integrationEvent) { Id = integrationEvent.Id; CreationDate = integrationEvent.CreationDate; } - public Guid Id { get; } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the CreationDate + /// public DateTime CreationDate { get; } + + /// + /// Gets the Id + /// + public Guid Id { get; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/EventBus/IEventBusSubscriptionsManager.cs b/src/Surging.Core/Surging.Core.CPlatform/EventBus/IEventBusSubscriptionsManager.cs index 73e8ef6ff..0515bc165 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/EventBus/IEventBusSubscriptionsManager.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/EventBus/IEventBusSubscriptionsManager.cs @@ -5,21 +5,93 @@ namespace Surging.Core.CPlatform.EventBus { - public interface IEventBusSubscriptionsManager + #region 接口 + + /// + /// Defines the + /// + public interface IEventBusSubscriptionsManager { + #region 事件 + + /// + /// Defines the OnEventRemoved + /// + event EventHandler> OnEventRemoved; + + #endregion 事件 + + #region 属性 + + /// + /// Gets a value indicating whether IsEmpty + /// bool IsEmpty { get; } - event EventHandler> OnEventRemoved; + #endregion 属性 + + #region 方法 + + /// + /// The AddSubscription + /// + /// + /// + /// The handler + /// The consumerName void AddSubscription(Func handler, string consumerName) where TH : IIntegrationEventHandler; - void RemoveSubscription() - where TH : IIntegrationEventHandler; - bool HasSubscriptionsForEvent(); - bool HasSubscriptionsForEvent(string eventName); - Type GetEventTypeByName(string eventName); + /// + /// The Clear + /// void Clear(); + + /// + /// The GetEventTypeByName + /// + /// The eventName + /// The + Type GetEventTypeByName(string eventName); + + /// + /// The GetHandlersForEvent + /// + /// + /// The IEnumerable GetHandlersForEvent() where T : IntegrationEvent; + + /// + /// The GetHandlersForEvent + /// + /// The eventName + /// The IEnumerable GetHandlersForEvent(string eventName); + + /// + /// The HasSubscriptionsForEvent + /// + /// + /// The + bool HasSubscriptionsForEvent(); + + /// + /// The HasSubscriptionsForEvent + /// + /// The eventName + /// The + bool HasSubscriptionsForEvent(string eventName); + + /// + /// The RemoveSubscription + /// + /// + /// + void RemoveSubscription() + where TH : IIntegrationEventHandler; + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/EventBus/ISubscriptionAdapt.cs b/src/Surging.Core/Surging.Core.CPlatform/EventBus/ISubscriptionAdapt.cs index f7adefd76..04d926d6e 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/EventBus/ISubscriptionAdapt.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/EventBus/ISubscriptionAdapt.cs @@ -4,10 +4,27 @@ namespace Surging.Core.CPlatform.EventBus { - public interface ISubscriptionAdapt + #region 接口 + + /// + /// Defines the + /// + public interface ISubscriptionAdapt { + #region 方法 + + /// + /// The SubscribeAt + /// void SubscribeAt(); + /// + /// The Unsubscribe + /// void Unsubscribe(); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/EventBus/Implementation/IEventBus.cs b/src/Surging.Core/Surging.Core.CPlatform/EventBus/Implementation/IEventBus.cs index 005fd765d..ddd3256ae 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/EventBus/Implementation/IEventBus.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/EventBus/Implementation/IEventBus.cs @@ -5,15 +5,49 @@ namespace Surging.Core.CPlatform.EventBus.Implementation { - public interface IEventBus + #region 接口 + + /// + /// Defines the + /// + public interface IEventBus { + #region 事件 + + /// + /// Defines the OnShutdown + /// + event EventHandler OnShutdown; + + #endregion 事件 + + #region 方法 + + /// + /// The Publish + /// + /// The event + void Publish(IntegrationEvent @event); + + /// + /// The Subscribe + /// + /// + /// + /// The handler void Subscribe(Func handler) where TH : IIntegrationEventHandler; + + /// + /// The Unsubscribe + /// + /// + /// void Unsubscribe() where TH : IIntegrationEventHandler; - void Publish(IntegrationEvent @event); - - event EventHandler OnShutdown; + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/EventBus/InMemoryEventBusSubscriptionsManager.cs b/src/Surging.Core/Surging.Core.CPlatform/EventBus/InMemoryEventBusSubscriptionsManager.cs index fdb930561..a92162dad 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/EventBus/InMemoryEventBusSubscriptionsManager.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/EventBus/InMemoryEventBusSubscriptionsManager.cs @@ -7,14 +7,35 @@ namespace Surging.Core.CPlatform.EventBus { - public class InMemoryEventBusSubscriptionsManager : IEventBusSubscriptionsManager + /// + /// Defines the + /// + public class InMemoryEventBusSubscriptionsManager : IEventBusSubscriptionsManager { - private readonly Dictionary> _handlers; + #region 字段 + + /// + /// Defines the _consumers + /// private readonly Dictionary _consumers; + + /// + /// Defines the _eventTypes + /// private readonly List _eventTypes; - public event EventHandler> OnEventRemoved; + /// + /// Defines the _handlers + /// + private readonly Dictionary> _handlers; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public InMemoryEventBusSubscriptionsManager() { _handlers = new Dictionary>(); @@ -22,10 +43,36 @@ public InMemoryEventBusSubscriptionsManager() _eventTypes = new List(); } + #endregion 构造函数 + + #region 事件 + + /// + /// Defines the OnEventRemoved + /// + public event EventHandler> OnEventRemoved; + + #endregion 事件 + + #region 属性 + + /// + /// Gets a value indicating whether IsEmpty + /// public bool IsEmpty => !_handlers.Keys.Any(); - public void Clear() => _handlers.Clear(); - public void AddSubscription(Func handler,string consumerName) + #endregion 属性 + + #region 方法 + + /// + /// The AddSubscription + /// + /// + /// + /// The handler + /// The consumerName + public void AddSubscription(Func handler, string consumerName) where TH : IIntegrationEventHandler { var key = GetEventKey(); @@ -38,6 +85,59 @@ public void AddSubscription(Func handler,string consumerName) _eventTypes.Add(typeof(T)); } + /// + /// The Clear + /// + public void Clear() => _handlers.Clear(); + + /// + /// The GetEventTypeByName + /// + /// The eventName + /// The + public Type GetEventTypeByName(string eventName) => _eventTypes.Single(t => t.Name == eventName); + + /// + /// The GetHandlersForEvent + /// + /// + /// The + public IEnumerable GetHandlersForEvent() where T : IntegrationEvent + { + var key = GetEventKey(); + return GetHandlersForEvent(key); + } + + /// + /// The GetHandlersForEvent + /// + /// The eventName + /// The + public IEnumerable GetHandlersForEvent(string eventName) => _handlers[eventName]; + + /// + /// The HasSubscriptionsForEvent + /// + /// + /// The + public bool HasSubscriptionsForEvent() + { + var key = GetEventKey(); + return HasSubscriptionsForEvent(key); + } + + /// + /// The HasSubscriptionsForEvent + /// + /// The eventName + /// The + public bool HasSubscriptionsForEvent(string eventName) => _handlers.ContainsKey(eventName); + + /// + /// The RemoveSubscription + /// + /// + /// public void RemoveSubscription() where TH : IIntegrationEventHandler { @@ -55,29 +155,18 @@ public void RemoveSubscription() { _eventTypes.Remove(eventType); _consumers.Remove(handlerToRemove); - RaiseOnEventRemoved(eventType.Name,consumerName); + RaiseOnEventRemoved(eventType.Name, consumerName); } } - - } - } - - public IEnumerable GetHandlersForEvent() where T : IntegrationEvent - { - var key = GetEventKey(); - return GetHandlersForEvent(key); - } - public IEnumerable GetHandlersForEvent(string eventName) => _handlers[eventName]; - - private void RaiseOnEventRemoved(string eventName,string consumerName) - { - var handler = OnEventRemoved; - if (handler != null) - { - handler(this,new ValueTuple(consumerName, eventName)); } } + /// + /// The FindHandlerToRemove + /// + /// + /// + /// The private Delegate FindHandlerToRemove() where TH : IIntegrationEventHandler { @@ -99,18 +188,30 @@ private Delegate FindHandlerToRemove() return null; } - public bool HasSubscriptionsForEvent() + /// + /// The GetEventKey + /// + /// + /// The + private string GetEventKey() { - var key = GetEventKey(); - return HasSubscriptionsForEvent(key); + return typeof(T).Name; } - public bool HasSubscriptionsForEvent(string eventName) => _handlers.ContainsKey(eventName); - - public Type GetEventTypeByName(string eventName) => _eventTypes.Single(t => t.Name == eventName); - private string GetEventKey() + /// + /// The RaiseOnEventRemoved + /// + /// The eventName + /// The consumerName + private void RaiseOnEventRemoved(string eventName, string consumerName) { - return typeof(T).Name; + var handler = OnEventRemoved; + if (handler != null) + { + handler(this, new ValueTuple(consumerName, eventName)); + } } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Exceptions/CPlatformException.cs b/src/Surging.Core/Surging.Core.CPlatform/Exceptions/CPlatformException.cs index ce9ac4c0d..5699c9cec 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Exceptions/CPlatformException.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Exceptions/CPlatformException.cs @@ -7,13 +7,17 @@ namespace Surging.Core.CPlatform.Exceptions /// public class CPlatformException : Exception { + #region 构造函数 + /// - /// 初始化构造函数 + /// Initializes a new instance of the class. /// /// 异常消息。 /// 内部异常。 public CPlatformException(string message, Exception innerException = null) : base(message, innerException) { } + + #endregion 构造函数 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Exceptions/CPlatformRemoteException.cs b/src/Surging.Core/Surging.Core.CPlatform/Exceptions/CPlatformRemoteException.cs index f3e0250fb..caf508597 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Exceptions/CPlatformRemoteException.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Exceptions/CPlatformRemoteException.cs @@ -7,15 +7,19 @@ namespace Surging.Core.CPlatform.Exceptions /// public class CPlatformCommunicationException : CPlatformException { + #region 构造函数 + /// - /// 初始化构造函数 + /// Initializes a new instance of the class. /// /// 异常消息。 + /// The StatusCode /// 内部异常。 - public CPlatformCommunicationException(string message,int StatusCode=0, Exception innerException = null) : base(message, innerException) + public CPlatformCommunicationException(string message, int StatusCode = 0, Exception innerException = null) : base(message, innerException) { base.HResult = StatusCode; } + #endregion 构造函数 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Exceptions/CommunicationException.cs b/src/Surging.Core/Surging.Core.CPlatform/Exceptions/CommunicationException.cs index a8ed26dd4..9d4c5a09a 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Exceptions/CommunicationException.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Exceptions/CommunicationException.cs @@ -7,13 +7,17 @@ namespace Surging.Core.CPlatform.Exceptions /// public class CommunicationException : CPlatformException { + #region 构造函数 + /// - /// 初始构造函数 + /// Initializes a new instance of the class. /// /// 异常消息。 /// 内部异常。 public CommunicationException(string message, Exception innerException = null) : base(message, innerException) { } + + #endregion 构造函数 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Exceptions/RegisterConnectionException.cs b/src/Surging.Core/Surging.Core.CPlatform/Exceptions/RegisterConnectionException.cs index f677ef482..32f4a22ca 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Exceptions/RegisterConnectionException.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Exceptions/RegisterConnectionException.cs @@ -4,10 +4,22 @@ namespace Surging.Core.CPlatform.Exceptions { + /// + /// Defines the + /// public class RegisterConnectionException : CPlatformException { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The message + /// The innerException public RegisterConnectionException(string message, Exception innerException = null) : base(message, innerException) { } + + #endregion 构造函数 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Filters/IAuthorizationFilter.cs b/src/Surging.Core/Surging.Core.CPlatform/Filters/IAuthorizationFilter.cs index db7678578..d8d590f74 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Filters/IAuthorizationFilter.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Filters/IAuthorizationFilter.cs @@ -8,8 +8,24 @@ namespace Surging.Core.CPlatform.Filters { - public interface IAuthorizationFilter: IFilter + #region 接口 + + /// + /// Defines the + /// + public interface IAuthorizationFilter : IFilter { - void ExecuteAuthorizationFilterAsync(ServiceRouteContext serviceRouteContext,CancellationToken cancellationToken); + #region 方法 + + /// + /// The ExecuteAuthorizationFilterAsync + /// + /// The serviceRouteContext + /// The cancellationToken + void ExecuteAuthorizationFilterAsync(ServiceRouteContext serviceRouteContext, CancellationToken cancellationToken); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Filters/IExceptionFilter.cs b/src/Surging.Core/Surging.Core.CPlatform/Filters/IExceptionFilter.cs index 62a05fafd..95252a927 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Filters/IExceptionFilter.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Filters/IExceptionFilter.cs @@ -7,8 +7,25 @@ namespace Surging.Core.CPlatform.Filters { - public interface IExceptionFilter: IFilter + #region 接口 + + /// + /// Defines the + /// + public interface IExceptionFilter : IFilter { + #region 方法 + + /// + /// The ExecuteExceptionFilterAsync + /// + /// The actionExecutedContext + /// The cancellationToken + /// The Task ExecuteExceptionFilterAsync(RpcActionExecutedContext actionExecutedContext, CancellationToken cancellationToken); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Filters/IFilter.cs b/src/Surging.Core/Surging.Core.CPlatform/Filters/IFilter.cs index a4112f7db..772fc45a7 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Filters/IFilter.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Filters/IFilter.cs @@ -4,9 +4,22 @@ namespace Surging.Core.CPlatform.Filters { - public interface IFilter + #region 接口 + + /// + /// Defines the + /// + public interface IFilter { + #region 属性 + + /// + /// Gets a value indicating whether AllowMultiple + /// + bool AllowMultiple { get; } - bool AllowMultiple { get; } + #endregion 属性 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Filters/Implementation/AuthorizationAttribute.cs b/src/Surging.Core/Surging.Core.CPlatform/Filters/Implementation/AuthorizationAttribute.cs index 7fe3f15e0..aed7d0b55 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Filters/Implementation/AuthorizationAttribute.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Filters/Implementation/AuthorizationAttribute.cs @@ -5,8 +5,18 @@ namespace Surging.Core.CPlatform.Filters.Implementation { - public class AuthorizationAttribute : AuthorizationFilterAttribute + /// + /// Defines the + /// + public class AuthorizationAttribute : AuthorizationFilterAttribute { + #region 属性 + + /// + /// Gets or sets the AuthType + /// public AuthorizationType AuthType { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Filters/Implementation/AuthorizationFilterAttribute.cs b/src/Surging.Core/Surging.Core.CPlatform/Filters/Implementation/AuthorizationFilterAttribute.cs index ea246f2be..66d3f901c 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Filters/Implementation/AuthorizationFilterAttribute.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Filters/Implementation/AuthorizationFilterAttribute.cs @@ -1,20 +1,25 @@ using Surging.Core.CPlatform.Routing; using System; using System.Collections.Generic; +using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; -using System.Linq; namespace Surging.Core.CPlatform.Filters.Implementation { - public abstract class AuthorizationFilterAttribute : FilterAttribute,IAuthorizationFilter, IFilter + /// + /// Defines the + /// + public abstract class AuthorizationFilterAttribute : FilterAttribute, IAuthorizationFilter, IFilter { - public virtual bool OnAuthorization(ServiceRouteContext context) - { - return true; - } + #region 方法 + /// + /// The ExecuteAuthorizationFilterAsync + /// + /// The serviceRouteContext + /// The cancellationToken public virtual void ExecuteAuthorizationFilterAsync(ServiceRouteContext serviceRouteContext, CancellationToken cancellationToken) { var result = OnAuthorization(serviceRouteContext); @@ -24,5 +29,17 @@ public virtual void ExecuteAuthorizationFilterAsync(ServiceRouteContext serviceR serviceRouteContext.ResultMessage.ExceptionMessage = "令牌验证失败."; } } + + /// + /// The OnAuthorization + /// + /// The context + /// The + public virtual bool OnAuthorization(ServiceRouteContext context) + { + return true; + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Filters/Implementation/AuthorizationType.cs b/src/Surging.Core/Surging.Core.CPlatform/Filters/Implementation/AuthorizationType.cs index d89332e63..e5bbde928 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Filters/Implementation/AuthorizationType.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Filters/Implementation/AuthorizationType.cs @@ -4,9 +4,23 @@ namespace Surging.Core.CPlatform.Filters.Implementation { + #region 枚举 + + /// + /// Defines the AuthorizationType + /// public enum AuthorizationType { + /// + /// Defines the JWT + /// JWT, + + /// + /// Defines the AppSecret + /// AppSecret } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Filters/Implementation/ExceptionFilterAttribute.cs b/src/Surging.Core/Surging.Core.CPlatform/Filters/Implementation/ExceptionFilterAttribute.cs index d3cbbdde0..770896075 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Filters/Implementation/ExceptionFilterAttribute.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Filters/Implementation/ExceptionFilterAttribute.cs @@ -8,12 +8,27 @@ namespace Surging.Core.CPlatform.Filters.Implementation { + /// + /// Defines the + /// public abstract class ExceptionFilterAttribute : FilterAttribute, IExceptionFilter, IFilter { + #region 方法 + + /// + /// The OnException + /// + /// The actionExecutedContext public virtual void OnException(RpcActionExecutedContext actionExecutedContext) { - } + + /// + /// The OnExceptionAsync + /// + /// The actionExecutedContext + /// The cancellationToken + /// The public virtual Task OnExceptionAsync(RpcActionExecutedContext actionExecutedContext, CancellationToken cancellationToken) { try @@ -28,15 +43,29 @@ public virtual Task OnExceptionAsync(RpcActionExecutedContext actionExecutedCont return TaskHelpers.Completed(); } + /// + /// The ExecuteExceptionFilterAsyncCore + /// + /// The actionExecutedContext + /// The cancellationToken + /// The + private async Task ExecuteExceptionFilterAsyncCore(RpcActionExecutedContext actionExecutedContext, CancellationToken cancellationToken) + { + await OnExceptionAsync(actionExecutedContext, cancellationToken); + } + + /// + /// The ExecuteExceptionFilterAsync + /// + /// The actionExecutedContext + /// The cancellationToken + /// The Task IExceptionFilter.ExecuteExceptionFilterAsync(RpcActionExecutedContext actionExecutedContext, CancellationToken cancellationToken) { Check.NotNull(actionExecutedContext, "actionExecutedContext"); return ExecuteExceptionFilterAsyncCore(actionExecutedContext, cancellationToken); } - private async Task ExecuteExceptionFilterAsyncCore(RpcActionExecutedContext actionExecutedContext, CancellationToken cancellationToken) - { - await OnExceptionAsync(actionExecutedContext, cancellationToken); - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Filters/Implementation/FilterAttribute.cs b/src/Surging.Core/Surging.Core.CPlatform/Filters/Implementation/FilterAttribute.cs index a959bd1db..b7b79be7e 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Filters/Implementation/FilterAttribute.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Filters/Implementation/FilterAttribute.cs @@ -4,13 +4,39 @@ namespace Surging.Core.CPlatform.Filters.Implementation { + /// + /// Defines the + /// public abstract class FilterAttribute : Attribute, IFilter { + #region 字段 + + /// + /// Defines the _filterAttribute + /// private readonly bool _filterAttribute; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// protected FilterAttribute() { _filterAttribute = true; } + + #endregion 构造函数 + + #region 属性 + + /// + /// Gets a value indicating whether AllowMultiple + /// public virtual bool AllowMultiple { get => _filterAttribute; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Filters/Implementation/RpcActionExecutedContext.cs b/src/Surging.Core/Surging.Core.CPlatform/Filters/Implementation/RpcActionExecutedContext.cs index 8d699bb2b..d5438a45d 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Filters/Implementation/RpcActionExecutedContext.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Filters/Implementation/RpcActionExecutedContext.cs @@ -5,11 +5,23 @@ namespace Surging.Core.CPlatform.Filters.Implementation { + /// + /// Defines the + /// public class RpcActionExecutedContext { + #region 属性 - public RemoteInvokeMessage InvokeMessage { get; set; } - + /// + /// Gets or sets the Exception + /// public Exception Exception { get; set; } + + /// + /// Gets or sets the InvokeMessage + /// + public RemoteInvokeMessage InvokeMessage { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/HashAlgorithms/ConsistentHash.cs b/src/Surging.Core/Surging.Core.CPlatform/HashAlgorithms/ConsistentHash.cs index ea8574321..f9469b57d 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/HashAlgorithms/ConsistentHash.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/HashAlgorithms/ConsistentHash.cs @@ -8,70 +8,79 @@ namespace Surging.Core.CPlatform.HashAlgorithms /// 针对哈希算法实现 /// /// 类型 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public class ConsistentHash { #region 字段 - private readonly SortedDictionary _ring = new SortedDictionary(); - private int[] _nodeKeysInRing = null; + + /// + /// Defines the _hashAlgorithm + /// private readonly IHashAlgorithm _hashAlgorithm; + + /// + /// Defines the _ring + /// + private readonly SortedDictionary _ring = new SortedDictionary(); + + /// + /// Defines the _virtualNodeReplicationFactor + /// private readonly int _virtualNodeReplicationFactor = 1000; - #endregion + /// + /// Defines the _nodeKeysInRing + /// + private int[] _nodeKeysInRing = null; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The hashAlgorithm public ConsistentHash(IHashAlgorithm hashAlgorithm) { _hashAlgorithm = hashAlgorithm; } + /// + /// Initializes a new instance of the class. + /// + /// The hashAlgorithm + /// The virtualNodeReplicationFactor public ConsistentHash(IHashAlgorithm hashAlgorithm, int virtualNodeReplicationFactor) : this(hashAlgorithm) { _virtualNodeReplicationFactor = virtualNodeReplicationFactor; } + #endregion 构造函数 + #region 属性 + /// + /// Gets the VirtualNodeReplicationFactor /// 复制哈希节点数 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public int VirtualNodeReplicationFactor { get { return _virtualNodeReplicationFactor; } } - #endregion - + + #endregion 属性 + + #region 方法 /// /// 添加节点 /// /// 节点 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public void Add(T node,string value) + /// The value + public void Add(T node, string value) { - AddNode(node,value); - _nodeKeysInRing = _ring.Keys.ToArray(); - } - - /// - /// 删除节点 - /// - /// 节点 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public void Remove(string node) - { - RemoveNode(node); + AddNode(node, value); _nodeKeysInRing = _ring.Keys.ToArray(); } @@ -80,10 +89,6 @@ public void Remove(string node) /// /// 值 /// 返回节点 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public T GetItemNode(string item) { var hashOfItem = _hashAlgorithm.Hash(item); @@ -92,50 +97,35 @@ public T GetItemNode(string item) } /// - /// 添加节点 + /// 删除节点 /// /// 节点 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - private void AddNode(T node,string value) + public void Remove(string node) { - for (var i = 0; i < _virtualNodeReplicationFactor; i++) - { - var hashOfVirtualNode = _hashAlgorithm.Hash(value.ToString(CultureInfo.InvariantCulture) + i); - _ring[hashOfVirtualNode] = node; - } + RemoveNode(node); + _nodeKeysInRing = _ring.Keys.ToArray(); } /// - /// 删除节点 + /// 添加节点 /// /// 节点 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - private void RemoveNode(string value) + /// The value + private void AddNode(T node, string value) { for (var i = 0; i < _virtualNodeReplicationFactor; i++) { - var hashOfVirtualNode = _hashAlgorithm.Hash(value.ToString() + i); - _ring.Remove(hashOfVirtualNode); + var hashOfVirtualNode = _hashAlgorithm.Hash(value.ToString(CultureInfo.InvariantCulture) + i); + _ring[hashOfVirtualNode] = node; } } - /// /// 顺时针查找对应哈希的位置 /// /// 键集合数 /// 哈希值 /// 返回哈希的位置 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// private int GetClockwiseNearestNode(int[] keys, int hashOfItem) { var begin = 0; @@ -153,5 +143,20 @@ private int GetClockwiseNearestNode(int[] keys, int hashOfItem) } return end; } + + /// + /// 删除节点 + /// + /// The value + private void RemoveNode(string value) + { + for (var i = 0; i < _virtualNodeReplicationFactor; i++) + { + var hashOfVirtualNode = _hashAlgorithm.Hash(value.ToString() + i); + _ring.Remove(hashOfVirtualNode); + } + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/HashAlgorithms/HashAlgorithm.cs b/src/Surging.Core/Surging.Core.CPlatform/HashAlgorithms/HashAlgorithm.cs index 5255bca5e..f7fa48b67 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/HashAlgorithms/HashAlgorithm.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/HashAlgorithms/HashAlgorithm.cs @@ -7,26 +7,30 @@ namespace Surging.Core.CPlatform.HashAlgorithms /// /// 一致性哈希算法 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public class HashAlgorithm : IHashAlgorithm { - #region 构造函数 - public int Hash(string item) - { - var hash = Hash(Encoding.ASCII.GetBytes(item??"")); - return (int)hash; - } - #endregion - #region 常量 + + /// + /// Defines the m + /// private const UInt32 m = 0x5bd1e995; + + /// + /// Defines the r + /// private const Int32 r = 24; - #endregion - #region 公共方法 + #endregion 常量 + + #region 方法 + + /// + /// The Hash + /// + /// The data + /// The seed + /// The public static UInt32 Hash(Byte[] data, UInt32 seed = 0xc58f1a7b) { var length = data.Length; @@ -56,14 +60,17 @@ public static UInt32 Hash(Byte[] data, UInt32 seed = 0xc58f1a7b) h ^= (UInt32)(data[c] << 16); h *= m; break; + case 2: h ^= (UInt16)(data[c++] | data[c] << 8); h *= m; break; + case 1: h ^= data[c]; h *= m; break; + default: break; } @@ -73,6 +80,18 @@ public static UInt32 Hash(Byte[] data, UInt32 seed = 0xc58f1a7b) h ^= h >> 15; return h; } - #endregion + + /// + /// The Hash + /// + /// The item + /// The + public int Hash(string item) + { + var hash = Hash(Encoding.ASCII.GetBytes(item ?? "")); + return (int)hash; + } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/HashAlgorithms/IHashAlgorithm.cs b/src/Surging.Core/Surging.Core.CPlatform/HashAlgorithms/IHashAlgorithm.cs index 55eeec551..64f118a0c 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/HashAlgorithms/IHashAlgorithm.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/HashAlgorithms/IHashAlgorithm.cs @@ -4,24 +4,24 @@ namespace Surging.Core.CPlatform.HashAlgorithms { + #region 接口 + /// /// 一致性哈希的抽象接口 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public interface IHashAlgorithm { + #region 方法 + /// /// 获取哈希值 /// /// 字符串 /// 返回哈希值 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// int Hash(string item); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/IdentifyAttribute.cs b/src/Surging.Core/Surging.Core.CPlatform/IdentifyAttribute.cs index dd44f26f2..5dc558bac 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/IdentifyAttribute.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/IdentifyAttribute.cs @@ -4,15 +4,32 @@ namespace Surging.Core.CPlatform { + /// + /// Defines the + /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)] public class IdentifyAttribute : Attribute { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The name public IdentifyAttribute(CommunicationProtocol name) { this.Name = name; } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets or sets the Name + /// public CommunicationProtocol Name { get; set; } - } -} + #endregion 属性 + } +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Ids/IServiceIdGenerator.cs b/src/Surging.Core/Surging.Core.CPlatform/Ids/IServiceIdGenerator.cs index c511dc4a6..7e2c6f793 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Ids/IServiceIdGenerator.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Ids/IServiceIdGenerator.cs @@ -2,16 +2,24 @@ namespace Surging.Core.CPlatform.Ids { + #region 接口 + /// /// 一个抽象的服务Id生成器。 /// public interface IServiceIdGenerator { + #region 方法 + /// /// 生成一个服务Id。 /// /// 本地方法信息。 /// 对应方法的唯一服务Id。 string GenerateServiceId(MethodInfo method); + + #endregion 方法 } + + #endregion 接口 } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Ids/Implementation/DefaultServiceIdGenerator.cs b/src/Surging.Core/Surging.Core.CPlatform/Ids/Implementation/DefaultServiceIdGenerator.cs index d19396563..11905e9e2 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Ids/Implementation/DefaultServiceIdGenerator.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Ids/Implementation/DefaultServiceIdGenerator.cs @@ -10,14 +10,29 @@ namespace Surging.Core.CPlatform.Ids.Implementation /// public class DefaultServiceIdGenerator : IServiceIdGenerator { + #region 字段 + + /// + /// Defines the _logger + /// private readonly ILogger _logger; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The logger public DefaultServiceIdGenerator(ILogger logger) { _logger = logger; } - #region Implementation of IServiceIdFactory + #endregion 构造函数 + + #region 方法 /// /// 生成一个服务Id。 @@ -43,6 +58,6 @@ public string GenerateServiceId(MethodInfo method) return id; } - #endregion Implementation of IServiceIdFactory + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Ioc/BaseRepository.cs b/src/Surging.Core/Surging.Core.CPlatform/Ioc/BaseRepository.cs index 8514405b3..79e3152e7 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Ioc/BaseRepository.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Ioc/BaseRepository.cs @@ -4,7 +4,10 @@ namespace Surging.Core.CPlatform.Ioc { + /// + /// Defines the + /// public abstract class BaseRepository { } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Ioc/IServiceBehavior.cs b/src/Surging.Core/Surging.Core.CPlatform/Ioc/IServiceBehavior.cs index 1e85b50cf..1377300ff 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Ioc/IServiceBehavior.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Ioc/IServiceBehavior.cs @@ -4,7 +4,14 @@ namespace Surging.Core.CPlatform.Ioc { - public interface IServiceBehavior + #region 接口 + + /// + /// Defines the + /// + public interface IServiceBehavior { } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Ioc/IServiceKey.cs b/src/Surging.Core/Surging.Core.CPlatform/Ioc/IServiceKey.cs index 48c1305f0..03568c28c 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Ioc/IServiceKey.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Ioc/IServiceKey.cs @@ -4,7 +4,14 @@ namespace Surging.Core.CPlatform.Ioc { + #region 接口 + + /// + /// Defines the + /// public interface IServiceKey { } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Ioc/ISingletonDependency.cs b/src/Surging.Core/Surging.Core.CPlatform/Ioc/ISingletonDependency.cs index db67b9ca0..d7d2c9070 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Ioc/ISingletonDependency.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Ioc/ISingletonDependency.cs @@ -1,7 +1,13 @@ - -namespace Surging.Core.CPlatform.Ioc +namespace Surging.Core.CPlatform.Ioc { + #region 接口 + + /// + /// Defines the + /// public interface ISingletonDependency { } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Ioc/ITransientDependency.cs b/src/Surging.Core/Surging.Core.CPlatform/Ioc/ITransientDependency.cs index 507ac5440..5537f38e4 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Ioc/ITransientDependency.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Ioc/ITransientDependency.cs @@ -1,7 +1,13 @@ - -namespace Surging.Core.CPlatform.Ioc +namespace Surging.Core.CPlatform.Ioc { + #region 接口 + + /// + /// Defines the + /// public interface ITransientDependency { } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Ioc/ModuleNameAttribute.cs b/src/Surging.Core/Surging.Core.CPlatform/Ioc/ModuleNameAttribute.cs index ae900eb13..425c03660 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Ioc/ModuleNameAttribute.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Ioc/ModuleNameAttribute.cs @@ -4,20 +4,44 @@ namespace Surging.Core.CPlatform.Ioc { + /// + /// Defines the + /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] public class ModuleNameAttribute : Attribute { - public string ModuleName { get; set; } - - public string Version { get; set; } + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// public ModuleNameAttribute() { - } + + /// + /// Initializes a new instance of the class. + /// + /// The moduleName public ModuleNameAttribute(string moduleName) { ModuleName = moduleName; } + + #endregion 构造函数 + + #region 属性 + + /// + /// Gets or sets the ModuleName + /// + public string ModuleName { get; set; } + + /// + /// Gets or sets the Version + /// + public string Version { get; set; } + + #endregion 属性 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Ioc/ServiceBase.cs b/src/Surging.Core/Surging.Core.CPlatform/Ioc/ServiceBase.cs index 937910c29..7ad62191a 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Ioc/ServiceBase.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Ioc/ServiceBase.cs @@ -5,26 +5,55 @@ namespace Surging.Core.CPlatform.Ioc { - public abstract class ServiceBase: IServiceBehavior + /// + /// Defines the + /// + public abstract class ServiceBase : IServiceBehavior { + #region 方法 + + /// + /// The GetService + /// + /// + /// The public virtual T GetService() where T : class { return ServiceLocator.GetService(); } + /// + /// The GetService + /// + /// + /// The key + /// The public virtual T GetService(string key) where T : class { return ServiceLocator.GetService(key); } - public virtual object GetService(Type type) + /// + /// The GetService + /// + /// The key + /// The type + /// The + public virtual object GetService(string key, Type type) { - return ServiceLocator.GetService(type); + return ServiceLocator.GetService(key, type); } - public virtual object GetService(string key, Type type) + /// + /// The GetService + /// + /// The type + /// The + public virtual object GetService(Type type) { - return ServiceLocator.GetService(key, type); + return ServiceLocator.GetService(type); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Logging/NullLogger.cs b/src/Surging.Core/Surging.Core.CPlatform/Logging/NullLogger.cs index 4c7dab88d..0eb3afefe 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Logging/NullLogger.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Logging/NullLogger.cs @@ -6,6 +6,7 @@ namespace Surging.Core.CPlatform.Logging /// /// 一个空的日志记录器。 /// + /// public sealed class NullLogger : NullLogger, ILogger { } @@ -15,18 +16,26 @@ public sealed class NullLogger : NullLogger, ILogger /// public class NullLogger : ILogger { + #region 属性 + + /// + /// Gets the Instance + /// public static NullLogger Instance { get; } = new NullLogger(); - #region Implementation of ILogger + #endregion 属性 - /// Writes a log entry. - /// Entry will be written on this level. - /// Id of the event. - /// The entry to be written. Can be also an object. - /// The exception related to this entry. - /// Function to create a string message of the and . - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + #region 方法 + + /// + /// The BeginScope + /// + /// + /// The identifier for the scope. + /// An IDisposable that ends the logical operation scope on dispose. + public IDisposable BeginScope(TState state) { + throw new NotImplementedException(); } /// @@ -39,14 +48,19 @@ public bool IsEnabled(LogLevel logLevel) return false; } - /// Begins a logical operation scope. - /// The identifier for the scope. - /// An IDisposable that ends the logical operation scope on dispose. - public IDisposable BeginScope(TState state) + /// + /// The Log + /// + /// + /// Entry will be written on this level. + /// Id of the event. + /// The entry to be written. Can be also an object. + /// The exception related to this entry. + /// Function to create a string message of the and . + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { - throw new NotImplementedException(); } - #endregion Implementation of ILogger + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Messages/HttpMessage.cs b/src/Surging.Core/Surging.Core.CPlatform/Messages/HttpMessage.cs index 90fd57de8..0fdf22fc1 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Messages/HttpMessage.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Messages/HttpMessage.cs @@ -4,13 +4,28 @@ namespace Surging.Core.CPlatform.Messages { - public class HttpMessage - { + /// + /// Defines the + /// + public class HttpMessage + { + #region 属性 + + /// + /// Gets or sets the Parameters + /// + public IDictionary Parameters { get; set; } + + /// + /// Gets or sets the RoutePath + /// public string RoutePath { get; set; } - - public string ServiceKey { get; set; } + /// + /// Gets or sets the ServiceKey + /// + public string ServiceKey { get; set; } - public IDictionary Parameters { get; set; } + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Messages/HttpResultMessage.cs b/src/Surging.Core/Surging.Core.CPlatform/Messages/HttpResultMessage.cs index 7173104d6..78c8d3b0d 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Messages/HttpResultMessage.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Messages/HttpResultMessage.cs @@ -4,13 +4,24 @@ namespace Surging.Core.CPlatform.Messages { + /// + /// Defines the + /// + /// public class HttpResultMessage : HttpResultMessage { + #region 属性 + /// + /// Gets or sets the Entity /// 数据集 /// public T Entity { get; set; } + #endregion 属性 + + #region 方法 + /// /// 生成自定义服务数据集 /// @@ -42,34 +53,19 @@ public static HttpResultMessage Create(bool successd, T entity) Entity = entity }; } + + #endregion 方法 } + /// + /// Defines the + /// public class HttpResultMessage { - /// - /// 生成错误信息 - /// - /// 返回客户端的消息 - /// 返回服务数据集 - public static HttpResultMessage Error(string message) - { - return new HttpResultMessage() { Message = message, IsSucceed = false }; - } + #region 构造函数 /// - /// 生成服务器数据集 - /// - /// 状态值(true:成功 false:失败) - /// 返回客户端的消息 - /// 错误信息 - /// 返回服务数据集 - public static HttpResultMessage Create(bool success, string successMessage = "", string errorMessage = "") - { - return new HttpResultMessage() { Message = success ? successMessage : errorMessage, IsSucceed = success }; - } - - /// - /// 构造服务数据集 + /// Initializes a new instance of the class. /// public HttpResultMessage() { @@ -77,21 +73,54 @@ public HttpResultMessage() Message = string.Empty; } + #endregion 构造函数 + + #region 属性 + /// + /// Gets or sets a value indicating whether IsSucceed /// 状态值 /// - public bool IsSucceed { get; set; } /// - ///返回客户端的消息 + /// Gets or sets the Message + /// 返回客户端的消息 /// public string Message { get; set; } /// + /// Gets or sets the StatusCode /// 状态码 /// public int StatusCode { get; set; } + + #endregion 属性 + + #region 方法 + + /// + /// 生成服务器数据集 + /// + /// 状态值(true:成功 false:失败) + /// 返回客户端的消息 + /// 错误信息 + /// 返回服务数据集 + public static HttpResultMessage Create(bool success, string successMessage = "", string errorMessage = "") + { + return new HttpResultMessage() { Message = success ? successMessage : errorMessage, IsSucceed = success }; + } + + /// + /// 生成错误信息 + /// + /// 返回客户端的消息 + /// 返回服务数据集 + public static HttpResultMessage Error(string message) + { + return new HttpResultMessage() { Message = message, IsSucceed = false }; + } + + #endregion 方法 } -} - +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Messages/MessagePackTransportMessageType.cs b/src/Surging.Core/Surging.Core.CPlatform/Messages/MessagePackTransportMessageType.cs index 99dc9b30a..8cfa0812f 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Messages/MessagePackTransportMessageType.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Messages/MessagePackTransportMessageType.cs @@ -5,14 +5,33 @@ namespace Surging.Core.CPlatform.Messages { + /// + /// Defines the + /// public class MessagePackTransportMessageType - { - public static string remoteInvokeResultMessageTypeName= typeof(RemoteInvokeResultMessage).FullName; - - public static string remoteInvokeMessageTypeName = typeof(RemoteInvokeMessage).FullName; + { + #region 字段 + /// + /// Defines the httpMessageTypeName + /// public static string httpMessageTypeName = typeof(HttpMessage).FullName; + /// + /// Defines the httpResultMessageTypeName + /// public static string httpResultMessageTypeName = typeof(HttpResultMessage).FullName; + + /// + /// Defines the remoteInvokeMessageTypeName + /// + public static string remoteInvokeMessageTypeName = typeof(RemoteInvokeMessage).FullName; + + /// + /// Defines the remoteInvokeResultMessageTypeName + /// + public static string remoteInvokeResultMessageTypeName = typeof(RemoteInvokeResultMessage).FullName; + + #endregion 字段 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Messages/RemoteInvokeMessage.cs b/src/Surging.Core/Surging.Core.CPlatform/Messages/RemoteInvokeMessage.cs index 6522a786a..86756b6e2 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Messages/RemoteInvokeMessage.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Messages/RemoteInvokeMessage.cs @@ -7,19 +7,35 @@ namespace Surging.Core.CPlatform.Messages /// public class RemoteInvokeMessage { + #region 属性 + /// - /// 服务Id。 + /// Gets or sets the Attachments + /// + public IDictionary Attachments { get; set; } + + /// + /// Gets or sets a value indicating whether DecodeJOject /// - public string ServiceId { get; set; } - public bool DecodeJOject { get; set; } - public string ServiceKey { get; set; } /// + /// Gets or sets the Parameters /// 服务参数。 /// public IDictionary Parameters { get; set; } - public IDictionary Attachments { get; set; } + /// + /// Gets or sets the ServiceId + /// 服务Id。 + /// + public string ServiceId { get; set; } + + /// + /// Gets or sets the ServiceKey + /// + public string ServiceKey { get; set; } + + #endregion 属性 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Messages/RemoteInvokeResultMessage.cs b/src/Surging.Core/Surging.Core.CPlatform/Messages/RemoteInvokeResultMessage.cs index 5a2206126..13f6606ca 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Messages/RemoteInvokeResultMessage.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Messages/RemoteInvokeResultMessage.cs @@ -5,18 +5,26 @@ /// public class RemoteInvokeResultMessage { + #region 属性 + /// + /// Gets or sets the ExceptionMessage /// 异常消息。 /// public string ExceptionMessage { get; set; } /// - /// 状态码 - /// - public int StatusCode { get; set; } = 200; - /// + /// Gets or sets the Result /// 结果内容。 /// public object Result { get; set; } + + /// + /// Gets or sets the StatusCode + /// 状态码 + /// + public int StatusCode { get; set; } = 200; + + #endregion 属性 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Messages/TransportMessage.cs b/src/Surging.Core/Surging.Core.CPlatform/Messages/TransportMessage.cs index 23468c02a..a059b20d8 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Messages/TransportMessage.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Messages/TransportMessage.cs @@ -8,10 +8,19 @@ namespace Surging.Core.CPlatform.Messages /// public class TransportMessage { + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// public TransportMessage() { } + + /// + /// Initializes a new instance of the class. + /// + /// The content [MethodImpl(MethodImplOptions.AggressiveInlining)] public TransportMessage(object content) { @@ -21,6 +30,12 @@ public TransportMessage(object content) Content = content; ContentType = content.GetType().FullName; } + + /// + /// Initializes a new instance of the class. + /// + /// The content + /// The fullName [MethodImpl(MethodImplOptions.AggressiveInlining)] public TransportMessage(object content, string fullName) { @@ -31,47 +46,84 @@ public TransportMessage(object content, string fullName) ContentType = fullName; } - /// - /// 消息Id。 - /// - public string Id { get; set; } + #endregion 构造函数 + + #region 属性 /// + /// Gets or sets the Content /// 消息内容。 /// public object Content { get; set; } /// + /// Gets or sets the ContentType /// 内容类型。 /// public string ContentType { get; set; } /// - /// 是否调用消息。 + /// Gets or sets the Id + /// 消息Id。 /// - /// 如果是则返回true,否则返回false。 - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool IsInvokeMessage() + public string Id { get; set; } + + #endregion 属性 + + #region 方法 + + /// + /// 创建一个调用传输消息。 + /// + /// 调用实例。 + /// 调用传输消息。 + public static TransportMessage CreateInvokeMessage(RemoteInvokeMessage invokeMessage) { - return ContentType == MessagePackTransportMessageType.remoteInvokeMessageTypeName; + return new TransportMessage(invokeMessage, MessagePackTransportMessageType.remoteInvokeMessageTypeName) + { + Id = Guid.NewGuid().ToString("N") + }; } /// - /// 是否是调用结果消息。 + /// 创建一个调用结果传输消息。 /// - /// 如果是则返回true,否则返回false。 + /// 消息Id。 + /// 调用结果实例。 + /// 调用结果传输消息。 + public static TransportMessage CreateInvokeResultMessage(string id, RemoteInvokeResultMessage invokeResultMessage) + { + return new TransportMessage(invokeResultMessage, MessagePackTransportMessageType.remoteInvokeResultMessageTypeName) + { + Id = id + }; + } + + /// + /// 获取内容。 + /// + /// 内容类型。 + /// 内容实例。 [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool IsInvokeResultMessage() + public T GetContent() { - return ContentType == MessagePackTransportMessageType.remoteInvokeResultMessageTypeName; + return (T)Content; } + /// + /// The IsHttpMessage + /// + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsHttpMessage() { return ContentType == MessagePackTransportMessageType.httpMessageTypeName; } + /// + /// The IsHttpResultMessage + /// + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsHttpResultMessage() { @@ -79,41 +131,25 @@ public bool IsHttpResultMessage() } /// - /// 获取内容。 + /// 是否调用消息。 /// - /// 内容类型。 - /// 内容实例。 + /// 如果是则返回true,否则返回false。 [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T GetContent() + public bool IsInvokeMessage() { - return (T)Content; + return ContentType == MessagePackTransportMessageType.remoteInvokeMessageTypeName; } /// - /// 创建一个调用传输消息。 + /// 是否是调用结果消息。 /// - /// 调用实例。 - /// 调用传输消息。 - public static TransportMessage CreateInvokeMessage(RemoteInvokeMessage invokeMessage) + /// 如果是则返回true,否则返回false。 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsInvokeResultMessage() { - return new TransportMessage(invokeMessage, MessagePackTransportMessageType.remoteInvokeMessageTypeName) - { - Id = Guid.NewGuid().ToString("N") - }; + return ContentType == MessagePackTransportMessageType.remoteInvokeResultMessageTypeName; } - /// - /// 创建一个调用结果传输消息。 - /// - /// 消息Id。 - /// 调用结果实例。 - /// 调用结果传输消息。 - public static TransportMessage CreateInvokeResultMessage(string id, RemoteInvokeResultMessage invokeResultMessage) - { - return new TransportMessage(invokeResultMessage, MessagePackTransportMessageType.remoteInvokeResultMessageTypeName) - { - Id = id - }; - } + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Module/AbstractModule.cs b/src/Surging.Core/Surging.Core.CPlatform/Module/AbstractModule.cs index 0a2bdac68..e7d75d114 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Module/AbstractModule.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Module/AbstractModule.cs @@ -10,97 +10,142 @@ namespace Surging.Core.CPlatform.Module { - public abstract class AbstractModule : Autofac.Module,IDisposable + /// + /// Defines the + /// + public abstract class AbstractModule : Autofac.Module, IDisposable { - #region 实例属性 + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + public AbstractModule() + { + ModuleName = this.GetType().Name; + TypeName = this.GetType().BaseType.Name; + } + + #endregion 构造函数 + + #region 属性 + /// + /// Gets or sets the Builder /// 容器创建包装属性 /// public ContainerBuilderWrapper Builder { get; set; } - + /// - /// 唯一标识guid + /// Gets or sets the Components + /// 组件 /// - public Guid Identifier { get; set; } + public List Components { get; set; } /// - /// 模块名 + /// Gets or sets the Description + /// 描述 /// - public string ModuleName { get; set; } + public string Description { get; set; } /// - /// 类型名 + /// Gets or sets a value indicating whether Enable + /// 是否可用(控制模块是否加载) /// - public string TypeName { get; set; } + public bool Enable { get; set; } = true; - /// - /// 标题 + /// Gets or sets the Identifier + /// 唯一标识guid /// - public string Title { get; set; } + public Guid Identifier { get; set; } + /// - /// 是否可用(控制模块是否加载) + /// Gets or sets the ModuleName + /// 模块名 /// - public bool Enable { get; set; } = true; + public string ModuleName { get; set; } /// - /// 描述 + /// Gets or sets the Title + /// 标题 /// - public string Description { get; set; } - + public string Title { get; set; } + /// - /// 组件 + /// Gets or sets the TypeName + /// 类型名 /// - public List Components { get; set; } + public string TypeName { get; set; } - #endregion + #endregion 属性 - #region 构造函数 - public AbstractModule() + #region 方法 + + /// + /// The Dispose + /// + public virtual void Dispose() { - ModuleName = this.GetType().Name; - TypeName = this.GetType().BaseType.Name; } - #endregion - - #region 实例方法 + /// + /// The Initialize + /// + /// The serviceProvider public virtual void Initialize(AppModuleContext serviceProvider) { Dispose(); } + /// - /// 重写autofac load方法 - /// 判断组件是否可用,并注册模块组件 + /// The ToString /// - /// - protected override void Load(ContainerBuilder builder) + /// The + public override string ToString() { - try - { - base.Load(builder); - Builder = new ContainerBuilderWrapper(builder); - if (Enable)//如果可用 - { - //注册创建容器 - RegisterBuilder(Builder); - //注册组件 - RegisterComponents(Builder); - - } - } - catch (Exception ex) + StringBuilder sb = new StringBuilder(); + sb.AppendFormat("标识符:{0}", Identifier); + sb.AppendLine(); + sb.AppendFormat("模块名:{0}", ModuleName); + sb.AppendLine(); + sb.AppendFormat("类型名:{0}", TypeName); + sb.AppendLine(); + sb.AppendFormat("标题:{0}", Title); + sb.AppendLine(); + sb.AppendFormat("描述:{0}", Description); + sb.AppendLine(); + sb.AppendFormat("组件详细 {0}个", Components.Count); + sb.AppendLine(); + Components.ForEach(c => { - throw new CPlatformException(string.Format("注册模块组件类型时发生错误:{0}", ex.Message)); - } + sb.AppendLine(c.ToString()); + }); + return sb.ToString(); } - - protected virtual void RegisterBuilder(ContainerBuilderWrapper builder) + /// + /// 验证模块 + /// + public virtual void ValidateModule() { + if (this.Identifier == Guid.Empty || string.IsNullOrEmpty(this.ModuleName) || string.IsNullOrEmpty(this.TypeName) + || string.IsNullOrEmpty(this.Title)) + { + throw new CPlatformException("模块属性:Identifier,ModuleName,TypeName,Title 是必须的不能为空!"); + } + + Regex regex = new Regex(@"^[a-zA-Z][a-zA-Z0-9_]*$"); + if (!regex.IsMatch(this.ModuleName)) + { + throw new CPlatformException("模块属性:ModuleName 必须为字母开头数字或下划线的组合!"); + } } - + /// + /// The RegisterComponents + /// + /// The builder internal virtual void RegisterComponents(ContainerBuilderWrapper builder) { if (Components != null) @@ -127,6 +172,7 @@ internal virtual void RegisterComponents(ContainerBuilderWrapper builder) builder.RegisterType(implementType).As(serviceType).InstancePerDependency(); } break; + case LifetimeScope.SingleInstance://单例 if (serviceType.GetTypeInfo().IsGenericType || implementType.GetTypeInfo().IsGenericType) { @@ -137,6 +183,7 @@ internal virtual void RegisterComponents(ContainerBuilderWrapper builder) builder.RegisterType(implementType).As(serviceType).SingleInstance(); } break; + default://默认依赖创建 if (serviceType.GetTypeInfo().IsGenericType || implementType.GetTypeInfo().IsGenericType) { @@ -148,54 +195,43 @@ internal virtual void RegisterComponents(ContainerBuilderWrapper builder) } break; } - }); } } + /// - /// 验证模块 + /// 重写autofac load方法 + /// 判断组件是否可用,并注册模块组件 /// - public virtual void ValidateModule() + /// + protected override void Load(ContainerBuilder builder) { - if (this.Identifier == Guid.Empty || string.IsNullOrEmpty(this.ModuleName) || string.IsNullOrEmpty(this.TypeName) - || string.IsNullOrEmpty(this.Title)) + try { - throw new CPlatformException("模块属性:Identifier,ModuleName,TypeName,Title 是必须的不能为空!"); + base.Load(builder); + Builder = new ContainerBuilderWrapper(builder); + if (Enable)//如果可用 + { + //注册创建容器 + RegisterBuilder(Builder); + //注册组件 + RegisterComponents(Builder); + } } - - Regex regex = new Regex(@"^[a-zA-Z][a-zA-Z0-9_]*$"); - if (!regex.IsMatch(this.ModuleName)) + catch (Exception ex) { - throw new CPlatformException("模块属性:ModuleName 必须为字母开头数字或下划线的组合!"); + throw new CPlatformException(string.Format("注册模块组件类型时发生错误:{0}", ex.Message)); } } - public override string ToString() - { - StringBuilder sb = new StringBuilder(); - sb.AppendFormat("标识符:{0}", Identifier); - sb.AppendLine(); - sb.AppendFormat("模块名:{0}", ModuleName); - sb.AppendLine(); - sb.AppendFormat("类型名:{0}", TypeName); - sb.AppendLine(); - sb.AppendFormat("标题:{0}", Title); - sb.AppendLine(); - sb.AppendFormat("描述:{0}", Description); - sb.AppendLine(); - sb.AppendFormat("组件详细 {0}个", Components.Count); - sb.AppendLine(); - Components.ForEach(c => - { - sb.AppendLine(c.ToString()); - }); - return sb.ToString(); - } - - public virtual void Dispose() + /// + /// The RegisterBuilder + /// + /// The builder + protected virtual void RegisterBuilder(ContainerBuilderWrapper builder) { } - #endregion + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Module/AppModuleContext.cs b/src/Surging.Core/Surging.Core.CPlatform/Module/AppModuleContext.cs index 2f584b55b..3486af599 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Module/AppModuleContext.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Module/AppModuleContext.cs @@ -5,21 +5,47 @@ namespace Surging.Core.CPlatform.Module { - public class AppModuleContext + /// + /// Defines the + /// + public class AppModuleContext { - public AppModuleContext(List modules, + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The modules + /// The virtualPaths + /// The serviceProvoider + public AppModuleContext(List modules, string[] virtualPaths, CPlatformContainer serviceProvoider) - { + { Modules = Check.NotNull(modules, nameof(modules)); VirtualPaths = Check.NotNull(virtualPaths, nameof(virtualPaths)); ServiceProvoider = Check.NotNull(serviceProvoider, nameof(serviceProvoider)); } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the Modules + /// public List Modules { get; } + /// + /// Gets the ServiceProvoider + /// + public CPlatformContainer ServiceProvoider { get; } + + /// + /// Gets the VirtualPaths + /// public string[] VirtualPaths { get; } - public CPlatformContainer ServiceProvoider { get; } + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Module/AssemblyEntry.cs b/src/Surging.Core/Surging.Core.CPlatform/Module/AssemblyEntry.cs index 64ef174e9..5351d8082 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Module/AssemblyEntry.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Module/AssemblyEntry.cs @@ -7,182 +7,95 @@ namespace Surging.Core.CPlatform.Module { + /// + /// Defines the + /// [XmlRoot("Assembly")] public class AssemblyEntry : IXmlSerializable { - #region 实例属性 + #region 属性 /// - /// 获取程序集文件名。 + /// Gets or sets the AbstractModules + /// 获取程序集模块列表。 /// - public string FileName - { - get; - internal set; - } + public List AbstractModules { get; set; } /// - /// 获取程序集完全名称。 + /// Gets or sets the Description + /// 获取程序集功能描述。 /// - public string FullName - { - get; - internal set; - } + public string Description { get; internal set; } /// - /// 获取程序集名称唯一键。 + /// Gets or sets a value indicating whether DisableStopAndUninstalled + /// 获取一个值指示是否禁止停止和卸载。 /// - public string Name - { - get; - internal set; - } + public bool DisableStopAndUninstalled { get; internal set; } /// - /// 获取程序集标题文本。 + /// Gets or sets the FileName + /// 获取程序集文件名。 /// - /// - /// 标题文本。 - /// - public string Title - { - get; - internal set; - } + public string FileName { get; internal set; } /// - /// 获取程序集功能描述。 + /// Gets or sets the FullName + /// 获取程序集完全名称。 /// - /// - /// 程序集功能描述文本。 - /// - public string Description - { - get; - internal set; - } - - /// - /// 获取程序集版本号。 - /// - - public Version Version - { - get; - internal set; - } + public string FullName { get; internal set; } /// + /// Gets or sets a value indicating whether IsExtend /// 获取一个值指示该程序集是否是扩展的程序集。 /// - /// - /// 如果 true 这个程序集是扩展的;否则,false 不是扩展的程序集。 - /// - public bool IsExtend - { - get; - internal set; - } - - /// - /// 获取一个值指示是否禁止停止和卸载。 - /// - /// - /// 如果 true 这个程序集将禁止停止和卸载;否则,false 允许停止和卸载。 - /// - public bool DisableStopAndUninstalled - { - get; - internal set; - } + public bool IsExtend { get; internal set; } /// + /// Gets or sets the ListOrder /// 获取程序集排序顺序。 /// - /// - /// 从 1 开始的整数数值。 - /// public int ListOrder { get; set; } /// - /// 获取程序集模块状态。 + /// Gets or sets the Name + /// 获取程序集名称唯一键。 /// - /// - /// 程序集模块状态枚举值:Installed | Start | Stop | Uninstalled 。 - /// - public ModuleState State { get; set; } + public string Name { get; internal set; } /// + /// Gets or sets the Reference /// 获取程序集引用列表。 /// - public List Reference - { - get; - internal set; - } + public List Reference { get; internal set; } /// - /// 获取程序集模块列表。 + /// Gets or sets the State + /// 获取程序集模块状态。 /// - /// - /// 程序集模块列表 List 泛型集合。 - /// - public List AbstractModules - { - get; - set; - } - - #endregion - - #region 实例方法 + public ModuleState State { get; set; } /// - /// 注册程序集所有抽象模块。 + /// Gets or sets the Title + /// 获取程序集标题文本。 /// - /// 容器构建对象。 - public void RegisterModule(ContainerBuilderWrapper builder) - { - AbstractModules.ForEach(module => - { - // module.Initialize(); - builder.RegisterModule(module); - }); - } + public string Title { get; internal set; } /// - /// 获取指定控制器类型的业务模块实例。 + /// Gets or sets the Version + /// 获取程序集版本号。 /// - public BusinessModule GetBusinessModuleByControllerType(Type controllerType) - { - BusinessModule businessModule = this.AbstractModules.Find(m => controllerType.Namespace.Contains(m.GetType().Namespace) == true) as BusinessModule; + public Version Version { get; internal set; } - if (businessModule == null) - { - throw new Exception(string.Format("无法找到 {0} 控制器所属的业务模块实例对象", controllerType.Name)); - } + #endregion 属性 - return businessModule; - } - - /// - /// 获取程序集视图目录名称:公司.产品.程序集模块(程序集视图目录名称)。 - /// - /// 返回程序集视图目录名称 - public string GetAssemblyViewDirectoryName() - { - return this.Name.Substring(this.Name.LastIndexOf('.') + 1); - } + #region 方法 /// /// 比较程序集模块版本。 /// /// 版本字符串。 - /// - /// 一个有符号整数,它指示两个对象的相对值,如下表所示。返回值含义小于零当前的 System.Version 对象是 version 之前的一个版本。 - /// 零当前的System.Version 对象是与 version 相同的版本。大于零当前 System.Version 对象是 version 之后的一个版本。-或 -version 为 null。 - /// + /// The public int CompareVersion(string version) { return CompareVersion(new Version(version)); @@ -192,78 +105,42 @@ public int CompareVersion(string version) /// 比较程序集模块版本。 /// /// 版本对象。 - /// - /// 一个有符号整数,它指示两个对象的相对值,如下表所示。返回值含义小于零当前的 System.Version 对象是 version 之前的一个版本。 - /// 零当前的System.Version 对象是与 version 相同的版本。大于零当前 System.Version 对象是 version 之后的一个版本。-或 -version 为 null。 - /// + /// The public int CompareVersion(Version version) { return Version.CompareTo(version); } /// - /// 获取程序集模块的字符串文本描述信息。 + /// 获取程序集视图目录名称:公司.产品.程序集模块(程序集视图目录名称)。 /// - /// - /// 返回程序集模块对象的字符串文本描述信息。 - /// - public override string ToString() + /// 返回程序集视图目录名称 + public string GetAssemblyViewDirectoryName() { - StringBuilder sb = new StringBuilder(); - sb.AppendFormat("程序集文件名:{0}", FileName); - sb.AppendLine(); - - sb.AppendFormat("程序集类型全名:{0}", FullName); - sb.AppendLine(); - - sb.AppendFormat("程序集类型名:{0}", Name); - sb.AppendLine(); - - sb.AppendFormat("程序集标题:{0}", Title); - sb.AppendLine(); - - sb.AppendFormat("程序集描述:{0}", Description); - sb.AppendLine(); - - sb.AppendFormat("程序集版本:{0}", Version); - sb.AppendLine(); - - sb.AppendFormat("扩展的程序集:{0}", IsExtend); - sb.AppendLine(); - - sb.AppendFormat("禁用停止和卸载:{0}", DisableStopAndUninstalled); - sb.AppendLine(); - - sb.AppendFormat("程序集注册顺序:{0}", ListOrder); - sb.AppendLine(); - - sb.AppendFormat("程序集引用 {0}个", Reference.Count); - sb.AppendLine(); - - Reference.ForEach(r => - { - sb.AppendLine(r); - }); + return this.Name.Substring(this.Name.LastIndexOf('.') + 1); + } - sb.AppendFormat("程序集模块 {0}个", AbstractModules.Count); - sb.AppendLine(); + /// + /// 获取指定控制器类型的业务模块实例。 + /// + /// The controllerType + /// The + public BusinessModule GetBusinessModuleByControllerType(Type controllerType) + { + BusinessModule businessModule = this.AbstractModules.Find(m => controllerType.Namespace.Contains(m.GetType().Namespace) == true) as BusinessModule; - AbstractModules.ForEach(m => + if (businessModule == null) { - sb.Append(m.ToString()); - }); + throw new Exception(string.Format("无法找到 {0} 控制器所属的业务模块实例对象", controllerType.Name)); + } - return sb.ToString(); + return businessModule; } - #region IXmlSerializable 成员 - /// /// 此方法是保留方法,请不要使用。在实现 IXmlSerializable 接口时,应从此方法返回 null(在 Visual Basic 中为 Nothing),如果需要指定自定义架构,应向该类应用 。 /// - /// - /// ,描述由 方法产生并由 方法使用的对象的 XML 表示形式。 - /// + /// The public XmlSchema GetSchema() { return null; @@ -273,7 +150,6 @@ public XmlSchema GetSchema() /// 从对象的 XML 表示形式生成该对象。 /// /// 对象从中进行反序列化的 流。 - public void ReadXml(XmlReader reader) { if (reader.IsEmptyElement || !reader.Read()) @@ -349,6 +225,72 @@ public void ReadXml(XmlReader reader) } } + /// + /// 注册程序集所有抽象模块。 + /// + /// 容器构建对象。 + public void RegisterModule(ContainerBuilderWrapper builder) + { + AbstractModules.ForEach(module => + { + // module.Initialize(); + builder.RegisterModule(module); + }); + } + + /// + /// 获取程序集模块的字符串文本描述信息。 + /// + /// The + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.AppendFormat("程序集文件名:{0}", FileName); + sb.AppendLine(); + + sb.AppendFormat("程序集类型全名:{0}", FullName); + sb.AppendLine(); + + sb.AppendFormat("程序集类型名:{0}", Name); + sb.AppendLine(); + + sb.AppendFormat("程序集标题:{0}", Title); + sb.AppendLine(); + + sb.AppendFormat("程序集描述:{0}", Description); + sb.AppendLine(); + + sb.AppendFormat("程序集版本:{0}", Version); + sb.AppendLine(); + + sb.AppendFormat("扩展的程序集:{0}", IsExtend); + sb.AppendLine(); + + sb.AppendFormat("禁用停止和卸载:{0}", DisableStopAndUninstalled); + sb.AppendLine(); + + sb.AppendFormat("程序集注册顺序:{0}", ListOrder); + sb.AppendLine(); + + sb.AppendFormat("程序集引用 {0}个", Reference.Count); + sb.AppendLine(); + + Reference.ForEach(r => + { + sb.AppendLine(r); + }); + + sb.AppendFormat("程序集模块 {0}个", AbstractModules.Count); + sb.AppendLine(); + + AbstractModules.ForEach(m => + { + sb.Append(m.ToString()); + }); + + return sb.ToString(); + } + /// /// 将对象转换为其 XML 表示形式。 /// @@ -390,9 +332,6 @@ public void WriteXml(XmlWriter writer) writer.WriteEndElement(); } - #endregion - - #endregion + #endregion 方法 } -} - +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Module/BusinessModule.cs b/src/Surging.Core/Surging.Core.CPlatform/Module/BusinessModule.cs index 2bfa9b726..5152af073 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Module/BusinessModule.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Module/BusinessModule.cs @@ -5,18 +5,30 @@ namespace Surging.Core.CPlatform.Module { /// - /// 业务模块基类 + /// 业务模块基类 /// public class BusinessModule : AbstractModule { + #region 方法 + + /// + /// The Initialize + /// + /// The serviceProvider public override void Initialize(AppModuleContext serviceProvider) { base.Initialize(serviceProvider); } - + + /// + /// The RegisterComponents + /// + /// The builder internal override void RegisterComponents(ContainerBuilderWrapper builder) { base.RegisterComponents(builder); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Module/Component.cs b/src/Surging.Core/Surging.Core.CPlatform/Module/Component.cs index e5c6149e1..8a1ff41ce 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Module/Component.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Module/Component.cs @@ -4,35 +4,61 @@ namespace Surging.Core.CPlatform.Module { - #region 组件生命周期枚举类 - + #region 枚举 + + /// + /// Defines the LifetimeScope + /// public enum LifetimeScope { + /// + /// Defines the InstancePerDependency + /// InstancePerDependency, - + + /// + /// Defines the InstancePerHttpRequest + /// InstancePerHttpRequest, - + + /// + /// Defines the SingleInstance + /// SingleInstance } - #endregion + #endregion 枚举 - #region 组件类 - + /// + /// Defines the + /// public class Component { - #region 实例属性 - - public string ServiceType { get; set; } + #region 属性 + /// + /// Gets or sets the ImplementType + /// public string ImplementType { get; set; } + /// + /// Gets or sets the LifetimeScope + /// public LifetimeScope LifetimeScope { get; set; } - #endregion + /// + /// Gets or sets the ServiceType + /// + public string ServiceType { get; set; } + + #endregion 属性 - #region 实例方法 - + #region 方法 + + /// + /// The ToString + /// + /// The public override string ToString() { StringBuilder sb = new StringBuilder(); @@ -44,8 +70,6 @@ public override string ToString() return sb.ToString(); } - #endregion + #endregion 方法 } - - #endregion -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Module/ContainerBuilderWrapper.cs b/src/Surging.Core/Surging.Core.CPlatform/Module/ContainerBuilderWrapper.cs index ac75852cb..a7f46fbe0 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Module/ContainerBuilderWrapper.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Module/ContainerBuilderWrapper.cs @@ -5,19 +5,44 @@ namespace Surging.Core.CPlatform.Module { - public class ContainerBuilderWrapper + /// + /// Defines the + /// + public class ContainerBuilderWrapper { - - public ContainerBuilder ContainerBuilder { get; private set; } - + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The builder public ContainerBuilderWrapper(ContainerBuilder builder) { ContainerBuilder = builder; } - + + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the ContainerBuilder + /// + public ContainerBuilder ContainerBuilder { get; private set; } + + #endregion 属性 + + #region 方法 + + /// + /// The Build + /// + /// The public IContainer Build() { return ContainerBuilder.Build(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Module/EchoService.cs b/src/Surging.Core/Surging.Core.CPlatform/Module/EchoService.cs index c819b8484..5930fb063 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Module/EchoService.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Module/EchoService.cs @@ -9,39 +9,82 @@ namespace Surging.Core.CPlatform.Module { + /// + /// Defines the + /// public class EchoService : ServiceBase, IEchoService { - private readonly IHashAlgorithm _hashAlgorithm; + #region 字段 + + /// + /// Defines the _addressSelector + /// private readonly IAddressSelector _addressSelector; - private readonly IServiceRouteProvider _serviceRouteProvider; + + /// + /// Defines the _hashAlgorithm + /// + private readonly IHashAlgorithm _hashAlgorithm; + + /// + /// Defines the _serviceHeartbeatManager + /// private readonly IServiceHeartbeatManager _serviceHeartbeatManager; + /// + /// Defines the _serviceRouteProvider + /// + private readonly IServiceRouteProvider _serviceRouteProvider; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The hashAlgorithm + /// The serviceRouteProvider + /// The container + /// The serviceHeartbeatManager public EchoService(IHashAlgorithm hashAlgorithm, IServiceRouteProvider serviceRouteProvider, CPlatformContainer container, IServiceHeartbeatManager serviceHeartbeatManager) { _hashAlgorithm = hashAlgorithm; - _addressSelector =container.GetInstances(AddressSelectorMode.HashAlgorithm.ToString()); + _addressSelector = container.GetInstances(AddressSelectorMode.HashAlgorithm.ToString()); _serviceRouteProvider = serviceRouteProvider; _serviceHeartbeatManager = serviceHeartbeatManager; } - public async Task Locate(string key,string routePath) + #endregion 构造函数 + + #region 方法 + + /// + /// The Locate + /// + /// The key + /// The routePath + /// The + public async Task Locate(string key, string routePath) { - var route= await _serviceRouteProvider.SearchRoute(routePath); + var route = await _serviceRouteProvider.SearchRoute(routePath); AddressModel result = new IpAddressModel(); if (route != null) { - result = await _addressSelector.SelectAsync(new AddressSelectContext() + result = await _addressSelector.SelectAsync(new AddressSelectContext() { Address = route.Address, Descriptor = route.ServiceDescriptor, Item = key, }); _serviceHeartbeatManager.AddWhitelist(route.ServiceDescriptor.Id); - } + } var ipAddress = result as IpAddressModel; return ipAddress; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Module/EnginePartModule.cs b/src/Surging.Core/Surging.Core.CPlatform/Module/EnginePartModule.cs index d034dfe5a..90e43f8dc 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Module/EnginePartModule.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Module/EnginePartModule.cs @@ -5,30 +5,47 @@ namespace Surging.Core.CPlatform.Module { + /// + /// Defines the + /// public class EnginePartModule : AbstractModule { - public override void Initialize(AppModuleContext context) - { - base.Initialize(context); - } + #region 方法 + /// + /// The Dispose + /// public override void Dispose() { base.Dispose(); } - - protected virtual void RegisterServiceBuilder(IServiceBuilder builder) + /// + /// The Initialize + /// + /// The context + public override void Initialize(AppModuleContext context) { + base.Initialize(context); } - + /// + /// The RegisterComponents + /// + /// The builder internal override void RegisterComponents(ContainerBuilderWrapper builder) { base.RegisterComponents(builder); - } - - } + /// + /// The RegisterServiceBuilder + /// + /// The builder + protected virtual void RegisterServiceBuilder(IServiceBuilder builder) + { + } + + #endregion 方法 + } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Module/IEchoService.cs b/src/Surging.Core/Surging.Core.CPlatform/Module/IEchoService.cs index 175b0d258..41a14af36 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Module/IEchoService.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Module/IEchoService.cs @@ -7,10 +7,27 @@ namespace Surging.Core.CPlatform.Module { + #region 接口 + + /// + /// Defines the + /// [ServiceBundle("")] - public interface IEchoService: IServiceKey + public interface IEchoService : IServiceKey { + #region 方法 + + /// + /// The Locate + /// + /// The key + /// The routePath + /// The [Command(ShuntStrategy = AddressSelectorMode.HashAlgorithm)] - Task Locate(string key,string routePath); + Task Locate(string key, string routePath); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Module/IModuleManager.cs b/src/Surging.Core/Surging.Core.CPlatform/Module/IModuleManager.cs index a0ab6f089..cd957ebdb 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Module/IModuleManager.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Module/IModuleManager.cs @@ -5,18 +5,55 @@ namespace Surging.Core.CPlatform.Module { - public interface IModuleManager + #region 接口 + + /// + /// Defines the + /// + public interface IModuleManager { + #region 方法 + + /// + /// The Delete + /// + /// The module + void Delete(AssemblyEntry module); + + /// + /// The Install + /// + /// The modulePackageFileName + /// The textWriter + /// The bool Install(string modulePackageFileName, TextWriter textWriter); + /// + /// The Save + /// + /// The module + void Save(AssemblyEntry module); + + /// + /// The Start + /// + /// The module void Start(AssemblyEntry module); - + + /// + /// The Stop + /// + /// The module void Stop(AssemblyEntry module); - + + /// + /// The Uninstall + /// + /// The module void Uninstall(AssemblyEntry module); - void Delete(AssemblyEntry module); - - void Save(AssemblyEntry module); + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Module/IModuleProvider.cs b/src/Surging.Core/Surging.Core.CPlatform/Module/IModuleProvider.cs index a6608077a..17c56d34d 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Module/IModuleProvider.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Module/IModuleProvider.cs @@ -4,11 +4,36 @@ namespace Surging.Core.CPlatform.Module { + #region 接口 + + /// + /// Defines the + /// public interface IModuleProvider { + #region 属性 + + /// + /// Gets the Modules + /// List Modules { get; } + /// + /// Gets the VirtualPaths + /// string[] VirtualPaths { get; } + + #endregion 属性 + + #region 方法 + + /// + /// The Initialize + /// void Initialize(); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Module/ModuleProvider.cs b/src/Surging.Core/Surging.Core.CPlatform/Module/ModuleProvider.cs index d2c9e6d31..a7ab074d2 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Module/ModuleProvider.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Module/ModuleProvider.cs @@ -6,17 +6,42 @@ namespace Surging.Core.CPlatform.Module { - public class ModuleProvider: IModuleProvider + /// + /// Defines the + /// + public class ModuleProvider : IModuleProvider { + #region 字段 + + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the _modules + /// private readonly List _modules; - private readonly string[] _virtualPaths; + + /// + /// Defines the _serviceProvoider + /// private readonly CPlatformContainer _serviceProvoider; - private readonly ILogger _logger; /// - /// 模块提供器 + /// Defines the _virtualPaths + /// + private readonly string[] _virtualPaths; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. /// /// + /// The virtualPaths /// /// public ModuleProvider(List modules, @@ -30,24 +55,41 @@ public ModuleProvider(List modules, _logger = logger; } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the Modules + /// public List Modules { get => _modules; } + /// + /// Gets the VirtualPaths + /// public string[] VirtualPaths { get => _virtualPaths; } + #endregion 属性 + + #region 方法 + + /// + /// The Initialize + /// public virtual void Initialize() { _modules.ForEach(p => { try { - Type[] types = { typeof(SystemModule), typeof(BusinessModule), typeof(EnginePartModule), typeof(AbstractModule) }; + Type[] types = { typeof(SystemModule), typeof(BusinessModule), typeof(EnginePartModule), typeof(AbstractModule) }; if (p.Enable) - p.Initialize(new AppModuleContext(_modules, _virtualPaths, _serviceProvoider)); + p.Initialize(new AppModuleContext(_modules, _virtualPaths, _serviceProvoider)); var type = p.GetType().BaseType; if (types.Any(ty => ty == type)) p.Dispose(); } - catch(Exception ex) + catch (Exception ex) { throw ex; } @@ -55,6 +97,9 @@ public virtual void Initialize() WriteLog(); } + /// + /// The WriteLog + /// public void WriteLog() { if (_logger.IsEnabled(LogLevel.Debug)) @@ -66,5 +111,7 @@ public void WriteLog() }); } } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Module/ModuleState.cs b/src/Surging.Core/Surging.Core.CPlatform/Module/ModuleState.cs index 9a33bbabc..2a56717ef 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Module/ModuleState.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Module/ModuleState.cs @@ -4,6 +4,8 @@ namespace Surging.Core.CPlatform.Module { + #region 枚举 + /// /// 模块状态枚举。 /// @@ -29,4 +31,6 @@ public enum ModuleState /// Uninstalled } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Module/RegistrationExtensions.cs b/src/Surging.Core/Surging.Core.CPlatform/Module/RegistrationExtensions.cs index 397c73396..d2ff8b440 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Module/RegistrationExtensions.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Module/RegistrationExtensions.cs @@ -11,93 +11,207 @@ namespace Surging.Core.CPlatform.Module { - public static class RegistrationExtensions + /// + /// Defines the + /// + public static class RegistrationExtensions { - public static IRegistrationBuilder Register(this ContainerBuilderWrapper builder, Func @delegate) + #region 方法 + + /// + /// The AsInheritedClasses + /// + /// + /// The registration + /// The + public static IRegistrationBuilder AsInheritedClasses(this IRegistrationBuilder registration) { - return builder.ContainerBuilder.Register(@delegate); + if (registration == null) throw new ArgumentNullException("registration"); + return registration.As(t => GetInheritedClasses(t)); } + /// + /// The Register + /// + /// + /// The builder + /// The delegate + /// The public static IRegistrationBuilder Register(this ContainerBuilderWrapper builder, Func, T> @delegate) { return builder.ContainerBuilder.Register(@delegate); } - - public static void RegisterModule(this ContainerBuilderWrapper builder, IModule module) - { - builder.ContainerBuilder.RegisterModule(module); - } - public static void RegisterModule(this ContainerBuilderWrapper builder) - where TModule : IModule, new() + /// + /// The Register + /// + /// + /// The builder + /// The delegate + /// The + public static IRegistrationBuilder Register(this ContainerBuilderWrapper builder, Func @delegate) { - builder.ContainerBuilder.RegisterModule(); + return builder.ContainerBuilder.Register(@delegate); } - public static IRegistrationBuilder RegisterInstance(this ContainerBuilderWrapper builder, T instance) - where T : class + /// + /// The RegisterAdapter + /// + /// + /// + /// The builder + /// The adapter + /// The + public static IRegistrationBuilder RegisterAdapter(this ContainerBuilderWrapper builder, Func adapter) { - return builder.ContainerBuilder.RegisterInstance(instance); + return builder.ContainerBuilder.RegisterAdapter(adapter); } - public static IRegistrationBuilder RegisterType(this ContainerBuilderWrapper builder) + /// + /// The RegisterAssemblyTypes + /// + /// The builder + /// The assemblies + /// The + public static IRegistrationBuilder RegisterAssemblyTypes(this ContainerBuilderWrapper builder, params Assembly[] assemblies) { - return builder.ContainerBuilder.RegisterType(); + return builder.ContainerBuilder.RegisterAssemblyTypes(assemblies); } - public static IRegistrationBuilder RegisterType(this ContainerBuilderWrapper builder, Type implementationType) + /// + /// The RegisterDecorator + /// + /// + /// The builder + /// The decorator + /// The fromKey + /// The toKey + /// The + public static IRegistrationBuilder RegisterDecorator(this ContainerBuilderWrapper builder, Func decorator, object fromKey, object toKey = null) { - return builder.ContainerBuilder.RegisterType(implementationType); + return builder.ContainerBuilder.RegisterDecorator(decorator, fromKey, toKey); } - public static IRegistrationBuilder RegisterTypes(this ContainerBuilderWrapper builder, params Type [] implementationTypes) + /// + /// The RegisterGeneratedFactory + /// + /// + /// The builder + /// The + public static IRegistrationBuilder RegisterGeneratedFactory(this ContainerBuilderWrapper builder) + where TDelegate : class { - return builder.ContainerBuilder.RegisterTypes(implementationTypes); + return builder.ContainerBuilder.RegisterGeneratedFactory(); } - public static IRegistrationBuilder RegisterAssemblyTypes(this ContainerBuilderWrapper builder, params Assembly[] assemblies) + /// + /// The RegisterGeneratedFactory + /// + /// The builder + /// The delegateType + /// The + public static IRegistrationBuilder RegisterGeneratedFactory(this ContainerBuilderWrapper builder, Type delegateType) { - return builder.ContainerBuilder.RegisterAssemblyTypes(assemblies); + return builder.ContainerBuilder.RegisterGeneratedFactory(delegateType); } + /// + /// The RegisterGeneric + /// + /// The builder + /// The implementor + /// The public static IRegistrationBuilder RegisterGeneric(this ContainerBuilderWrapper builder, Type implementor) { return builder.ContainerBuilder.RegisterGeneric(implementor); } - public static IRegistrationBuilder RegisterAdapter(this ContainerBuilderWrapper builder, Func adapter) + /// + /// The RegisterGenericDecorator + /// + /// The builder + /// The decoratorType + /// The decoratedServiceType + /// The fromKey + /// The toKey + /// The + public static IRegistrationBuilder RegisterGenericDecorator(this ContainerBuilderWrapper builder, Type decoratorType, Type decoratedServiceType, object fromKey, object toKey = null) { - return builder.ContainerBuilder.RegisterAdapter(adapter); + return builder.ContainerBuilder.RegisterGenericDecorator(decoratorType, decoratedServiceType, fromKey, toKey); } - public static IRegistrationBuilder RegisterGenericDecorator(this ContainerBuilderWrapper builder, Type decoratorType, Type decoratedServiceType, object fromKey, object toKey = null) + /// + /// The RegisterInstance + /// + /// + /// The builder + /// The instance + /// The + public static IRegistrationBuilder RegisterInstance(this ContainerBuilderWrapper builder, T instance) + where T : class { - return builder.ContainerBuilder.RegisterGenericDecorator(decoratorType, decoratedServiceType, fromKey, toKey); + return builder.ContainerBuilder.RegisterInstance(instance); } - public static IRegistrationBuilder RegisterDecorator(this ContainerBuilderWrapper builder, Func decorator, object fromKey, object toKey = null) + /// + /// The RegisterModule + /// + /// + /// The builder + public static void RegisterModule(this ContainerBuilderWrapper builder) + where TModule : IModule, new() { - return builder.ContainerBuilder.RegisterDecorator(decorator, fromKey, toKey); + builder.ContainerBuilder.RegisterModule(); } + /// + /// The RegisterModule + /// + /// The builder + /// The module + public static void RegisterModule(this ContainerBuilderWrapper builder, IModule module) + { + builder.ContainerBuilder.RegisterModule(module); + } - public static IRegistrationBuilder RegisterGeneratedFactory(this ContainerBuilderWrapper builder, Type delegateType) + /// + /// The RegisterType + /// + /// + /// The builder + /// The + public static IRegistrationBuilder RegisterType(this ContainerBuilderWrapper builder) { - return builder.ContainerBuilder.RegisterGeneratedFactory(delegateType); + return builder.ContainerBuilder.RegisterType(); } - public static IRegistrationBuilder RegisterGeneratedFactory(this ContainerBuilderWrapper builder) - where TDelegate : class + /// + /// The RegisterType + /// + /// The builder + /// The implementationType + /// The + public static IRegistrationBuilder RegisterType(this ContainerBuilderWrapper builder, Type implementationType) { - return builder.ContainerBuilder.RegisterGeneratedFactory(); + return builder.ContainerBuilder.RegisterType(implementationType); } - public static IRegistrationBuilder AsInheritedClasses(this IRegistrationBuilder registration) + /// + /// The RegisterTypes + /// + /// The builder + /// The implementationTypes + /// The + public static IRegistrationBuilder RegisterTypes(this ContainerBuilderWrapper builder, params Type[] implementationTypes) { - if (registration == null) throw new ArgumentNullException("registration"); - return registration.As(t => GetInheritedClasses(t)); + return builder.ContainerBuilder.RegisterTypes(implementationTypes); } + /// + /// The GetInheritedClasses + /// + /// The type + /// The private static List GetInheritedClasses(Type type) { List types = new List(); @@ -123,5 +237,7 @@ private static List GetInheritedClasses(Type type) return types; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Module/SystemModule.cs b/src/Surging.Core/Surging.Core.CPlatform/Module/SystemModule.cs index 0a202bee1..1a76c6080 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Module/SystemModule.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Module/SystemModule.cs @@ -7,17 +7,28 @@ namespace Surging.Core.CPlatform.Module /// /// 系统模块基类 /// - public class SystemModule : AbstractModule + public class SystemModule : AbstractModule { + #region 方法 + + /// + /// The Initialize + /// + /// The context public override void Initialize(AppModuleContext context) { base.Initialize(context); } - + + /// + /// The RegisterComponents + /// + /// The builder internal override void RegisterComponents(ContainerBuilderWrapper builder) { base.RegisterComponents(builder); } - } -} + #endregion 方法 + } +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Mqtt/IMqttServiceFactory.cs b/src/Surging.Core/Surging.Core.CPlatform/Mqtt/IMqttServiceFactory.cs index 2249cd0dc..db78d687f 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Mqtt/IMqttServiceFactory.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Mqtt/IMqttServiceFactory.cs @@ -5,8 +5,14 @@ namespace Surging.Core.CPlatform.Mqtt { - public interface IMqttServiceFactory + #region 接口 + + /// + /// Defines the + /// + public interface IMqttServiceFactory { + #region 方法 /// /// 根据Mqtt服务路由描述符创建Mqtt服务路由。 @@ -14,5 +20,9 @@ public interface IMqttServiceFactory /// Mqtt服务路由描述符。 /// Mqtt服务路由集合。 Task> CreateMqttServiceRoutesAsync(IEnumerable descriptors); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Mqtt/IMqttServiceRouteManager.cs b/src/Surging.Core/Surging.Core.CPlatform/Mqtt/IMqttServiceRouteManager.cs index 27325cf6e..db345e9da 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Mqtt/IMqttServiceRouteManager.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Mqtt/IMqttServiceRouteManager.cs @@ -7,31 +7,70 @@ namespace Surging.Core.CPlatform.Mqtt { - public interface IMqttServiceRouteManager + #region 接口 + + /// + /// Defines the + /// + public interface IMqttServiceRouteManager { + #region 事件 + + /// + /// Defines the Changed + /// + event EventHandler Changed; + + /// + /// Defines the Created + /// event EventHandler Created; + /// + /// Defines the Removed + /// event EventHandler Removed; - event EventHandler Changed; - - Task> GetRoutesAsync(); + #endregion 事件 + + #region 方法 /// - /// 设置服务路由。 + /// 清空所有的服务路由。 /// - /// 服务路由集合。 /// 一个任务。 - Task SetRoutesAsync(IEnumerable routes); + Task ClearAsync(); - Task RemoveByTopicAsync(string topic, IEnumerable endpoint); + /// + /// The GetRoutesAsync + /// + /// The + Task> GetRoutesAsync(); + /// + /// The RemoveByTopicAsync + /// + /// The topic + /// The endpoint + /// The + Task RemoveByTopicAsync(string topic, IEnumerable endpoint); + /// + /// The RemveAddressAsync + /// + /// The addresses + /// The Task RemveAddressAsync(IEnumerable addresses); + /// - /// 清空所有的服务路由。 + /// 设置服务路由。 /// + /// 服务路由集合。 /// 一个任务。 - Task ClearAsync(); + Task SetRoutesAsync(IEnumerable routes); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Mqtt/Implementation/MqttServiceRouteManagerBase.cs b/src/Surging.Core/Surging.Core.CPlatform/Mqtt/Implementation/MqttServiceRouteManagerBase.cs index ab25bf068..0e7731d00 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Mqtt/Implementation/MqttServiceRouteManagerBase.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Mqtt/Implementation/MqttServiceRouteManagerBase.cs @@ -8,24 +8,61 @@ namespace Surging.Core.CPlatform.Mqtt.Implementation { - public class MqttServiceRouteEventArgs + /// + /// Defines the + /// + public class MqttServiceRouteChangedEventArgs : MqttServiceRouteEventArgs { - public MqttServiceRouteEventArgs(MqttServiceRoute route) + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The route + /// The oldRoute + public MqttServiceRouteChangedEventArgs(MqttServiceRoute route, MqttServiceRoute oldRoute) : base(route) { - Route = route; + OldRoute = oldRoute; } - public MqttServiceRoute Route { get; private set; } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets or sets the OldRoute + /// + public MqttServiceRoute OldRoute { get; set; } + + #endregion 属性 } - public class MqttServiceRouteChangedEventArgs : MqttServiceRouteEventArgs + /// + /// Defines the + /// + public class MqttServiceRouteEventArgs { - public MqttServiceRouteChangedEventArgs(MqttServiceRoute route, MqttServiceRoute oldRoute) : base(route) + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The route + public MqttServiceRouteEventArgs(MqttServiceRoute route) { - OldRoute = oldRoute; + Route = route; } - public MqttServiceRoute OldRoute { get; set; } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the Route + /// + public MqttServiceRoute Route { get; private set; } + + #endregion 属性 } public abstract class MqttServiceRouteManagerBase : IMqttServiceRouteManager @@ -59,13 +96,13 @@ public event EventHandler Changed } public abstract Task ClearAsync(); + public abstract Task> GetRoutesAsync(); public abstract Task RemveAddressAsync(IEnumerable addresses); public abstract Task RemoveByTopicAsync(string topic, IEnumerable endpoint); - public virtual Task SetRoutesAsync(IEnumerable routes) { if (routes == null) @@ -78,10 +115,11 @@ public virtual Task SetRoutesAsync(IEnumerable routes) Type = address.GetType().FullName, Value = _serializer.Serialize(address) }) ?? Enumerable.Empty(), - MqttDescriptor = route.MqttDescriptor + MqttDescriptor = route.MqttDescriptor }); return SetRoutesAsync(descriptors); } + protected abstract Task SetRoutesAsync(IEnumerable descriptors); protected void OnCreated(params MqttServiceRouteEventArgs[] args) @@ -111,4 +149,4 @@ protected void OnRemoved(params MqttServiceRouteEventArgs[] args) _removed(this, arg); } } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Mqtt/MqttDescriptor.cs b/src/Surging.Core/Surging.Core.CPlatform/Mqtt/MqttDescriptor.cs index 48693b634..fc75a8dad 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Mqtt/MqttDescriptor.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Mqtt/MqttDescriptor.cs @@ -5,57 +5,47 @@ namespace Surging.Core.CPlatform.Mqtt { - /// - /// 服务描述符扩展方法。 - /// - public static class MqttDescriptorExtensions - { - - } - /// /// 服务描述符。 /// [Serializable] public class MqttDescriptor { + #region 构造函数 + /// - /// 初始化一个新的服务描述符。 + /// Initializes a new instance of the class. /// public MqttDescriptor() { Metadatas = new Dictionary(StringComparer.OrdinalIgnoreCase); } + #endregion 构造函数 + + #region 属性 + /// - /// Topic。 - /// - public string Topic { get; set; } - /// + /// Gets or sets the Metadatas /// 元数据。 /// public IDictionary Metadatas { get; set; } /// - /// 获取一个元数据。 + /// Gets or sets the Topic + /// Topic。 /// - /// 元数据类型。 - /// 元数据名称。 - /// 如果指定名称的元数据不存在则返回这个参数。 - /// 元数据值。 - public T GetMetadata(string name, T def = default(T)) - { - if (!Metadatas.ContainsKey(name)) - return def; + public string Topic { get; set; } - return (T)Metadatas[name]; - } + #endregion 属性 - #region Equality members + #region 方法 - /// Determines whether the specified object is equal to the current object. - /// true if the specified object is equal to the current object; otherwise, false. + /// + /// The Equals + /// /// The object to compare with the current object. + /// true if the specified object is equal to the current object; otherwise, false. public override bool Equals(object obj) { var model = obj as MqttDescriptor; @@ -83,13 +73,32 @@ public override bool Equals(object obj) }); } - /// Serves as the default hash function. + /// + /// The GetHashCode + /// /// A hash code for the current object. public override int GetHashCode() { return ToString().GetHashCode(); } + /// + /// 获取一个元数据。 + /// + /// 元数据类型。 + /// 元数据名称。 + /// 如果指定名称的元数据不存在则返回这个参数。 + /// 元数据值。 + public T GetMetadata(string name, T def = default(T)) + { + if (!Metadatas.ContainsKey(name)) + return def; + + return (T)Metadatas[name]; + } + + #endregion 方法 + public static bool operator ==(MqttDescriptor model1, MqttDescriptor model2) { return Equals(model1, model2); @@ -99,7 +108,12 @@ public override int GetHashCode() { return !Equals(model1, model2); } + } - #endregion Equality members + /// + /// 服务描述符扩展方法。 + /// + public static class MqttDescriptorExtensions + { } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Mqtt/MqttServiceDescriptor.cs b/src/Surging.Core/Surging.Core.CPlatform/Mqtt/MqttServiceDescriptor.cs index aac8c3063..01e537c41 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Mqtt/MqttServiceDescriptor.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Mqtt/MqttServiceDescriptor.cs @@ -12,17 +12,25 @@ namespace Surging.Core.CPlatform.Mqtt /// public class MqttEndpointDescriptor { + #region 属性 + /// + /// Gets or sets the Type /// 地址类型。 /// [JsonIgnore] - public string Type { get; set; } + public string Type { get; set; } /// + /// Gets or sets the Value /// 地址值。 /// public string Value { get; set; } + #endregion 属性 + + #region 方法 + /// /// 创建一个描述符。 /// @@ -38,18 +46,29 @@ public class MqttEndpointDescriptor Value = serializer.Serialize(address) }; } + + #endregion 方法 } + /// + /// Defines the + /// public class MqttServiceDescriptor { + #region 属性 + /// + /// Gets or sets the AddressDescriptors /// Mqtt地址描述符集合。 /// public IEnumerable AddressDescriptors { get; set; } /// + /// Gets or sets the MqttDescriptor /// Mqtt描述符。 /// public MqttDescriptor MqttDescriptor { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Mqtt/MqttServiceRoute.cs b/src/Surging.Core/Surging.Core.CPlatform/Mqtt/MqttServiceRoute.cs index 49e56e6fc..dab4badbb 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Mqtt/MqttServiceRoute.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Mqtt/MqttServiceRoute.cs @@ -6,22 +6,34 @@ namespace Surging.Core.CPlatform.Mqtt { - public class MqttServiceRoute + /// + /// Defines the + /// + public class MqttServiceRoute { + #region 属性 + /// - /// Mqtt服务可用地址。 - /// - public IEnumerable MqttEndpoint { get; set; } - /// + /// Gets or sets the MqttDescriptor /// Mqtt服务描述符。 /// public MqttDescriptor MqttDescriptor { get; set; } - #region Equality members + /// + /// Gets or sets the MqttEndpoint + /// Mqtt服务可用地址。 + /// + public IEnumerable MqttEndpoint { get; set; } - /// Determines whether the specified object is equal to the current object. - /// true if the specified object is equal to the current object; otherwise, false. + #endregion 属性 + + #region 方法 + + /// + /// The Equals + /// /// The object to compare with the current object. + /// true if the specified object is equal to the current object; otherwise, false. public override bool Equals(object obj) { var model = obj as MqttServiceRoute; @@ -37,23 +49,25 @@ public override bool Equals(object obj) return model.MqttEndpoint.Count() == MqttEndpoint.Count() && model.MqttEndpoint.All(addressModel => MqttEndpoint.Contains(addressModel)); } - /// Serves as the default hash function. + /// + /// The GetHashCode + /// /// A hash code for the current object. public override int GetHashCode() { return ToString().GetHashCode(); } + #endregion 方法 + public static bool operator ==(MqttServiceRoute model1, MqttServiceRoute model2) { return Equals(model1, model2); } - public static bool operator != (MqttServiceRoute model1, MqttServiceRoute model2) + public static bool operator !=(MqttServiceRoute model1, MqttServiceRoute model2) { return !Equals(model1, model2); } - - #endregion Equality members } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/RequetData.cs b/src/Surging.Core/Surging.Core.CPlatform/RequetData.cs index 5b59464f5..76b5ea3d6 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/RequetData.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/RequetData.cs @@ -4,8 +4,18 @@ namespace Surging.Core.CPlatform { - public class RequestData + /// + /// Defines the + /// + public class RequestData { + #region 属性 + + /// + /// Gets or sets the Payload + /// public string Payload { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Routing/IServiceRouteFactory.cs b/src/Surging.Core/Surging.Core.CPlatform/Routing/IServiceRouteFactory.cs index c673ff723..d4f7e6ca8 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Routing/IServiceRouteFactory.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Routing/IServiceRouteFactory.cs @@ -3,16 +3,24 @@ namespace Surging.Core.CPlatform.Routing { + #region 接口 + /// /// 一个抽象的服务路由工厂。 /// public interface IServiceRouteFactory { + #region 方法 + /// /// 根据服务路由描述符创建服务路由。 /// /// 服务路由描述符。 /// 服务路由集合。 Task> CreateServiceRoutesAsync(IEnumerable descriptors); + + #endregion 方法 } + + #endregion 接口 } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Routing/IServiceRouteManager.cs b/src/Surging.Core/Surging.Core.CPlatform/Routing/IServiceRouteManager.cs index 3acc5eb7d..be3d96c2a 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Routing/IServiceRouteManager.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Routing/IServiceRouteManager.cs @@ -8,11 +8,19 @@ namespace Surging.Core.CPlatform.Routing { + #region 接口 + /// /// 一个抽象的服务路由发现者。 /// public interface IServiceRouteManager { + #region 事件 + + /// + /// 服务路由被修改。 + /// + event EventHandler Changed; /// /// 服务路由被创建。 @@ -24,10 +32,15 @@ public interface IServiceRouteManager /// event EventHandler Removed; + #endregion 事件 + + #region 方法 + /// - /// 服务路由被修改。 + /// 清空所有的服务路由。 /// - event EventHandler Changed; + /// 一个任务。 + Task ClearAsync(); /// /// 获取所有可用的服务路由信息。 @@ -35,47 +48,37 @@ public interface IServiceRouteManager /// 服务路由集合。 Task> GetRoutesAsync(); - /// - /// 设置服务路由。 - /// - /// 服务路由集合。 - /// 一个任务。 - Task SetRoutesAsync(IEnumerable routes); - /// /// 移除地址列表 /// - /// 地址列表。 + /// The Address /// 一个任务。 Task RemveAddressAsync(IEnumerable Address); + /// - /// 清空所有的服务路由。 + /// 设置服务路由。 /// + /// 服务路由集合。 /// 一个任务。 - Task ClearAsync(); + Task SetRoutesAsync(IEnumerable routes); + + #endregion 方法 } + #endregion 接口 + /// /// 服务路由管理者扩展方法。 /// public static class ServiceRouteManagerExtensions { - /// - /// 根据服务Id获取一个服务路由。 - /// - /// 服务路由管理者。 - /// 服务Id。 - /// 服务路由。 - public static async Task GetAsync(this IServiceRouteManager serviceRouteManager, string serviceId) - { - return (await serviceRouteManager.GetRoutesAsync()).SingleOrDefault(i => i.ServiceDescriptor.Id == serviceId); - } - - + #region 方法 /// /// 获取地址 /// + /// The serviceRouteManager + /// The condition /// public static async Task> GetAddressAsync(this IServiceRouteManager serviceRouteManager, string condition = null) { @@ -109,12 +112,36 @@ public static async Task> GetAddressAsync(this IServic return result.Values; } + /// + /// 根据服务Id获取一个服务路由。 + /// + /// 服务路由管理者。 + /// 服务Id。 + /// 服务路由。 + public static async Task GetAsync(this IServiceRouteManager serviceRouteManager, string serviceId) + { + return (await serviceRouteManager.GetRoutesAsync()).SingleOrDefault(i => i.ServiceDescriptor.Id == serviceId); + } + + /// + /// The GetRoutesAsync + /// + /// The serviceRouteManager + /// The address + /// The public static async Task> GetRoutesAsync(this IServiceRouteManager serviceRouteManager, string address) { var routes = await serviceRouteManager.GetRoutesAsync(); return routes.Where(p => p.Address.Any(m => m.ToString() == address)); } + /// + /// The GetServiceDescriptorAsync + /// + /// The serviceRouteManager + /// The address + /// The serviceId + /// The public static async Task> GetServiceDescriptorAsync(this IServiceRouteManager serviceRouteManager, string address, string serviceId = null) { var routes = await serviceRouteManager.GetRoutesAsync(); @@ -129,5 +156,7 @@ public static async Task> GetServiceDescriptorAsy .Select(p => p.ServiceDescriptor); } } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Routing/IServiceRouteProvider.cs b/src/Surging.Core/Surging.Core.CPlatform/Routing/IServiceRouteProvider.cs index f369820a0..d96df78c3 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Routing/IServiceRouteProvider.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Routing/IServiceRouteProvider.cs @@ -3,17 +3,14 @@ namespace Surging.Core.CPlatform.Routing { + #region 接口 + /// /// 服务路由接口 /// public interface IServiceRouteProvider { - /// - /// 根据服务id找到相关服务信息 - /// - /// - /// - Task Locate(string serviceId); + #region 方法 /// /// 根据服务路由路径获取路由信息 @@ -23,11 +20,18 @@ public interface IServiceRouteProvider ValueTask GetRouteByPath(string path); /// - /// 根据服务路由路径找到相关服务信息 + /// The GetRouteByPathRegex /// - /// + /// The path + /// The + ValueTask GetRouteByPathRegex(string path); + + /// + /// 根据服务id找到相关服务信息 + /// + /// /// - Task SearchRoute(string path); + Task Locate(string serviceId); /// /// 注册路由 @@ -36,6 +40,15 @@ public interface IServiceRouteProvider /// Task RegisterRoutes(decimal processorTime); - ValueTask GetRouteByPathRegex(string path); + /// + /// 根据服务路由路径找到相关服务信息 + /// + /// + /// + Task SearchRoute(string path); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Routing/Implementation/DefaultServiceRouteFactory.cs b/src/Surging.Core/Surging.Core.CPlatform/Routing/Implementation/DefaultServiceRouteFactory.cs index 18dd41695..c1d3a2b36 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Routing/Implementation/DefaultServiceRouteFactory.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Routing/Implementation/DefaultServiceRouteFactory.cs @@ -13,16 +13,35 @@ namespace Surging.Core.CPlatform.Routing.Implementation /// public class DefaultServiceRouteFactory : IServiceRouteFactory { - private readonly ISerializer _serializer; - private readonly ConcurrentDictionary _addressModel = + #region 字段 + + /// + /// Defines the _addressModel + /// + private readonly ConcurrentDictionary _addressModel = new ConcurrentDictionary(); + /// + /// Defines the _serializer + /// + private readonly ISerializer _serializer; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serializer public DefaultServiceRouteFactory(ISerializer serializer) { _serializer = serializer; } - #region Implementation of IServiceRouteFactory + #endregion 构造函数 + + #region 方法 /// /// 根据服务路由描述符创建服务路由。 @@ -39,7 +58,6 @@ public Task> CreateServiceRoutesAsync(IEnumerable new ServiceRoute { - Address = CreateAddress(descriptor.AddressDescriptors), ServiceDescriptor = descriptor.ServiceDescriptor })); @@ -47,8 +65,11 @@ public Task> CreateServiceRoutesAsync(IEnumerable + /// The CreateAddress + /// + /// The descriptors + /// The private IEnumerable CreateAddress(IEnumerable descriptors) { if (descriptors == null) @@ -65,5 +86,7 @@ private IEnumerable CreateAddress(IEnumerable + /// Defines the + /// public class DefaultServiceRouteProvider : IServiceRouteProvider { + #region 字段 + + /// + /// Defines the _concurrent + /// private readonly ConcurrentDictionary _concurrent = new ConcurrentDictionary(); + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the _serviceEntryManager + /// + private readonly IServiceEntryManager _serviceEntryManager; + + /// + /// Defines the _serviceRoute + /// private readonly ConcurrentDictionary _serviceRoute = new ConcurrentDictionary(); - private readonly IServiceEntryManager _serviceEntryManager; - private readonly ILogger _logger; + /// + /// Defines the _serviceRouteManager + /// private readonly IServiceRouteManager _serviceRouteManager; + + /// + /// Defines the _serviceTokenGenerator + /// private readonly IServiceTokenGenerator _serviceTokenGenerator; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serviceRouteManager + /// The logger + /// The serviceEntryManager + /// The serviceTokenGenerator public DefaultServiceRouteProvider(IServiceRouteManager serviceRouteManager, ILogger logger, IServiceEntryManager serviceEntryManager, IServiceTokenGenerator serviceTokenGenerator) { @@ -38,26 +76,15 @@ public DefaultServiceRouteProvider(IServiceRouteManager serviceRouteManager, ILo _logger = logger; } - public async Task Locate(string serviceId) - { - _concurrent.TryGetValue(serviceId, out ServiceRoute route); - if (route == null) - { - var routes = await _serviceRouteManager.GetRoutesAsync(); - route = routes.FirstOrDefault(i => i.ServiceDescriptor.Id == serviceId); - if (route == null) - { - if (_logger.IsEnabled(LogLevel.Warning)) - _logger.LogWarning($"根据服务id:{serviceId},找不到相关服务信息。"); - } - else - _concurrent.GetOrAdd(serviceId, route); - } - return route; - } - + #endregion 构造函数 + #region 方法 + /// + /// The GetRouteByPath + /// + /// The path + /// The public ValueTask GetRouteByPath(string path) { _serviceRoute.TryGetValue(path.ToLower(), out ServiceRoute route); @@ -71,6 +98,11 @@ public ValueTask GetRouteByPath(string path) } } + /// + /// The GetRouteByPathRegex + /// + /// The path + /// The public ValueTask GetRouteByPathRegex(string path) { path = path.ToLower(); @@ -85,13 +117,36 @@ public ValueTask GetRouteByPathRegex(string path) } } - public async Task SearchRoute(string path) + /// + /// The Locate + /// + /// The serviceId + /// The + public async Task Locate(string serviceId) { - return await SearchRouteAsync(path); + _concurrent.TryGetValue(serviceId, out ServiceRoute route); + if (route == null) + { + var routes = await _serviceRouteManager.GetRoutesAsync(); + route = routes.FirstOrDefault(i => i.ServiceDescriptor.Id == serviceId); + if (route == null) + { + if (_logger.IsEnabled(LogLevel.Warning)) + _logger.LogWarning($"根据服务id:{serviceId},找不到相关服务信息。"); + } + else + _concurrent.GetOrAdd(serviceId, route); + } + return route; } + /// + /// The RegisterRoutes + /// + /// The processorTime + /// The public async Task RegisterRoutes(decimal processorTime) - { + { var ports = AppConfig.ServerOptions.Ports; var addess = NetUtils.GetHostAddress(); addess.ProcessorTime = processorTime; @@ -105,34 +160,38 @@ public async Task RegisterRoutes(decimal processorTime) ServiceDescriptor = i.Descriptor }; }).ToList(); - await _serviceRouteManager.SetRoutesAsync(addressDescriptors); - } - - #region 私有方法 - private static string GetCacheKey(ServiceDescriptor descriptor) - { - return descriptor.Id; + await _serviceRouteManager.SetRoutesAsync(addressDescriptors); } - private void ServiceRouteManager_Removed(object sender, ServiceRouteEventArgs e) + /// + /// The SearchRoute + /// + /// The path + /// The + public async Task SearchRoute(string path) { - var key = GetCacheKey(e.Route.ServiceDescriptor); - ServiceRoute value; - _concurrent.TryRemove(key, out value); - _serviceRoute.TryRemove(e.Route.ServiceDescriptor.RoutePath, out value); + return await SearchRouteAsync(path); } - private void ServiceRouteManager_Add(object sender, ServiceRouteEventArgs e) + /// + /// The GetCacheKey + /// + /// The descriptor + /// The + private static string GetCacheKey(ServiceDescriptor descriptor) { - var key = GetCacheKey(e.Route.ServiceDescriptor); - _concurrent.GetOrAdd(key, e.Route); - _serviceRoute.GetOrAdd(e.Route.ServiceDescriptor.RoutePath, e.Route); + return descriptor.Id; } - private async Task SearchRouteAsync(string path) + /// + /// The GetRouteByPathAsync + /// + /// The path + /// The + private async Task GetRouteByPathAsync(string path) { var routes = await _serviceRouteManager.GetRoutesAsync(); - var route = routes.FirstOrDefault(i => String.Compare(i.ServiceDescriptor.RoutePath, path, true) == 0); + var route = routes.FirstOrDefault(i => String.Compare(i.ServiceDescriptor.RoutePath, path, true) == 0 && !i.ServiceDescriptor.GetMetadata("IsOverload")); if (route == null) { if (_logger.IsEnabled(LogLevel.Warning)) @@ -143,43 +202,77 @@ private async Task SearchRouteAsync(string path) return route; } - private async Task GetRouteByPathAsync(string path) + /// + /// The GetRouteByPathRegexAsync + /// + /// The path + /// The + private async Task GetRouteByPathRegexAsync(string path) { var routes = await _serviceRouteManager.GetRoutesAsync(); - var route = routes.FirstOrDefault(i => String.Compare(i.ServiceDescriptor.RoutePath, path, true) == 0 && !i.ServiceDescriptor.GetMetadata("IsOverload")); + var pattern = "/{.*?}"; + + var route = routes.FirstOrDefault(i => + { + var routePath = Regex.Replace(i.ServiceDescriptor.RoutePath, pattern, ""); + var newPath = path.Replace(routePath, ""); + return (newPath.StartsWith("/") || newPath.Length == 0) && i.ServiceDescriptor.RoutePath.Split("/").Length == path.Split("/").Length && !i.ServiceDescriptor.GetMetadata("IsOverload"); + }); + if (route == null) { if (_logger.IsEnabled(LogLevel.Warning)) _logger.LogWarning($"根据服务路由路径:{path},找不到相关服务信息。"); } else - _serviceRoute.GetOrAdd(path, route); + if (!Regex.IsMatch(route.ServiceDescriptor.RoutePath, pattern)) _serviceRoute.GetOrAdd(path, route); return route; } - private async Task GetRouteByPathRegexAsync(string path) + /// + /// The SearchRouteAsync + /// + /// The path + /// The + private async Task SearchRouteAsync(string path) { var routes = await _serviceRouteManager.GetRoutesAsync(); - var pattern = "/{.*?}"; - - var route = routes.FirstOrDefault(i => - { - var routePath = Regex.Replace(i.ServiceDescriptor.RoutePath, pattern, ""); - var newPath = path.Replace(routePath, ""); - return (newPath.StartsWith("/")|| newPath.Length==0) && i.ServiceDescriptor.RoutePath.Split("/").Length == path.Split("/").Length && !i.ServiceDescriptor.GetMetadata("IsOverload"); - }); - - + var route = routes.FirstOrDefault(i => String.Compare(i.ServiceDescriptor.RoutePath, path, true) == 0); if (route == null) { if (_logger.IsEnabled(LogLevel.Warning)) _logger.LogWarning($"根据服务路由路径:{path},找不到相关服务信息。"); } else - if(!Regex.IsMatch(route.ServiceDescriptor.RoutePath, pattern)) _serviceRoute.GetOrAdd(path, route); + _serviceRoute.GetOrAdd(path, route); return route; } - #endregion + /// + /// The ServiceRouteManager_Add + /// + /// The sender + /// The e + private void ServiceRouteManager_Add(object sender, ServiceRouteEventArgs e) + { + var key = GetCacheKey(e.Route.ServiceDescriptor); + _concurrent.GetOrAdd(key, e.Route); + _serviceRoute.GetOrAdd(e.Route.ServiceDescriptor.RoutePath, e.Route); + } + + /// + /// The ServiceRouteManager_Removed + /// + /// The sender + /// The e + private void ServiceRouteManager_Removed(object sender, ServiceRouteEventArgs e) + { + var key = GetCacheKey(e.Route.ServiceDescriptor); + ServiceRoute value; + _concurrent.TryRemove(key, out value); + _serviceRoute.TryRemove(e.Route.ServiceDescriptor.RoutePath, out value); + } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Routing/Implementation/ServiceRouteManagerBase.cs b/src/Surging.Core/Surging.Core.CPlatform/Routing/Implementation/ServiceRouteManagerBase.cs index 3c8a6d5fc..f52620947 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Routing/Implementation/ServiceRouteManagerBase.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Routing/Implementation/ServiceRouteManagerBase.cs @@ -8,35 +8,62 @@ namespace Surging.Core.CPlatform.Routing.Implementation { /// - /// 服务路由事件参数。 + /// 服务路由变更事件参数。 /// - public class ServiceRouteEventArgs + public class ServiceRouteChangedEventArgs : ServiceRouteEventArgs { - public ServiceRouteEventArgs(ServiceRoute route) + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The route + /// The oldRoute + public ServiceRouteChangedEventArgs(ServiceRoute route, ServiceRoute oldRoute) : base(route) { - Route = route; + OldRoute = oldRoute; } + #endregion 构造函数 + + #region 属性 + /// - /// 服务路由信息。 + /// Gets or sets the OldRoute + /// 旧的服务路由信息。 /// - public ServiceRoute Route { get; private set; } + public ServiceRoute OldRoute { get; set; } + + #endregion 属性 } /// - /// 服务路由变更事件参数。 + /// 服务路由事件参数。 /// - public class ServiceRouteChangedEventArgs : ServiceRouteEventArgs + public class ServiceRouteEventArgs { - public ServiceRouteChangedEventArgs(ServiceRoute route, ServiceRoute oldRoute) : base(route) + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The route + public ServiceRouteEventArgs(ServiceRoute route) { - OldRoute = oldRoute; + Route = route; } + #endregion 构造函数 + + #region 属性 + /// - /// 旧的服务路由信息。 + /// Gets the Route + /// 服务路由信息。 /// - public ServiceRoute OldRoute { get; set; } + public ServiceRoute Route { get; private set; } + + #endregion 属性 } /// @@ -54,8 +81,6 @@ protected ServiceRouteManagerBase(ISerializer serializer) _serializer = serializer; } - #region Implementation of IServiceRouteManager - /// /// 服务路由被创建。 /// @@ -110,6 +135,7 @@ public virtual Task SetRoutesAsync(IEnumerable routes) return SetRoutesAsync(descriptors); } + public abstract Task RemveAddressAsync(IEnumerable Address); /// @@ -118,15 +144,13 @@ public virtual Task SetRoutesAsync(IEnumerable routes) /// 一个任务。 public abstract Task ClearAsync(); - #endregion Implementation of IServiceRouteManager - /// /// 设置服务路由。 /// /// 服务路由集合。 /// 一个任务。 protected abstract Task SetRoutesAsync(IEnumerable routes); - + protected void OnCreated(params ServiceRouteEventArgs[] args) { if (_created == null) diff --git a/src/Surging.Core/Surging.Core.CPlatform/Routing/Implementation/SharedFileServiceRouteManager.cs b/src/Surging.Core/Surging.Core.CPlatform/Routing/Implementation/SharedFileServiceRouteManager.cs index bf7aba2af..a7ff9c7fd 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Routing/Implementation/SharedFileServiceRouteManager.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Routing/Implementation/SharedFileServiceRouteManager.cs @@ -15,19 +15,49 @@ namespace Surging.Core.CPlatform.Routing.Implementation /// public class SharedFileServiceRouteManager : ServiceRouteManagerBase, IDisposable { - #region Field + #region 字段 + /// + /// Defines the _filePath + /// private readonly string _filePath; + + /// + /// Defines the _fileSystemWatcher + /// + private readonly FileSystemWatcher _fileSystemWatcher; + + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the _serializer + /// private readonly ISerializer _serializer; + + /// + /// Defines the _serviceRouteFactory + /// private readonly IServiceRouteFactory _serviceRouteFactory; - private readonly ILogger _logger; + + /// + /// Defines the _routes + /// private ServiceRoute[] _routes; - private readonly FileSystemWatcher _fileSystemWatcher; - #endregion Field + #endregion 字段 - #region Constructor + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The filePath + /// The serializer + /// The serviceRouteFactory + /// The logger public SharedFileServiceRouteManager(string filePath, ISerializer serializer, IServiceRouteFactory serviceRouteFactory, ILogger logger) : base(serializer) { @@ -48,9 +78,20 @@ public SharedFileServiceRouteManager(string filePath, ISerializer serial _fileSystemWatcher.EnableRaisingEvents = true; } - #endregion Constructor + #endregion 构造函数 + + #region 方法 - #region Implementation of IDisposable + /// + /// 清空所有的服务路由。 + /// + /// 一个任务。 + public override Task ClearAsync() + { + if (File.Exists(_filePath)) + File.Delete(_filePath); + return Task.FromResult(0); + } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. @@ -60,12 +101,8 @@ public void Dispose() _fileSystemWatcher?.Dispose(); } - #endregion Implementation of IDisposable - - #region Overrides of ServiceRouteManagerBase - /// - /// 获取所有可用的服务路由信息。 + /// 获取所有可用的服务路由信息。 /// /// 服务路由集合。 public override async Task> GetRoutesAsync() @@ -76,18 +113,22 @@ public override async Task> GetRoutesAsync() } /// - /// 清空所有的服务路由。 + /// The RemveAddressAsync /// - /// 一个任务。 - public override Task ClearAsync() + /// The Address + /// The + public override async Task RemveAddressAsync(IEnumerable Address) { - if (File.Exists(_filePath)) - File.Delete(_filePath); - return Task.FromResult(0); + var routes = await GetRoutesAsync(); + foreach (var route in routes) + { + route.Address = route.Address.Except(Address); + } + await base.SetRoutesAsync(routes); } /// - /// 设置服务路由。 + /// 设置服务路由。 /// /// 服务路由集合。 /// 一个任务。 @@ -103,71 +144,45 @@ protected override async Task SetRoutesAsync(IEnumerable } } - public override async Task RemveAddressAsync(IEnumerable Address) + /// + /// The _fileSystemWatcher_Changed + /// + /// The sender + /// The e + private async void _fileSystemWatcher_Changed(object sender, FileSystemEventArgs e) { - var routes = await GetRoutesAsync(); - foreach (var route in routes) - { - route.Address = route.Address.Except(Address); - } - await base.SetRoutesAsync(routes); - } - - #endregion Overrides of ServiceRouteManagerBase - - #region Private Method + if (_logger.IsEnabled(LogLevel.Information)) + _logger.LogInformation($"文件{_filePath}发生了变更,将重新获取路由信息。"); - private async Task> GetRoutes(string file) - { - ServiceRoute[] routes; - if (File.Exists(file)) + if (e.ChangeType == WatcherChangeTypes.Changed) { - if (_logger.IsEnabled(LogLevel.Debug)) - _logger.LogDebug($"准备从文件:{file}中获取服务路由。"); string content; - while (true) + try { - try - { - using ( - var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - var reader = new StreamReader(fileStream, Encoding.UTF8); - content = await reader.ReadToEndAsync(); - } - break; - } - catch (IOException) - { - } + content = File.ReadAllText(_filePath, Encoding.UTF8); } - try + catch (IOException) //还没有操作完,忽略本次修改 { - var serializer = _serializer; - routes = - (await - _serviceRouteFactory.CreateServiceRoutesAsync( - serializer.Deserialize(content))).ToArray(); - if (_logger.IsEnabled(LogLevel.Information)) - _logger.LogInformation( - $"成功获取到以下路由信息:{string.Join(",", routes.Select(i => i.ServiceDescriptor.Id))}。"); + return; } - catch (Exception exception) + if (!string.IsNullOrWhiteSpace(content)) { - if (_logger.IsEnabled(LogLevel.Error)) - _logger.LogError(exception,"获取路由信息时发生了错误。"); - routes = new ServiceRoute[0]; + await EntryRoutes(_filePath); + } + else + { + return; } } - else - { - if (_logger.IsEnabled(LogLevel.Warning)) - _logger.LogWarning($"无法获取路由信息,因为文件:{file}不存在。"); - routes = new ServiceRoute[0]; - } - return routes; + + await EntryRoutes(_filePath); } + /// + /// The EntryRoutes + /// + /// The file + /// The private async Task EntryRoutes(string file) { var oldRoutes = _routes?.ToArray(); @@ -222,35 +237,62 @@ private async Task EntryRoutes(string file) } } - private async void _fileSystemWatcher_Changed(object sender, FileSystemEventArgs e) + /// + /// The GetRoutes + /// + /// The file + /// The + private async Task> GetRoutes(string file) { - if (_logger.IsEnabled(LogLevel.Information)) - _logger.LogInformation($"文件{_filePath}发生了变更,将重新获取路由信息。"); - - if (e.ChangeType == WatcherChangeTypes.Changed) + ServiceRoute[] routes; + if (File.Exists(file)) { + if (_logger.IsEnabled(LogLevel.Debug)) + _logger.LogDebug($"准备从文件:{file}中获取服务路由。"); string content; - try - { - content = File.ReadAllText(_filePath, Encoding.UTF8); - } - catch (IOException) //还没有操作完,忽略本次修改 + while (true) { - return; + try + { + using ( + var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + var reader = new StreamReader(fileStream, Encoding.UTF8); + content = await reader.ReadToEndAsync(); + } + break; + } + catch (IOException) + { + } } - if (!string.IsNullOrWhiteSpace(content)) + try { - await EntryRoutes(_filePath); + var serializer = _serializer; + routes = + (await + _serviceRouteFactory.CreateServiceRoutesAsync( + serializer.Deserialize(content))).ToArray(); + if (_logger.IsEnabled(LogLevel.Information)) + _logger.LogInformation( + $"成功获取到以下路由信息:{string.Join(",", routes.Select(i => i.ServiceDescriptor.Id))}。"); } - else + catch (Exception exception) { - return; + if (_logger.IsEnabled(LogLevel.Error)) + _logger.LogError(exception, "获取路由信息时发生了错误。"); + routes = new ServiceRoute[0]; } } - - await EntryRoutes(_filePath); + else + { + if (_logger.IsEnabled(LogLevel.Warning)) + _logger.LogWarning($"无法获取路由信息,因为文件:{file}不存在。"); + routes = new ServiceRoute[0]; + } + return routes; } - #endregion Private Method + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Routing/ServiceRoute.cs b/src/Surging.Core/Surging.Core.CPlatform/Routing/ServiceRoute.cs index 962773bca..7445bece7 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Routing/ServiceRoute.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Routing/ServiceRoute.cs @@ -9,20 +9,29 @@ namespace Surging.Core.CPlatform.Routing /// public class ServiceRoute { + #region 属性 + /// + /// Gets or sets the Address /// 服务可用地址。 /// public IEnumerable Address { get; set; } + /// + /// Gets or sets the ServiceDescriptor /// 服务描述符。 /// public ServiceDescriptor ServiceDescriptor { get; set; } - #region Equality members + #endregion 属性 - /// Determines whether the specified object is equal to the current object. - /// true if the specified object is equal to the current object; otherwise, false. + #region 方法 + + /// + /// The Equals + /// /// The object to compare with the current object. + /// true if the specified object is equal to the current object; otherwise, false. public override bool Equals(object obj) { var model = obj as ServiceRoute; @@ -38,13 +47,17 @@ public override bool Equals(object obj) return model.Address.Count() == Address.Count() && model.Address.All(addressModel => Address.Contains(addressModel)); } - /// Serves as the default hash function. + /// + /// The GetHashCode + /// /// A hash code for the current object. public override int GetHashCode() { return ToString().GetHashCode(); } + #endregion 方法 + public static bool operator ==(ServiceRoute model1, ServiceRoute model2) { return Equals(model1, model2); @@ -54,7 +67,5 @@ public override int GetHashCode() { return !Equals(model1, model2); } - - #endregion Equality members } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Routing/ServiceRouteContext.cs b/src/Surging.Core/Surging.Core.CPlatform/Routing/ServiceRouteContext.cs index b021c5601..aaf592993 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Routing/ServiceRouteContext.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Routing/ServiceRouteContext.cs @@ -8,12 +8,28 @@ namespace Surging.Core.CPlatform.Routing { - public class ServiceRouteContext - { - public ServiceRoute Route { get; set; } + /// + /// Defines the + /// + public class ServiceRouteContext + { + #region 属性 + /// + /// Gets or sets the InvokeMessage + /// + public RemoteInvokeMessage InvokeMessage { get; set; } + + /// + /// Gets or sets the ResultMessage + /// public RemoteInvokeResultMessage ResultMessage { get; set; } - public RemoteInvokeMessage InvokeMessage { get; set; } + /// + /// Gets or sets the Route + /// + public ServiceRoute Route { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Routing/ServiceRouteDescriptor.cs b/src/Surging.Core/Surging.Core.CPlatform/Routing/ServiceRouteDescriptor.cs index 2248acb5a..4b7cc58fb 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Routing/ServiceRouteDescriptor.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Routing/ServiceRouteDescriptor.cs @@ -11,18 +11,22 @@ namespace Surging.Core.CPlatform.Routing /// public class ServiceAddressDescriptor { + #region 属性 + /// + /// Gets or sets the Type /// 地址类型。 /// [JsonIgnore] public string Type { get; set; } /// + /// Gets or sets the Value /// 地址值。 /// public string Value { get; set; } - + #endregion 属性 } /// @@ -30,14 +34,20 @@ public class ServiceAddressDescriptor /// public class ServiceRouteDescriptor { + #region 属性 + /// + /// Gets or sets the AddressDescriptors /// 服务地址描述符集合。 /// public IEnumerable AddressDescriptors { get; set; } /// + /// Gets or sets the ServiceDescriptor /// 服务描述符。 /// public ServiceDescriptor ServiceDescriptor { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Routing/ServiceRouteWatch.cs b/src/Surging.Core/Surging.Core.CPlatform/Routing/ServiceRouteWatch.cs index 95f01afed..d11c499b9 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Routing/ServiceRouteWatch.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Routing/ServiceRouteWatch.cs @@ -8,10 +8,28 @@ namespace Surging.Core.CPlatform.Routing { + /// + /// Defines the + /// public class ServiceRouteWatch : ConfigurationWatch { + #region 字段 + + /// + /// Defines the _action + /// private readonly Action _action; - public ServiceRouteWatch(CPlatformContainer serviceProvider, Action action) + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serviceProvider + /// The action + public ServiceRouteWatch(CPlatformContainer serviceProvider, Action action) { this._action = action; if (serviceProvider.IsRegistered()) @@ -19,10 +37,19 @@ public ServiceRouteWatch(CPlatformContainer serviceProvider, Action action) _action.Invoke(); } + #endregion 构造函数 + + #region 方法 + + /// + /// The Process + /// + /// The public override async Task Process() { await Task.Run(_action); } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Routing/Template/RoutePatternParser.cs b/src/Surging.Core/Surging.Core.CPlatform/Routing/Template/RoutePatternParser.cs index 9d52ce7cb..bc27f2a79 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Routing/Template/RoutePatternParser.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Routing/Template/RoutePatternParser.cs @@ -6,13 +6,23 @@ namespace Surging.Core.CPlatform.Routing.Template { + /// + /// Defines the + /// public class RoutePatternParser { - public static string Parse(string routeTemplet, string service, string method) + #region 方法 + + /// + /// The Parse + /// + /// The routeTemplet + /// The service + /// The + public static string Parse(string routeTemplet, string service) { StringBuilder result = new StringBuilder(); var parameters = routeTemplet.Split(@"/"); - bool isAppendMethod=false; foreach (var parameter in parameters) { var param = GetParameters(parameter).FirstOrDefault(); @@ -24,28 +34,24 @@ public static string Parse(string routeTemplet, string service, string method) { result.Append(service.Substring(1, service.Length - param.Length - 1)); } - else if (param == "Method") - { - result.Append(method); - isAppendMethod = true; - } - else - { - if (!isAppendMethod) result.AppendFormat("{0}/", method); - result.Append(parameter); - isAppendMethod = true; - } result.Append("/"); } - result.Length = result.Length - 1; - if (!isAppendMethod) result.AppendFormat("/{0}", method); - return result.ToString().ToLower(); + + return result.ToString().TrimEnd('/').ToLower(); } - public static string Parse(string routeTemplet, string service) + /// + /// The Parse + /// + /// The routeTemplet + /// The service + /// The method + /// The + public static string Parse(string routeTemplet, string service, string method) { StringBuilder result = new StringBuilder(); var parameters = routeTemplet.Split(@"/"); + bool isAppendMethod = false; foreach (var parameter in parameters) { var param = GetParameters(parameter).FirstOrDefault(); @@ -57,12 +63,29 @@ public static string Parse(string routeTemplet, string service) { result.Append(service.Substring(1, service.Length - param.Length - 1)); } + else if (param == "Method") + { + result.Append(method); + isAppendMethod = true; + } + else + { + if (!isAppendMethod) result.AppendFormat("{0}/", method); + result.Append(parameter); + isAppendMethod = true; + } result.Append("/"); } - - return result.ToString().TrimEnd('/').ToLower(); + result.Length = result.Length - 1; + if (!isAppendMethod) result.AppendFormat("/{0}", method); + return result.ToString().ToLower(); } + /// + /// The GetParameters + /// + /// The text + /// The private static List GetParameters(string text) { var matchVale = new List(); @@ -74,5 +97,7 @@ private static List GetParameters(string text) } return matchVale; } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Routing/Template/RouteTemplateSegmenter.cs b/src/Surging.Core/Surging.Core.CPlatform/Routing/Template/RouteTemplateSegmenter.cs index 0d3839247..40e3da207 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Routing/Template/RouteTemplateSegmenter.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Routing/Template/RouteTemplateSegmenter.cs @@ -5,24 +5,37 @@ namespace Surging.Core.CPlatform.Routing.Template { - public class RouteTemplateSegmenter + /// + /// Defines the + /// + public class RouteTemplateSegmenter { - public static Dictionary Segment(string routePath, string path) + #region 方法 + + /// + /// The Segment + /// + /// The routePath + /// The path + /// The + public static Dictionary Segment(string routePath, string path) { var pattern = "/{.*?}"; var result = new Dictionary(); - if ( Regex.IsMatch(routePath, pattern)) + if (Regex.IsMatch(routePath, pattern)) { - var routeTemplate= Regex.Replace(routePath, pattern, ""); + var routeTemplate = Regex.Replace(routePath, pattern, ""); var routeSegments = routeTemplate.Split('/'); var pathSegments = path.Split('/'); var segments = routePath.Split("/"); - for(var i= routeSegments.Length;i< pathSegments.Length;i++) + for (var i = routeSegments.Length; i < pathSegments.Length; i++) { - result.Add(segments[i].Replace("{","").Replace("}", ""), pathSegments[i]); + result.Add(segments[i].Replace("{", "").Replace("}", ""), pathSegments[i]); } } return result; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/IAddressResolver.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/IAddressResolver.cs index 57045a465..759f8e057 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/IAddressResolver.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/IAddressResolver.cs @@ -3,16 +3,25 @@ namespace Surging.Core.CPlatform.Runtime.Client.Address.Resolvers { + #region 接口 + /// /// 一个抽象的服务地址解析器。 /// public interface IAddressResolver { + #region 方法 + /// /// 解析服务地址。 /// /// 服务Id。 + /// The item /// 服务地址模型。 ValueTask Resolver(string serviceId, string item); + + #endregion 方法 } + + #endregion 接口 } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/DefaultAddressResolver.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/DefaultAddressResolver.cs index cdb9730fb..77f1f8bcd 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/DefaultAddressResolver.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/DefaultAddressResolver.cs @@ -19,22 +19,63 @@ namespace Surging.Core.CPlatform.Runtime.Client.Address.Resolvers.Implementation /// public class DefaultAddressResolver : IAddressResolver { - #region Field + #region 字段 - private readonly IServiceRouteManager _serviceRouteManager; - private readonly ILogger _logger; - private readonly IHealthCheckService _healthCheckService; - private readonly CPlatformContainer _container; - private readonly ConcurrentDictionary _addressSelectors=new + /// + /// Defines the _addressSelectors + /// + private readonly ConcurrentDictionary _addressSelectors = new ConcurrentDictionary(); + + /// + /// Defines the _commandProvider + /// private readonly IServiceCommandProvider _commandProvider; + + /// + /// Defines the _concurrent + /// private readonly ConcurrentDictionary _concurrent = new ConcurrentDictionary(); + + /// + /// Defines the _container + /// + private readonly CPlatformContainer _container; + + /// + /// Defines the _healthCheckService + /// + private readonly IHealthCheckService _healthCheckService; + + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the _serviceHeartbeatManager + /// private readonly IServiceHeartbeatManager _serviceHeartbeatManager; - #endregion Field - #region Constructor + /// + /// Defines the _serviceRouteManager + /// + private readonly IServiceRouteManager _serviceRouteManager; + + #endregion 字段 + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The commandProvider + /// The serviceRouteManager + /// The logger + /// The container + /// The healthCheckService + /// The serviceHeartbeatManager public DefaultAddressResolver(IServiceCommandProvider commandProvider, IServiceRouteManager serviceRouteManager, ILogger logger, CPlatformContainer container, IHealthCheckService healthCheckService, IServiceHeartbeatManager serviceHeartbeatManager) @@ -51,24 +92,16 @@ public DefaultAddressResolver(IServiceCommandProvider commandProvider, IServiceR serviceRouteManager.Created += ServiceRouteManager_Add; } - #endregion Constructor + #endregion 构造函数 - #region Implementation of IAddressResolver + #region 方法 /// /// 解析服务地址。 /// /// 服务Id。 + /// The item /// 服务地址模型。 - /// 1.从字典中拿到serviceroute对象 - /// 2.从字典中拿到服务描述符集合 - /// 3.获取或添加serviceroute - /// 4.添加服务id到白名单 - /// 5.根据服务描述符得到地址并判断地址是否是可用的(地址应该是多个) - /// 6.添加到集合中 - /// 7.拿到服务命今 - /// 8.根据负载分流策略拿到一个选择器 - /// 9.返回addressmodel public async ValueTask Resolver(string serviceId, string item) { if (_logger.IsEnabled(LogLevel.Debug)) @@ -129,32 +162,50 @@ public async ValueTask Resolver(string serviceId, string item) return vt.IsCompletedSuccessfully ? vt.Result : await vt; } + /// + /// The GetCacheKey + /// + /// The descriptor + /// The private static string GetCacheKey(ServiceDescriptor descriptor) { return descriptor.Id; } - private void ServiceRouteManager_Removed(object sender, ServiceRouteEventArgs e) + /// + /// The LoadAddressSelectors + /// + private void LoadAddressSelectors() { - var key = GetCacheKey(e.Route.ServiceDescriptor); - ServiceRoute value; - _concurrent.TryRemove(key, out value); + foreach (AddressSelectorMode item in Enum.GetValues(typeof(AddressSelectorMode))) + { + _addressSelectors.TryAdd(item.ToString(), _container.GetInstances(item.ToString())); + } } + /// + /// The ServiceRouteManager_Add + /// + /// The sender + /// The e private void ServiceRouteManager_Add(object sender, ServiceRouteEventArgs e) { var key = GetCacheKey(e.Route.ServiceDescriptor); _concurrent.GetOrAdd(key, e.Route); } - private void LoadAddressSelectors() + /// + /// The ServiceRouteManager_Removed + /// + /// The sender + /// The e + private void ServiceRouteManager_Removed(object sender, ServiceRouteEventArgs e) { - foreach (AddressSelectorMode item in Enum.GetValues(typeof(AddressSelectorMode))) - { - _addressSelectors.TryAdd( item.ToString(), _container.GetInstances(item.ToString())); - } + var key = GetCacheKey(e.Route.ServiceDescriptor); + ServiceRoute value; + _concurrent.TryRemove(key, out value); } - #endregion Implementation of IAddressResolver + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/IAddressSelector.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/IAddressSelector.cs index b0479e86f..103e48e6f 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/IAddressSelector.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/IAddressSelector.cs @@ -4,36 +4,52 @@ namespace Surging.Core.CPlatform.Runtime.Client.Address.Resolvers.Implementation.Selectors { + #region 接口 + + /// + /// 一个抽象的地址选择器。 + /// + public interface IAddressSelector + { + #region 方法 + + /// + /// 选择一个地址。 + /// + /// 地址选择上下文。 + /// 地址模型。 + ValueTask SelectAsync(AddressSelectContext context); + + #endregion 方法 + } + + #endregion 接口 + /// /// 地址选择上下文。 /// public class AddressSelectContext { + #region 属性 + /// + /// Gets or sets the Address + /// 服务可用地址。 + /// + public IEnumerable Address { get; set; } + + /// + /// Gets or sets the Descriptor /// 服务描述符。 /// public ServiceDescriptor Descriptor { get; set; } /// + /// Gets or sets the Item /// 哈希参数 /// public string Item { get; set; } - /// - /// 服务可用地址。 - /// - public IEnumerable Address { get; set; } - } - /// - /// 一个抽象的地址选择器。 - /// - public interface IAddressSelector - { - /// - /// 选择一个地址。 - /// - /// 地址选择上下文。 - /// 地址模型。 - ValueTask SelectAsync(AddressSelectContext context); + #endregion 属性 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/Implementation/AddressSelectorBase.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/Implementation/AddressSelectorBase.cs index b03b7b169..85cf82cca 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/Implementation/AddressSelectorBase.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/Implementation/AddressSelectorBase.cs @@ -10,7 +10,14 @@ namespace Surging.Core.CPlatform.Runtime.Client.Address.Resolvers.Implementation /// public abstract class AddressSelectorBase : IAddressSelector { - #region Implementation of IAddressSelector + #region 方法 + + /// + /// 选择一个地址。 + /// + /// 地址选择上下文。 + /// 地址模型。 + protected abstract ValueTask SelectAsync(AddressSelectContext context); /// /// 选择一个地址。 @@ -29,7 +36,7 @@ async ValueTask IAddressSelector.SelectAsync(AddressSelectContext // var address = context.Address.ToArray(); if (context.Address.Count() == 0) throw new ArgumentException("没有任何地址信息。", nameof(context.Address)); - + if (context.Address.Count() == 1) { return context.Address.First(); @@ -41,13 +48,6 @@ async ValueTask IAddressSelector.SelectAsync(AddressSelectContext } } - #endregion Implementation of IAddressSelector - - /// - /// 选择一个地址。 - /// - /// 地址选择上下文。 - /// 地址模型。 - protected abstract ValueTask SelectAsync(AddressSelectContext context); + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/Implementation/AddressSelectorMode.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/Implementation/AddressSelectorMode.cs index 95862c9de..95b925549 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/Implementation/AddressSelectorMode.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/Implementation/AddressSelectorMode.cs @@ -4,6 +4,8 @@ namespace Surging.Core.CPlatform.Runtime.Client.Address.Resolvers.Implementation.Selectors.Implementation { + #region 枚举 + /// /// 负载均衡模式 /// @@ -13,17 +15,22 @@ public enum AddressSelectorMode /// Hash算法 /// HashAlgorithm, + /// /// 轮训 /// Polling, + /// /// 随机 /// Random, + /// /// 压力最小优先 /// FairPolling, } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/Implementation/FairPollingAdrSelector.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/Implementation/FairPollingAdrSelector.cs index f91310f0e..4053c5794 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/Implementation/FairPollingAdrSelector.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/Implementation/FairPollingAdrSelector.cs @@ -1,23 +1,43 @@ -using System; +using Surging.Core.CPlatform.Address; +using Surging.Core.CPlatform.Routing; +using Surging.Core.CPlatform.Routing.Implementation; +using Surging.Core.CPlatform.Runtime.Client.HealthChecks; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Surging.Core.CPlatform.Address; -using Surging.Core.CPlatform.Routing; -using Surging.Core.CPlatform.Routing.Implementation; -using Surging.Core.CPlatform.Runtime.Client.HealthChecks; namespace Surging.Core.CPlatform.Runtime.Client.Address.Resolvers.Implementation.Selectors.Implementation { + /// + /// Defines the + /// public class FairPollingAdrSelector : AddressSelectorBase { - private readonly IHealthCheckService _healthCheckService; + #region 字段 + /// + /// Defines the _concurrent + /// private readonly ConcurrentDictionary> _concurrent = new ConcurrentDictionary>(); + /// + /// Defines the _healthCheckService + /// + private readonly IHealthCheckService _healthCheckService; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serviceRouteManager + /// The healthCheckService public FairPollingAdrSelector(IServiceRouteManager serviceRouteManager, IHealthCheckService healthCheckService) { _healthCheckService = healthCheckService; @@ -26,18 +46,15 @@ public FairPollingAdrSelector(IServiceRouteManager serviceRouteManager, IHealthC serviceRouteManager.Removed += ServiceRouteManager_Removed; } - private void ServiceRouteManager_Removed(object sender, ServiceRouteEventArgs e) - { - var key = GetCacheKey(e.Route.ServiceDescriptor); - Lazy value; - _concurrent.TryRemove(key, out value); - } + #endregion 构造函数 - private static string GetCacheKey(ServiceDescriptor descriptor) - { - return descriptor.Id; - } + #region 方法 + /// + /// The SelectAsync + /// + /// The context + /// The protected override async ValueTask SelectAsync(AddressSelectContext context) { var key = GetCacheKey(context.Descriptor); @@ -59,31 +76,79 @@ protected override async ValueTask SelectAsync(AddressSelectContex return addressModel; } - #region Help Class + /// + /// The GetCacheKey + /// + /// The descriptor + /// The + private static string GetCacheKey(ServiceDescriptor descriptor) + { + return descriptor.Id; + } + + /// + /// The ServiceRouteManager_Removed + /// + /// The sender + /// The e + private void ServiceRouteManager_Removed(object sender, ServiceRouteEventArgs e) + { + var key = GetCacheKey(e.Route.ServiceDescriptor); + Lazy value; + _concurrent.TryRemove(key, out value); + } + #endregion 方法 + + /// + /// Defines the + /// protected class AddressEntry { - #region Field + #region 字段 + + /// + /// Defines the _address + /// + private readonly AddressModel[] _address; + + /// + /// Defines the _maxIndex + /// + private readonly int _maxIndex; + /// + /// Defines the _index + /// private int _index; + + /// + /// Defines the _lock + /// private int _lock; - private readonly int _maxIndex; - private readonly AddressModel[] _address; - #endregion Field + #endregion 字段 - #region Constructor + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The address public AddressEntry(IEnumerable address) { - _address = address.OrderBy(p=>p.ProcessorTime).ToArray(); + _address = address.OrderBy(p => p.ProcessorTime).ToArray(); _maxIndex = _address.Length - 1; } - - #endregion Constructor - #region Public Method + #endregion 构造函数 + #region 方法 + + /// + /// The GetAddress + /// + /// The public AddressModel GetAddress() { while (true) @@ -110,9 +175,7 @@ public AddressModel GetAddress() } } - #endregion Public Method + #endregion 方法 } - - #endregion Help Class } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/Implementation/HashAlgorithmAdrSelector.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/Implementation/HashAlgorithmAdrSelector.cs index 5c3f293c6..65eff462a 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/Implementation/HashAlgorithmAdrSelector.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/Implementation/HashAlgorithmAdrSelector.cs @@ -14,14 +14,45 @@ namespace Surging.Core.CPlatform.Runtime.Client.Address.Resolvers.Implementation.Selectors.Implementation { - public class HashAlgorithmAdrSelector : AddressSelectorBase + /// + /// Defines the + /// + public class HashAlgorithmAdrSelector : AddressSelectorBase { - private readonly IHealthCheckService _healthCheckService; + #region 字段 + + /// + /// Defines the _concurrent + /// private readonly ConcurrentDictionary> _concurrent = new ConcurrentDictionary>(); + + /// + /// Defines the _hashAlgorithm + /// + private readonly IHashAlgorithm _hashAlgorithm; + + /// + /// Defines the _healthCheckService + /// + private readonly IHealthCheckService _healthCheckService; + + /// + /// Defines the _unHealths + /// private readonly List> _unHealths = new List>(); - private readonly IHashAlgorithm _hashAlgorithm; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serviceRouteManager + /// The healthCheckService + /// The hashAlgorithm public HashAlgorithmAdrSelector(IServiceRouteManager serviceRouteManager, IHealthCheckService healthCheckService, IHashAlgorithm hashAlgorithm) { _healthCheckService = healthCheckService; @@ -31,7 +62,10 @@ public HashAlgorithmAdrSelector(IServiceRouteManager serviceRouteManager, IHealt serviceRouteManager.Removed += ServiceRouteManager_Removed; } - #region Overrides of AddressSelectorBase + #endregion 构造函数 + + #region 方法 + /// /// 选择一个地址。 /// @@ -47,11 +81,11 @@ protected override async ValueTask SelectAsync(AddressSelectContex var hash = new ConsistentHash(_hashAlgorithm, len); foreach (var address in context.Address) { - hash.Add(address,address.ToString()); + hash.Add(address, address.ToString()); } return hash; }); - AddressModel addressModel; + AddressModel addressModel; var IsHealth = false; var index = 0; var count = context.Address.Count(); @@ -65,22 +99,34 @@ protected override async ValueTask SelectAsync(AddressSelectContex } index++; IsHealth = await _healthCheckService.IsHealth(addressModel); - if(!IsHealth) + if (!IsHealth) { - addressEntry.Remove(addressModel.ToString()); - _unHealths.Add(new ValueTuple(key,addressModel)); + addressEntry.Remove(addressModel.ToString()); + _unHealths.Add(new ValueTuple(key, addressModel)); _healthCheckService.Changed += ItemNode_Changed; } - } while (!IsHealth); + } while (!IsHealth); return addressModel; } - #endregion Overrides of AddressSelectorBase - #region Private Method + /// + /// The GetCacheKey + /// + /// The descriptor + /// The + private static string GetCacheKey(ServiceDescriptor descriptor) + { + return descriptor.Id; + } + /// + /// The ItemNode_Changed + /// + /// The sender + /// The e private void ItemNode_Changed(object sender, HealthCheckEventArgs e) - { - var list= _unHealths.Where(p=>p.Item2.ToString()==e.Address.ToString()).ToList(); + { + var list = _unHealths.Where(p => p.Item2.ToString() == e.Address.ToString()).ToList(); foreach (var item in list) { if (item.Item1 != null && e.Health) @@ -90,23 +136,23 @@ private void ItemNode_Changed(object sender, HealthCheckEventArgs e) _unHealths.Remove(item); } } - if(_unHealths.Count==0) + if (_unHealths.Count == 0) _healthCheckService.Changed -= ItemNode_Changed; } - private static string GetCacheKey(ServiceDescriptor descriptor) - { - return descriptor.Id; - } - + /// + /// The ServiceRouteManager_Removed + /// + /// The sender + /// The e private void ServiceRouteManager_Removed(object sender, ServiceRouteEventArgs e) { var key = GetCacheKey(e.Route.ServiceDescriptor); - var item = _unHealths.Where(p => e.Route.Address.Select(addr=> addr.ToString()).Contains(p.Item2.ToString())).ToList(); + var item = _unHealths.Where(p => e.Route.Address.Select(addr => addr.ToString()).Contains(p.Item2.ToString())).ToList(); item.ForEach(p => _unHealths.Remove(p)); _concurrent.TryRemove(key, out ConsistentHash value); } - #endregion + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/Implementation/PollingAddressSelector.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/Implementation/PollingAddressSelector.cs index 94ebfc82c..a93bea83f 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/Implementation/PollingAddressSelector.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/Implementation/PollingAddressSelector.cs @@ -16,11 +16,28 @@ namespace Surging.Core.CPlatform.Runtime.Client.Address.Resolvers.Implementation /// public class PollingAddressSelector : AddressSelectorBase { - private readonly IHealthCheckService _healthCheckService; + #region 字段 + /// + /// Defines the _concurrent + /// private readonly ConcurrentDictionary> _concurrent = new ConcurrentDictionary>(); + /// + /// Defines the _healthCheckService + /// + private readonly IHealthCheckService _healthCheckService; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serviceRouteManager + /// The healthCheckService public PollingAddressSelector(IServiceRouteManager serviceRouteManager, IHealthCheckService healthCheckService) { _healthCheckService = healthCheckService; @@ -29,7 +46,20 @@ public PollingAddressSelector(IServiceRouteManager serviceRouteManager, IHealthC serviceRouteManager.Removed += ServiceRouteManager_Removed; } - #region Overrides of AddressSelectorBase + #endregion 构造函数 + + #region 方法 + + /// + /// The CheckHealth + /// + /// The addressModel + /// The + public async ValueTask CheckHealth(AddressModel addressModel) + { + var vt = _healthCheckService.IsHealth(addressModel); + return vt.IsCompletedSuccessfully ? vt.Result : await vt; + } /// /// 选择一个地址。 @@ -47,7 +77,6 @@ protected override async ValueTask SelectAsync(AddressSelectContex ValueTask vt; do { - addressModel = addressEntry.GetAddress(); if (len <= index) { @@ -60,21 +89,21 @@ protected override async ValueTask SelectAsync(AddressSelectContex return addressModel; } - #endregion Overrides of AddressSelectorBase - - #region Private Method - + /// + /// The GetCacheKey + /// + /// The descriptor + /// The private static string GetCacheKey(ServiceDescriptor descriptor) { return descriptor.Id; } - public async ValueTask CheckHealth(AddressModel addressModel) - { - var vt = _healthCheckService.IsHealth(addressModel); - return vt.IsCompletedSuccessfully ? vt.Result : await vt; - } - + /// + /// The ServiceRouteManager_Removed + /// + /// The sender + /// The e private void ServiceRouteManager_Removed(object sender, ServiceRouteEventArgs e) { var key = GetCacheKey(e.Route.ServiceDescriptor); @@ -82,33 +111,57 @@ private void ServiceRouteManager_Removed(object sender, ServiceRouteEventArgs e) _concurrent.TryRemove(key, out value); } - #endregion Private Method - - #region Help Class + #endregion 方法 + /// + /// Defines the + /// protected class AddressEntry { - #region Field + #region 字段 + /// + /// Defines the _address + /// + private readonly AddressModel[] _address; + + /// + /// Defines the _maxIndex + /// + private readonly int _maxIndex; + + /// + /// Defines the _index + /// private int _index; + + /// + /// Defines the _lock + /// private int _lock; - private readonly int _maxIndex; - private readonly AddressModel[] _address; - #endregion Field + #endregion 字段 - #region Constructor + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The address public AddressEntry(IEnumerable address) { _address = address.ToArray(); _maxIndex = _address.Length - 1; } - #endregion Constructor + #endregion 构造函数 - #region Public Method + #region 方法 + /// + /// The GetAddress + /// + /// The public AddressModel GetAddress() { while (true) @@ -135,9 +188,7 @@ public AddressModel GetAddress() } } - #endregion Public Method + #endregion 方法 } - - #endregion Help Class } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/Implementation/RandomAddressSelector.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/Implementation/RandomAddressSelector.cs index 3f28e7e53..11354dfd7 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/Implementation/RandomAddressSelector.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Address/Resolvers/Implementation/Selectors/Implementation/RandomAddressSelector.cs @@ -10,17 +10,24 @@ namespace Surging.Core.CPlatform.Runtime.Client.Address.Resolvers.Implementation /// public class RandomAddressSelector : AddressSelectorBase { - #region Field + #region 字段 + /// + /// Defines the _generate + /// private readonly Func _generate; + + /// + /// Defines the _random + /// private readonly Random _random; - #endregion Field + #endregion 字段 - #region Constructor + #region 构造函数 /// - /// 初始化一个以Random生成随机数的随机地址选择器。 + /// Initializes a new instance of the class. /// public RandomAddressSelector() { @@ -29,7 +36,7 @@ public RandomAddressSelector() } /// - /// 初始化一个自定义的随机地址选择器。 + /// Initializes a new instance of the class. /// /// 随机数生成委托,第一个参数为最小值,第二个参数为最大值(不可以超过该值)。 public RandomAddressSelector(Func generate) @@ -39,9 +46,9 @@ public RandomAddressSelector(Func generate) _generate = generate; } - #endregion Constructor + #endregion 构造函数 - #region Overrides of AddressSelectorBase + #region 方法 /// /// 选择一个地址。 @@ -57,6 +64,6 @@ protected override ValueTask SelectAsync(AddressSelectContext cont return new ValueTask(address[index]); } - #endregion Overrides of AddressSelectorBase + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/HealthChecks/IHealthCheckService.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/HealthChecks/IHealthCheckService.cs index 32577d4a1..7bed76c72 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/HealthChecks/IHealthCheckService.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/HealthChecks/IHealthCheckService.cs @@ -5,18 +5,28 @@ namespace Surging.Core.CPlatform.Runtime.Client.HealthChecks { + #region 接口 /// /// 一个抽象的健康检查服务。 /// public interface IHealthCheckService { + #region 事件 + /// - /// 监控一个地址。 + /// Defines the Changed /// - /// 地址模型。 - /// 一个任务。 - void Monitor(AddressModel address); + event EventHandler Changed; + + /// + /// Defines the Removed + /// + event EventHandler Removed; + + #endregion 事件 + + #region 方法 /// /// 判断一个地址是否健康。 @@ -32,8 +42,14 @@ public interface IHealthCheckService /// 一个任务。 Task MarkFailure(AddressModel address); - event EventHandler Removed; + /// + /// 监控一个地址。 + /// + /// 地址模型。 + void Monitor(AddressModel address); - event EventHandler Changed; + #endregion 方法 } + + #endregion 接口 } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/HealthChecks/Implementation/DefaultHealthCheckService.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/HealthChecks/Implementation/DefaultHealthCheckService.cs index 114049d5e..fc12b8909 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/HealthChecks/Implementation/DefaultHealthCheckService.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/HealthChecks/Implementation/DefaultHealthCheckService.cs @@ -12,12 +12,13 @@ namespace Surging.Core.CPlatform.Runtime.Client.HealthChecks.Implementation { /// - /// 默认健康检查服务(每10秒会检查一次服务状态,在构造函数中添加服务管理事件) + /// 默认健康检查服务(每10秒会检查一次服务状态,在构造函数中添加服务管理事件) /// public class DefaultHealthCheckService : IHealthCheckService, IDisposable { private readonly ConcurrentDictionary, MonitorEntry> _dictionary = new ConcurrentDictionary, MonitorEntry>(); + private readonly IServiceRouteManager _serviceRouteManager; private readonly int _timeout = 30000; private readonly Timer _timer; @@ -38,7 +39,7 @@ public event EventHandler Changed } /// - /// 默认心跳检查服务(每10秒会检查一次服务状态,在构造函数中添加服务管理事件) + /// 默认心跳检查服务(每10秒会检查一次服务状态,在构造函数中添加服务管理事件) /// /// public DefaultHealthCheckService(IServiceRouteManager serviceRouteManager) @@ -73,7 +74,8 @@ public DefaultHealthCheckService(IServiceRouteManager serviceRouteManager) //重新监控。 serviceRouteManager.Changed += async (s, e) => { - var keys = e.Route.Address.Select(address => { + var keys = e.Route.Address.Select(address => + { var ipAddress = address as IpAddressModel; return new ValueTuple(ipAddress.Ip, ipAddress.Port); }); @@ -81,9 +83,6 @@ public DefaultHealthCheckService(IServiceRouteManager serviceRouteManager) }; } - - #region Implementation of IHealthCheckService - /// /// 监控一个地址。 /// @@ -104,8 +103,8 @@ public async ValueTask IsHealth(AddressModel address) { var ipAddress = address as IpAddressModel; MonitorEntry entry; - var isHealth= !_dictionary.TryGetValue(new ValueTuple(ipAddress.Ip, ipAddress.Port), out entry) ? await Check(address, _timeout) :entry.Health; - OnChanged(new HealthCheckEventArgs(address,isHealth)); + var isHealth = !_dictionary.TryGetValue(new ValueTuple(ipAddress.Ip, ipAddress.Port), out entry) ? await Check(address, _timeout) : entry.Health; + OnChanged(new HealthCheckEventArgs(address, isHealth)); return isHealth; } @@ -142,20 +141,12 @@ protected void OnChanged(params HealthCheckEventArgs[] args) _changed(this, arg); } - #endregion Implementation of IHealthCheckService - - #region Implementation of IDisposable - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. public void Dispose() { _timer.Dispose(); } - #endregion Implementation of IDisposable - - #region Private Method - private void Remove(IEnumerable addressModels) { foreach (var addressModel in addressModels) @@ -176,7 +167,8 @@ private void RemoveUnhealthyAddress(IEnumerable monitorEntry) return new IpAddressModel(ipEndPoint.Address.ToString(), ipEndPoint.Port); }).ToList(); _serviceRouteManager.RemveAddressAsync(addresses).Wait(); - addresses.ForEach(p => { + addresses.ForEach(p => + { var ipAddress = p as IpAddressModel; _dictionary.TryRemove(new ValueTuple(ipAddress.Ip, ipAddress.Port), out MonitorEntry value); }); @@ -196,7 +188,6 @@ private static async Task Check(AddressModel address, int timeout) } catch { - } return isHealth; } @@ -223,17 +214,12 @@ private static async Task Check(IEnumerable entrys, int timeout) } } - #endregion Private Method - - #region Help Class - protected class MonitorEntry { public MonitorEntry(AddressModel addressModel, bool health = true) { EndPoint = addressModel.CreateEndPoint(); Health = health; - } public int UnhealthyTimes { get; set; } @@ -241,7 +227,5 @@ public MonitorEntry(AddressModel addressModel, bool health = true) public EndPoint EndPoint { get; set; } public bool Health { get; set; } } - - #endregion Help Class } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/HealthChecks/Implementation/HealthCheckEventArgs.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/HealthChecks/Implementation/HealthCheckEventArgs.cs index 6d5b553a7..28f4d619b 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/HealthChecks/Implementation/HealthCheckEventArgs.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/HealthChecks/Implementation/HealthCheckEventArgs.cs @@ -5,21 +5,47 @@ namespace Surging.Core.CPlatform.Runtime.Client.HealthChecks.Implementation { - public class HealthCheckEventArgs + /// + /// Defines the + /// + public class HealthCheckEventArgs { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The address public HealthCheckEventArgs(AddressModel address) { Address = address; } - public HealthCheckEventArgs(AddressModel address,bool health) + /// + /// Initializes a new instance of the class. + /// + /// The address + /// The health + public HealthCheckEventArgs(AddressModel address, bool health) { Address = address; Health = health; } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the Address + /// public AddressModel Address { get; private set; } + /// + /// Gets a value indicating whether Health + /// public bool Health { get; private set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/IRemoteInvokeService.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/IRemoteInvokeService.cs index 9104be745..8890fb082 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/IRemoteInvokeService.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/IRemoteInvokeService.cs @@ -4,11 +4,15 @@ namespace Surging.Core.CPlatform.Runtime.Client { + #region 接口 + /// /// 一个抽象的远程调用服务。 /// public interface IRemoteInvokeService { + #region 方法 + /// /// 调用。 /// @@ -28,8 +32,12 @@ public interface IRemoteInvokeService /// 调用。 /// /// 调用上下文。 - /// 超时时间。 + /// The requestTimeout /// 远程调用结果消息模型。 Task InvokeAsync(RemoteInvokeContext context, int requestTimeout); + + #endregion 方法 } + + #endregion 接口 } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/IServiceHeartbeatManager.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/IServiceHeartbeatManager.cs index 1f9bd741f..f193b5a2f 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/IServiceHeartbeatManager.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/IServiceHeartbeatManager.cs @@ -5,10 +5,30 @@ namespace Surging.Core.CPlatform.Runtime.Client { + #region 接口 + + /// + /// Defines the + /// public interface IServiceHeartbeatManager { + #region 方法 + + /// + /// The AddWhitelist + /// + /// The serviceId void AddWhitelist(string serviceId); + /// + /// The ExistsWhitelist + /// + /// The serviceId + /// The bool ExistsWhitelist(string serviceId); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/IServiceSubscribeManager.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/IServiceSubscribeManager.cs index 74128ccd3..19dc2097c 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/IServiceSubscribeManager.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/IServiceSubscribeManager.cs @@ -8,8 +8,20 @@ namespace Surging.Core.CPlatform.Runtime.Client { - public interface IServiceSubscribeManager + #region 接口 + + /// + /// Defines the + /// + public interface IServiceSubscribeManager { + #region 方法 + + /// + /// 清空所有的服务订阅者。 + /// + /// 一个任务。 + Task ClearAsync(); /// /// 获取所有可用的服务订阅者信息。 @@ -20,37 +32,27 @@ public interface IServiceSubscribeManager /// /// 设置服务订阅者。 /// - /// 服务路由集合。 + /// The subscibers /// 一个任务。 Task SetSubscribersAsync(IEnumerable subscibers); - - /// - /// 清空所有的服务订阅者。 - /// - /// 一个任务。 - Task ClearAsync(); + #endregion 方法 } + #endregion 接口 + /// /// 服务路由管理者扩展方法。 /// public static class ServiceRouteManagerExtensions { - /// - /// 根据服务Id获取一个服务订阅者。 - /// - /// 服务订阅管理者。 - /// 服务Id。 - /// 服务路由。 - public static async Task GetAsync(this IServiceSubscribeManager serviceSubscribeManager, string serviceId) - { - return (await serviceSubscribeManager.GetSubscribersAsync()).SingleOrDefault(i => i.ServiceDescriptor.Id == serviceId); - } + #region 方法 /// /// 获取地址 /// + /// The serviceSubscribeManager + /// The condition /// public static async Task> GetAddressAsync(this IServiceSubscribeManager serviceSubscribeManager, string condition = null) { @@ -81,6 +83,24 @@ public static async Task> GetAddressAsync(this IServic return result.Values; } + /// + /// 根据服务Id获取一个服务订阅者。 + /// + /// The serviceSubscribeManager + /// 服务Id。 + /// 服务路由。 + public static async Task GetAsync(this IServiceSubscribeManager serviceSubscribeManager, string serviceId) + { + return (await serviceSubscribeManager.GetSubscribersAsync()).SingleOrDefault(i => i.ServiceDescriptor.Id == serviceId); + } + + /// + /// The GetServiceDescriptorAsync + /// + /// The serviceSubscribeManager + /// The address + /// The serviceId + /// The public static async Task> GetServiceDescriptorAsync(this IServiceSubscribeManager serviceSubscribeManager, string address, string serviceId = null) { var subscribers = await serviceSubscribeManager.GetSubscribersAsync(); @@ -95,5 +115,7 @@ public static async Task> GetServiceDescriptorAsy .Select(p => p.ServiceDescriptor); } } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/IServiceSubscriberFactory.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/IServiceSubscriberFactory.cs index b2fbd7a3d..a41b8153a 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/IServiceSubscriberFactory.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/IServiceSubscriberFactory.cs @@ -5,16 +5,24 @@ namespace Surging.Core.CPlatform.Runtime.Client { + #region 接口 + /// /// 服务订阅者工厂接口 /// public interface IServiceSubscriberFactory { + #region 方法 + /// /// 根据服务描述创建服务订阅者 /// /// /// Task> CreateServiceSubscribersAsync(IEnumerable descriptors); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Implementation/DefaultServiceHeartbeatManager.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Implementation/DefaultServiceHeartbeatManager.cs index 7a5e5a251..ea100c130 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Implementation/DefaultServiceHeartbeatManager.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Implementation/DefaultServiceHeartbeatManager.cs @@ -4,19 +4,42 @@ namespace Surging.Core.CPlatform.Runtime.Client.Implementation { + /// + /// Defines the + /// public class DefaultServiceHeartbeatManager : IServiceHeartbeatManager { + #region 字段 + + /// + /// Defines the _whitelist + /// private readonly ConcurrentBag _whitelist = new ConcurrentBag(); + #endregion 字段 + + #region 方法 + + /// + /// The AddWhitelist + /// + /// The serviceId public void AddWhitelist(string serviceId) { - if(!_whitelist.Contains(serviceId)) - _whitelist.Add(serviceId); + if (!_whitelist.Contains(serviceId)) + _whitelist.Add(serviceId); } + /// + /// The ExistsWhitelist + /// + /// The serviceId + /// The public bool ExistsWhitelist(string serviceId) { return _whitelist.Contains(serviceId); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Implementation/DefaultServiceSubscriberFactory.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Implementation/DefaultServiceSubscriberFactory.cs index 66532ac45..68fb690c5 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Implementation/DefaultServiceSubscriberFactory.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Implementation/DefaultServiceSubscriberFactory.cs @@ -9,15 +9,40 @@ namespace Surging.Core.CPlatform.Runtime.Client.Implementation { - class DefaultServiceSubscriberFactory : IServiceSubscriberFactory + /// + /// Defines the + /// + internal class DefaultServiceSubscriberFactory : IServiceSubscriberFactory { + #region 字段 + + /// + /// Defines the _serializer + /// private readonly ISerializer _serializer; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serializer public DefaultServiceSubscriberFactory(ISerializer serializer) { _serializer = serializer; } - + + #endregion 构造函数 + + #region 方法 + + /// + /// The CreateServiceSubscribersAsync + /// + /// The descriptors + /// The public Task> CreateServiceSubscribersAsync(IEnumerable descriptors) { if (descriptors == null) @@ -33,6 +58,11 @@ public Task> CreateServiceSubscribersAsync(IEnume return Task.FromResult(subscribers.AsEnumerable()); } + /// + /// The CreateAddress + /// + /// The descriptors + /// The private IEnumerable CreateAddress(IEnumerable descriptors) { if (descriptors == null) @@ -43,5 +73,7 @@ private IEnumerable CreateAddress(IEnumerable public class RemoteInvokeService : IRemoteInvokeService { + #region 字段 + + /// + /// Defines the _addressResolver + /// private readonly IAddressResolver _addressResolver; - private readonly ITransportClientFactory _transportClientFactory; - private readonly ILogger _logger; + + /// + /// Defines the _healthCheckService + /// private readonly IHealthCheckService _healthCheckService; - public RemoteInvokeService(IHashAlgorithm hashAlgorithm,IAddressResolver addressResolver, ITransportClientFactory transportClientFactory, ILogger logger, IHealthCheckService healthCheckService) + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the _transportClientFactory + /// + private readonly ITransportClientFactory _transportClientFactory; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The hashAlgorithm + /// The addressResolver + /// The transportClientFactory + /// The logger + /// The healthCheckService + public RemoteInvokeService(IHashAlgorithm hashAlgorithm, IAddressResolver addressResolver, ITransportClientFactory transportClientFactory, ILogger logger, IHealthCheckService healthCheckService) { _addressResolver = addressResolver; _transportClientFactory = transportClientFactory; @@ -31,26 +60,39 @@ public RemoteInvokeService(IHashAlgorithm hashAlgorithm,IAddressResolver address _healthCheckService = healthCheckService; } - #region Implementation of IRemoteInvokeService + #endregion 构造函数 + #region 方法 + + /// + /// The InvokeAsync + /// + /// The context + /// The public async Task InvokeAsync(RemoteInvokeContext context) { return await InvokeAsync(context, Task.Factory.CancellationToken); } + /// + /// The InvokeAsync + /// + /// The context + /// The cancellationToken + /// The public async Task InvokeAsync(RemoteInvokeContext context, CancellationToken cancellationToken) { var invokeMessage = context.InvokeMessage; AddressModel address = null; var vt = ResolverAddress(context, context.Item); - address = vt.IsCompletedSuccessfully? vt.Result: await vt; + address = vt.IsCompletedSuccessfully ? vt.Result : await vt; try { var endPoint = address.CreateEndPoint(); if (_logger.IsEnabled(LogLevel.Debug)) _logger.LogDebug($"使用地址:'{endPoint}'进行调用。"); - var client =await _transportClientFactory.CreateClientAsync(endPoint); - return await client.SendAsync(invokeMessage,cancellationToken).WithCancellation(cancellationToken); + var client = await _transportClientFactory.CreateClientAsync(endPoint); + return await client.SendAsync(invokeMessage, cancellationToken).WithCancellation(cancellationToken); } catch (CommunicationException) { @@ -64,6 +106,12 @@ public async Task InvokeAsync(RemoteInvokeContext con } } + /// + /// The InvokeAsync + /// + /// The context + /// The requestTimeout + /// The public async Task InvokeAsync(RemoteInvokeContext context, int requestTimeout) { var invokeMessage = context.InvokeMessage; @@ -75,7 +123,7 @@ public async Task InvokeAsync(RemoteInvokeContext con var endPoint = address.CreateEndPoint(); if (_logger.IsEnabled(LogLevel.Debug)) _logger.LogDebug($"使用地址:'{endPoint}'进行调用。"); - var client =await _transportClientFactory.CreateClientAsync(endPoint); + var client = await _transportClientFactory.CreateClientAsync(endPoint); using (var cts = new CancellationTokenSource()) { return await client.SendAsync(invokeMessage, cts.Token).WithCancellation(cts, requestTimeout); @@ -93,7 +141,13 @@ public async Task InvokeAsync(RemoteInvokeContext con } } - private async ValueTask ResolverAddress(RemoteInvokeContext context,string item) + /// + /// The ResolverAddress + /// + /// The context + /// The item + /// The + private async ValueTask ResolverAddress(RemoteInvokeContext context, string item) { if (context == null) throw new ArgumentNullException(nameof(context)); @@ -104,15 +158,15 @@ private async ValueTask ResolverAddress(RemoteInvokeContext contex if (string.IsNullOrEmpty(context.InvokeMessage.ServiceId)) throw new ArgumentException("服务Id不能为空。", nameof(context.InvokeMessage.ServiceId)); //远程调用信息 - var invokeMessage = context.InvokeMessage; + var invokeMessage = context.InvokeMessage; //解析服务地址 - var vt = _addressResolver.Resolver(invokeMessage.ServiceId, item); + var vt = _addressResolver.Resolver(invokeMessage.ServiceId, item); var address = vt.IsCompletedSuccessfully ? vt.Result : await vt; if (address == null) throw new CPlatformException($"无法解析服务Id:{invokeMessage.ServiceId}的地址信息。"); return address; } - #endregion Implementation of IRemoteInvokeService + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Implementation/ServiceSubscribeManagerBase.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Implementation/ServiceSubscribeManagerBase.cs index 8adfebe99..6bbc1b425 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Implementation/ServiceSubscribeManagerBase.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/Implementation/ServiceSubscribeManagerBase.cs @@ -8,17 +8,40 @@ namespace Surging.Core.CPlatform.Runtime.Client.Implementation { - public abstract class ServiceSubscribeManagerBase : IServiceSubscribeManager - { - private readonly ISerializer _serializer; + /// + /// Defines the + /// + public abstract class ServiceSubscribeManagerBase : IServiceSubscribeManager + { + #region 字段 + /// + /// Defines the _serializer + /// + private readonly ISerializer _serializer; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serializer protected ServiceSubscribeManagerBase(ISerializer serializer) { _serializer = serializer; } - #region Implementation of IServiceRouteManager + #endregion 构造函数 + + #region 方法 + /// + /// 清空所有的服务订阅者。 + /// + /// 一个任务。 + public abstract Task ClearAsync(); /// /// 获取所有服务订阅者信息。 @@ -29,7 +52,7 @@ protected ServiceSubscribeManagerBase(ISerializer serializer) /// /// 设置服务订阅者。 /// - /// 服务路由集合。 + /// The subscibers /// 一个任务。 public virtual Task SetSubscribersAsync(IEnumerable subscibers) { @@ -49,21 +72,13 @@ public virtual Task SetSubscribersAsync(IEnumerable subsciber return SetSubscribersAsync(descriptors); } - /// - /// 清空所有的服务订阅者。 - /// - /// 一个任务。 - public abstract Task ClearAsync(); - - #endregion Implementation of IServiceSubscriberManager - /// /// 设置服务订阅者。 /// - /// 服务订阅者集合。 + /// The subscribers /// 一个任务。 protected abstract Task SetSubscribersAsync(IEnumerable subscribers); - + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/RemoteInvokeContext.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/RemoteInvokeContext.cs index 4384f417d..f32c25b40 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/RemoteInvokeContext.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/RemoteInvokeContext.cs @@ -7,11 +7,19 @@ namespace Surging.Core.CPlatform.Runtime.Client /// public class RemoteInvokeContext { + #region 属性 + /// + /// Gets or sets the InvokeMessage /// 远程调用消息。 /// public RemoteInvokeMessage InvokeMessage { get; set; } + /// + /// Gets or sets the Item + /// public string Item { get; set; } + + #endregion 属性 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/ServiceSubscriber.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/ServiceSubscriber.cs index e5e1388a6..9a9fdc7fd 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/ServiceSubscriber.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/ServiceSubscriber.cs @@ -11,18 +11,29 @@ namespace Surging.Core.CPlatform.Runtime.Client /// public class ServiceSubscriber { + #region 属性 + /// + /// Gets or sets the Address /// 订阅者服务地址。 /// public IEnumerable Address { get; set; } /// + /// Gets or sets the ServiceDescriptor /// 服务描述符。 /// public ServiceDescriptor ServiceDescriptor { get; set; } - #region Equality members + #endregion 属性 + + #region 方法 + /// + /// The Equals + /// + /// The obj + /// The public override bool Equals(object obj) { var model = obj as ServiceSubscriber; @@ -38,11 +49,17 @@ public override bool Equals(object obj) return model.Address.Count() == Address.Count() && model.Address.All(addressModel => Address.Contains(addressModel)); } + /// + /// The GetHashCode + /// + /// The public override int GetHashCode() { return ToString().GetHashCode(); } + #endregion 方法 + public static bool operator ==(ServiceSubscriber model1, ServiceSubscriber model2) { return Equals(model1, model2); @@ -52,7 +69,5 @@ public override int GetHashCode() { return !Equals(model1, model2); } - - #endregion Equality members } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/ServiceSubscriberDescriptor.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/ServiceSubscriberDescriptor.cs index b29bbcf02..e634e5156 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/ServiceSubscriberDescriptor.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Client/ServiceSubscriberDescriptor.cs @@ -3,16 +3,25 @@ namespace Surging.Core.CPlatform.Runtime.Client { + /// + /// Defines the + /// public class ServiceSubscriberDescriptor { + #region 属性 + /// + /// Gets or sets the AddressDescriptors /// 服务地址描述符集合。 /// public IEnumerable AddressDescriptors { get; set; } /// + /// Gets or sets the ServiceDescriptor /// 服务描述符。 /// public ServiceDescriptor ServiceDescriptor { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/IServiceEntryLocate.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/IServiceEntryLocate.cs index 4df41f219..04c101ad5 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/IServiceEntryLocate.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/IServiceEntryLocate.cs @@ -2,11 +2,22 @@ namespace Surging.Core.CPlatform.Runtime.Server { + #region 接口 + /// /// 一个抽象的服务条目定位器。 /// public interface IServiceEntryLocate { + #region 方法 + + /// + /// The Locate + /// + /// The httpMessage + /// The + ServiceEntry Locate(HttpMessage httpMessage); + /// /// 定位服务条目。 /// @@ -14,6 +25,8 @@ public interface IServiceEntryLocate /// 服务条目。 ServiceEntry Locate(RemoteInvokeMessage invokeMessage); - ServiceEntry Locate(HttpMessage httpMessage); + #endregion 方法 } + + #endregion 接口 } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/IServiceEntryManager.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/IServiceEntryManager.cs index c14d20c47..7bd5258e5 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/IServiceEntryManager.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/IServiceEntryManager.cs @@ -2,19 +2,35 @@ namespace Surging.Core.CPlatform.Runtime.Server { + #region 接口 + /// /// 一个抽象的服务条目管理者。 /// public interface IServiceEntryManager { + #region 方法 + + /// + /// The GetAllEntries + /// + /// The + IEnumerable GetAllEntries(); + /// /// 获取服务条目集合。 /// /// 服务条目集合。 IEnumerable GetEntries(); + /// + /// The UpdateEntries + /// + /// The providers void UpdateEntries(IEnumerable providers); - IEnumerable GetAllEntries(); + #endregion 方法 } + + #endregion 接口 } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/IServiceEntryProvider.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/IServiceEntryProvider.cs index b58a4976b..6c7c164c3 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/IServiceEntryProvider.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/IServiceEntryProvider.cs @@ -3,19 +3,35 @@ namespace Surging.Core.CPlatform.Runtime.Server { + #region 接口 + /// /// 一个抽象的服务条目提供程序。 /// public interface IServiceEntryProvider { + #region 方法 + + /// + /// The GetALLEntries + /// + /// The + IEnumerable GetALLEntries(); + /// /// 获取服务条目集合。 /// /// 服务条目集合。 IEnumerable GetEntries(); - IEnumerable GetALLEntries(); - + /// + /// The GetTypes + /// + /// The IEnumerable GetTypes(); + + #endregion 方法 } + + #endregion 接口 } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/IServiceExecutor.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/IServiceExecutor.cs index a76dbf660..c1e6d4fdb 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/IServiceExecutor.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/IServiceExecutor.cs @@ -4,16 +4,25 @@ namespace Surging.Core.CPlatform.Runtime.Server { + #region 接口 + /// /// 一个抽象的服务执行器。 /// public interface IServiceExecutor { + #region 方法 + /// /// 执行。 /// /// 消息发送者。 /// 调用消息。 - Task ExecuteAsync(IMessageSender sender,TransportMessage message); + /// The + Task ExecuteAsync(IMessageSender sender, TransportMessage message); + + #endregion 方法 } + + #endregion 接口 } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/IServiceHost.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/IServiceHost.cs index 9da1cefdb..21caf2a82 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/IServiceHost.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/IServiceHost.cs @@ -4,11 +4,15 @@ namespace Surging.Core.CPlatform.Runtime.Server { + #region 接口 + /// /// 一个抽象的服务主机。 /// public interface IServiceHost : IDisposable { + #region 方法 + /// /// 启动主机。 /// @@ -19,7 +23,13 @@ public interface IServiceHost : IDisposable /// /// 启动主机。 /// - /// ip地址。 - Task StartAsync(string ip,int port); + /// The ip + /// The port + /// The + Task StartAsync(string ip, int port); + + #endregion 方法 } + + #endregion 接口 } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/IServiceTokenGenerator.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/IServiceTokenGenerator.cs index 1937e32d9..fe4fc9638 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/IServiceTokenGenerator.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/IServiceTokenGenerator.cs @@ -4,10 +4,30 @@ namespace Surging.Core.CPlatform.Runtime.Server { + #region 接口 + + /// + /// Defines the + /// public interface IServiceTokenGenerator { + #region 方法 + + /// + /// The GeneratorToken + /// + /// The code + /// The string GeneratorToken(string code); + /// + /// The GetToken + /// + /// The string GetToken(); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/DefaultServiceEntryLocate.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/DefaultServiceEntryLocate.cs index cb0d4d523..1dbc65eae 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/DefaultServiceEntryLocate.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/DefaultServiceEntryLocate.cs @@ -10,27 +10,35 @@ namespace Surging.Core.CPlatform.Runtime.Server.Implementation /// public class DefaultServiceEntryLocate : IServiceEntryLocate { + #region 字段 + + /// + /// Defines the _serviceEntryManager + /// private readonly IServiceEntryManager _serviceEntryManager; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serviceEntryManager public DefaultServiceEntryLocate(IServiceEntryManager serviceEntryManager) { _serviceEntryManager = serviceEntryManager; } - #region Implementation of IServiceEntryLocate + #endregion 构造函数 + + #region 方法 /// - /// 定位服务条目。 + /// The Locate /// - /// 远程调用消息。 - /// 服务条目。 - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ServiceEntry Locate(RemoteInvokeMessage invokeMessage) - { - var serviceEntries = _serviceEntryManager.GetEntries(); - return serviceEntries.SingleOrDefault(i => i.Descriptor.Id == invokeMessage.ServiceId); - } - + /// The httpMessage + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] public ServiceEntry Locate(HttpMessage httpMessage) { @@ -41,6 +49,18 @@ public ServiceEntry Locate(HttpMessage httpMessage) return serviceEntries.SingleOrDefault(i => i.RoutePath == routePath && !i.Descriptor.GetMetadata("IsOverload")); } - #endregion Implementation of IServiceEntryLocate + /// + /// 定位服务条目。 + /// + /// 远程调用消息。 + /// 服务条目。 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ServiceEntry Locate(RemoteInvokeMessage invokeMessage) + { + var serviceEntries = _serviceEntryManager.GetEntries(); + return serviceEntries.SingleOrDefault(i => i.Descriptor.Id == invokeMessage.ServiceId); + } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/DefaultServiceEntryManager.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/DefaultServiceEntryManager.cs index b8b956a97..b7ee1db47 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/DefaultServiceEntryManager.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/DefaultServiceEntryManager.cs @@ -10,42 +10,43 @@ namespace Surging.Core.CPlatform.Runtime.Server.Implementation /// public class DefaultServiceEntryManager : IServiceEntryManager { - #region Field + #region 字段 - private IEnumerable _serviceEntries; + /// + /// Defines the _allEntries + /// + private IEnumerable _allEntries; - private IEnumerable _allEntries; + /// + /// Defines the _serviceEntries + /// + private IEnumerable _serviceEntries; - #endregion Field + #endregion 字段 - #region Constructor + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The providers public DefaultServiceEntryManager(IEnumerable providers) { UpdateEntries(providers); } - #endregion Constructor + #endregion 构造函数 - #region Implementation of IServiceEntryManager + #region 方法 - public void UpdateEntries(IEnumerable providers) + /// + /// The GetAllEntries + /// + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public IEnumerable GetAllEntries() { - var list = new List(); - var allEntries = new List(); - foreach (var provider in providers) - { - var entries = provider.GetEntries().ToArray(); - foreach (var entry in entries) - { - if (list.Any(i => i.Descriptor.Id == entry.Descriptor.Id)) - throw new InvalidOperationException($"本地包含多个Id为:{entry.Descriptor.Id} 的服务条目。"); - } - list.AddRange(entries); - allEntries.AddRange( provider.GetALLEntries()); - } - _serviceEntries = list.ToArray(); - _allEntries = allEntries; + return _allEntries; } /// @@ -58,12 +59,29 @@ public IEnumerable GetEntries() return _serviceEntries; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public IEnumerable GetAllEntries() + /// + /// The UpdateEntries + /// + /// The providers + public void UpdateEntries(IEnumerable providers) { - return _allEntries; + var list = new List(); + var allEntries = new List(); + foreach (var provider in providers) + { + var entries = provider.GetEntries().ToArray(); + foreach (var entry in entries) + { + if (list.Any(i => i.Descriptor.Id == entry.Descriptor.Id)) + throw new InvalidOperationException($"本地包含多个Id为:{entry.Descriptor.Id} 的服务条目。"); + } + list.AddRange(entries); + allEntries.AddRange(provider.GetALLEntries()); + } + _serviceEntries = list.ToArray(); + _allEntries = allEntries; } - #endregion Implementation of IServiceEntryManager + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/DefaultServiceExecutor.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/DefaultServiceExecutor.cs index c023712c6..feca81c53 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/DefaultServiceExecutor.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/DefaultServiceExecutor.cs @@ -15,19 +15,44 @@ namespace Surging.Core.CPlatform.Runtime.Server.Implementation { + /// + /// Defines the + /// public class DefaultServiceExecutor : IServiceExecutor { - #region Field + #region 字段 - private readonly IServiceEntryLocate _serviceEntryLocate; + /// + /// Defines the _authorizationFilter + /// + private readonly IAuthorizationFilter _authorizationFilter; + + /// + /// Defines the _logger + /// private readonly ILogger _logger; + + /// + /// Defines the _serviceEntryLocate + /// + private readonly IServiceEntryLocate _serviceEntryLocate; + + /// + /// Defines the _serviceRouteProvider + /// private readonly IServiceRouteProvider _serviceRouteProvider; - private readonly IAuthorizationFilter _authorizationFilter; - #endregion Field + #endregion 字段 - #region Constructor + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The serviceEntryLocate + /// The serviceRouteProvider + /// The authorizationFilter + /// The logger public DefaultServiceExecutor(IServiceEntryLocate serviceEntryLocate, IServiceRouteProvider serviceRouteProvider, IAuthorizationFilter authorizationFilter, ILogger logger) @@ -38,24 +63,24 @@ public DefaultServiceExecutor(IServiceEntryLocate serviceEntryLocate, IServiceRo _authorizationFilter = authorizationFilter; } - #endregion Constructor + #endregion 构造函数 - #region Implementation of IServiceExecutor + #region 方法 /// /// 执行。 /// /// 消息发送者。 /// 调用消息。 + /// The public async Task ExecuteAsync(IMessageSender sender, TransportMessage message) { - if (_logger.IsEnabled(LogLevel.Trace)) _logger.LogTrace("服务提供者接收到消息。"); if (!message.IsInvokeMessage()) return; - + RemoteInvokeMessage remoteInvokeMessage; try { @@ -63,12 +88,12 @@ public async Task ExecuteAsync(IMessageSender sender, TransportMessage message) } catch (Exception exception) { - _logger.LogError(exception,"将接收到的消息反序列化成 TransportMessage 时发送了错误。"); + _logger.LogError(exception, "将接收到的消息反序列化成 TransportMessage 时发送了错误。"); return; } - + var entry = _serviceEntryLocate.Locate(remoteInvokeMessage); - + if (entry == null) { if (_logger.IsEnabled(LogLevel.Error)) @@ -76,10 +101,10 @@ public async Task ExecuteAsync(IMessageSender sender, TransportMessage message) return; } - if(remoteInvokeMessage.Attachments !=null) + if (remoteInvokeMessage.Attachments != null) { - foreach(var attachment in remoteInvokeMessage.Attachments) - RpcContext.GetContext().SetAttachment(attachment.Key,attachment.Value); + foreach (var attachment in remoteInvokeMessage.Attachments) + RpcContext.GetContext().SetAttachment(attachment.Key, attachment.Value); } if (_logger.IsEnabled(LogLevel.Debug)) @@ -91,7 +116,7 @@ public async Task ExecuteAsync(IMessageSender sender, TransportMessage message) if (entry.Descriptor.WaitExecution()) { //执行本地代码。 - await LocalExecuteAsync(entry, remoteInvokeMessage, resultMessage); + await LocalExecuteAsync(entry, remoteInvokeMessage, resultMessage); //向客户端发送调用结果。 await SendRemoteInvokeResult(sender, message.Id, resultMessage); } @@ -103,15 +128,36 @@ public async Task ExecuteAsync(IMessageSender sender, TransportMessage message) await Task.Factory.StartNew(async () => { //执行本地代码。 - await LocalExecuteAsync(entry, remoteInvokeMessage, resultMessage); - }, TaskCreationOptions.LongRunning); - } + await LocalExecuteAsync(entry, remoteInvokeMessage, resultMessage); + }, TaskCreationOptions.LongRunning); + } } - #endregion Implementation of IServiceExecutor + /// + /// The GetExceptionMessage + /// + /// The exception + /// The + private static string GetExceptionMessage(Exception exception) + { + if (exception == null) + return string.Empty; - #region Private Method + var message = exception.Message; + if (exception.InnerException != null) + { + message += "|InnerException:" + GetExceptionMessage(exception.InnerException); + } + return message; + } + /// + /// The LocalExecuteAsync + /// + /// The entry + /// The remoteInvokeMessage + /// The resultMessage + /// The private async Task LocalExecuteAsync(ServiceEntry entry, RemoteInvokeMessage remoteInvokeMessage, RemoteInvokeResultMessage resultMessage) { try @@ -145,7 +191,14 @@ private async Task LocalExecuteAsync(ServiceEntry entry, RemoteInvokeMessage rem resultMessage.StatusCode = exception.HResult; } } - + + /// + /// The SendRemoteInvokeResult + /// + /// The sender + /// The messageId + /// The resultMessage + /// The private async Task SendRemoteInvokeResult(IMessageSender sender, string messageId, RemoteInvokeResultMessage resultMessage) { try @@ -160,23 +213,10 @@ private async Task SendRemoteInvokeResult(IMessageSender sender, string messageI catch (Exception exception) { if (_logger.IsEnabled(LogLevel.Error)) - _logger.LogError(exception,"发送响应消息时候发生了异常。" ); + _logger.LogError(exception, "发送响应消息时候发生了异常。"); } - } - - private static string GetExceptionMessage(Exception exception) - { - if (exception == null) - return string.Empty; - - var message = exception.Message; - if (exception.InnerException != null) - { - message += "|InnerException:" + GetExceptionMessage(exception.InnerException); - } - return message; } - #endregion Private Method + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/DefaultServiceHost.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/DefaultServiceHost.cs index 5f0954003..d2bdc6b96 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/DefaultServiceHost.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/DefaultServiceHost.cs @@ -10,21 +10,39 @@ namespace Surging.Core.CPlatform.Runtime.Server.Implementation /// public class DefaultServiceHost : ServiceHostAbstract { - #region Field + #region 字段 + /// + /// Defines the _messageListenerFactory + /// private readonly Func> _messageListenerFactory; + + /// + /// Defines the _serverMessageListener + /// private IMessageListener _serverMessageListener; - #endregion Field + #endregion 字段 + + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The messageListenerFactory + /// The serviceExecutor public DefaultServiceHost(Func> messageListenerFactory, IServiceExecutor serviceExecutor) : base(serviceExecutor) { _messageListenerFactory = messageListenerFactory; } - #region Overrides of ServiceHostAbstract + #endregion 构造函数 + + #region 方法 - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + /// The Dispose + /// public override void Dispose() { (_serverMessageListener as IDisposable)?.Dispose(); @@ -49,7 +67,13 @@ await Task.Run(() => }; } - public override async Task StartAsync(string ip,int port) + /// + /// The StartAsync + /// + /// The ip + /// The port + /// The + public override async Task StartAsync(string ip, int port) { if (_serverMessageListener != null) return; @@ -63,6 +87,6 @@ await Task.Run(() => }; } - #endregion Overrides of ServiceHostAbstract + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Attributes/AttributeServiceEntryProvider.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Attributes/AttributeServiceEntryProvider.cs index 9eea4af61..2e9a6c756 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Attributes/AttributeServiceEntryProvider.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Attributes/AttributeServiceEntryProvider.cs @@ -1,9 +1,9 @@ -using Microsoft.Extensions.Logging; +using Autofac; +using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; -using Autofac; namespace Surging.Core.CPlatform.Runtime.Server.Implementation.ServiceDiscovery.Attributes { @@ -12,18 +12,40 @@ namespace Surging.Core.CPlatform.Runtime.Server.Implementation.ServiceDiscovery. /// public class AttributeServiceEntryProvider : IServiceEntryProvider { - #region Field + #region 字段 - private readonly IEnumerable _types; + /// + /// Defines the _clrServiceEntryFactory + /// private readonly IClrServiceEntryFactory _clrServiceEntryFactory; + + /// + /// Defines the _logger + /// private readonly ILogger _logger; + + /// + /// Defines the _serviceProvider + /// private readonly CPlatformContainer _serviceProvider; - #endregion Field + /// + /// Defines the _types + /// + private readonly IEnumerable _types; + + #endregion 字段 - #region Constructor + #region 构造函数 - public AttributeServiceEntryProvider(IEnumerable types, IClrServiceEntryFactory clrServiceEntryFactory, ILogger logger ,CPlatformContainer serviceProvider) + /// + /// Initializes a new instance of the class. + /// + /// The types + /// The clrServiceEntryFactory + /// The logger + /// The serviceProvider + public AttributeServiceEntryProvider(IEnumerable types, IClrServiceEntryFactory clrServiceEntryFactory, ILogger logger, CPlatformContainer serviceProvider) { _types = types; _clrServiceEntryFactory = clrServiceEntryFactory; @@ -31,18 +53,21 @@ public AttributeServiceEntryProvider(IEnumerable types, IClrServiceEntryFa _serviceProvider = serviceProvider; } - #endregion Constructor + #endregion 构造函数 - #region Implementation of IServiceEntryProvider + #region 方法 /// - /// 获取服务条目集合。 + /// The GetALLEntries /// - /// 服务条目集合。 - public IEnumerable GetEntries() + /// The + public IEnumerable GetALLEntries() { - var services = GetTypes(); - + var services = _types.Where(i => + { + var typeInfo = i.GetTypeInfo(); + return typeInfo.IsInterface && typeInfo.GetCustomAttribute() != null; + }).Distinct().ToArray(); if (_logger.IsEnabled(LogLevel.Information)) { _logger.LogInformation($"发现了以下服务:{string.Join(",", services.Select(i => i.ToString()))}。"); @@ -50,18 +75,19 @@ public IEnumerable GetEntries() var entries = new List(); foreach (var service in services) { - entries.AddRange( _clrServiceEntryFactory.CreateServiceEntry(service)); + entries.AddRange(_clrServiceEntryFactory.CreateServiceEntry(service)); } return entries; } - public IEnumerable GetALLEntries() + /// + /// 获取服务条目集合。 + /// + /// 服务条目集合。 + public IEnumerable GetEntries() { - var services = _types.Where(i => - { - var typeInfo = i.GetTypeInfo(); - return typeInfo.IsInterface && typeInfo.GetCustomAttribute() != null; - }).Distinct().ToArray(); + var services = GetTypes(); + if (_logger.IsEnabled(LogLevel.Information)) { _logger.LogInformation($"发现了以下服务:{string.Join(",", services.Select(i => i.ToString()))}。"); @@ -74,6 +100,10 @@ public IEnumerable GetALLEntries() return entries; } + /// + /// The GetTypes + /// + /// The public IEnumerable GetTypes() { var services = _types.Where(i => @@ -84,6 +114,6 @@ public IEnumerable GetTypes() return services; } - #endregion Implementation of IServiceEntryProvider + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Attributes/ServiceAttribute.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Attributes/ServiceAttribute.cs index d7bdc9883..d5e419b21 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Attributes/ServiceAttribute.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Attributes/ServiceAttribute.cs @@ -8,42 +8,59 @@ namespace Surging.Core.CPlatform.Runtime.Server.Implementation.ServiceDiscovery. [AttributeUsage(AttributeTargets.Method, Inherited = false)] public class ServiceAttribute : ServiceDescriptorAttribute { + #region 构造函数 + /// - /// 初始化构造函数 + /// Initializes a new instance of the class. /// public ServiceAttribute() { IsWaitExecution = true; } + #endregion 构造函数 + + #region 属性 + /// - /// 是否需要等待服务执行。 + /// Gets or sets the Date + /// 日期 /// - public bool IsWaitExecution { get; set; } + public string Date { get; set; } /// + /// Gets or sets the Director /// 负责人 /// public string Director { get; set; } /// + /// Gets or sets a value indicating whether DisableNetwork /// 是否禁用外网访问 /// public bool DisableNetwork { get; set; } /// + /// Gets or sets a value indicating whether EnableAuthorization /// 是否授权 /// public bool EnableAuthorization { get; set; } + /// - /// 名称 + /// Gets or sets a value indicating whether IsWaitExecution + /// 是否需要等待服务执行。 /// - public string Name { get; set; } + public bool IsWaitExecution { get; set; } + /// - /// 日期 + /// Gets or sets the Name + /// 名称 /// - public string Date { get; set; } - #region Overrides of DescriptorAttribute + public string Name { get; set; } + + #endregion 属性 + + #region 方法 /// /// 应用标记。 @@ -60,6 +77,6 @@ public override void Apply(ServiceDescriptor descriptor) .Date(Date); } - #endregion Overrides of ServiceDescriptorAttribute + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Attributes/ServiceBundleAttribute.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Attributes/ServiceBundleAttribute.cs index 8328af33b..0e071aea7 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Attributes/ServiceBundleAttribute.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Attributes/ServiceBundleAttribute.cs @@ -8,13 +8,33 @@ namespace Surging.Core.CPlatform.Runtime.Server.Implementation.ServiceDiscovery. [AttributeUsage(AttributeTargets.Interface)] public class ServiceBundleAttribute : Attribute { - public ServiceBundleAttribute(string routeTemplate,bool isPrefix=true) + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The routeTemplate + /// The isPrefix + public ServiceBundleAttribute(string routeTemplate, bool isPrefix = true) { RouteTemplate = routeTemplate; IsPrefix = isPrefix; } - public string RouteTemplate { get; } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets a value indicating whether IsPrefix + /// public bool IsPrefix { get; } + + /// + /// Gets the RouteTemplate + /// + public string RouteTemplate { get; } + + #endregion 属性 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Attributes/ServiceDescriptorAttribute.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Attributes/ServiceDescriptorAttribute.cs index 2e2d44605..dda47bbf9 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Attributes/ServiceDescriptorAttribute.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Attributes/ServiceDescriptorAttribute.cs @@ -7,10 +7,14 @@ namespace Surging.Core.CPlatform.Runtime.Server.Implementation.ServiceDiscovery. /// public abstract class ServiceDescriptorAttribute : Attribute { + #region 方法 + /// /// 应用标记。 /// /// 服务描述符。 public abstract void Apply(ServiceDescriptor descriptor); + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Attributes/ServiceMetadataAttribute.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Attributes/ServiceMetadataAttribute.cs index 449f81ad3..8b6ed7ea0 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Attributes/ServiceMetadataAttribute.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Attributes/ServiceMetadataAttribute.cs @@ -8,8 +8,10 @@ namespace Surging.Core.CPlatform.Runtime.Server.Implementation.ServiceDiscovery. [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)] public class ServiceMetadataAttribute : ServiceDescriptorAttribute { + #region 构造函数 + /// - /// 初始化构造函数。 + /// Initializes a new instance of the class. /// /// 名称。 /// 数据。 @@ -19,17 +21,25 @@ public ServiceMetadataAttribute(string name, object data) Data = data; } - /// - /// 名称。 - /// - public string Name { get; } + #endregion 构造函数 + + #region 属性 /// + /// Gets the Data /// 数据。 /// public object Data { get; } - #region Overrides of ServiceDescriptorAttribute + /// + /// Gets the Name + /// 名称。 + /// + public string Name { get; } + + #endregion 属性 + + #region 方法 /// /// 应用标记。 @@ -40,6 +50,6 @@ public override void Apply(ServiceDescriptor descriptor) descriptor.Metadatas[Name] = Data; } - #endregion Overrides of RpcServiceDescriptorAttribute + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Attributes/ServiceRouteAttribute.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Attributes/ServiceRouteAttribute.cs index dd791bf0c..14be1e6b5 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Attributes/ServiceRouteAttribute.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Attributes/ServiceRouteAttribute.cs @@ -4,13 +4,31 @@ namespace Surging.Core.CPlatform.Runtime.Server.Implementation.ServiceDiscovery.Attributes { - public class ServiceRouteAttribute: Attribute + /// + /// Defines the + /// + public class ServiceRouteAttribute : Attribute { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The template public ServiceRouteAttribute(string template) { Template = template; } - - public string Template { get; } + + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the Template + /// + public string Template { get; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/IClrServiceEntryFactory.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/IClrServiceEntryFactory.cs index e757aa95f..d76f840cb 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/IClrServiceEntryFactory.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/IClrServiceEntryFactory.cs @@ -3,17 +3,24 @@ namespace Surging.Core.CPlatform.Runtime.Server.Implementation.ServiceDiscovery { + #region 接口 + /// /// 一个抽象的Clr服务条目工厂。 /// public interface IClrServiceEntryFactory { + #region 方法 + /// /// 创建服务条目。 /// /// 服务类型。 - /// 服务实现类型。 /// 服务条目集合。 IEnumerable CreateServiceEntry(Type service); + + #endregion 方法 } + + #endregion 接口 } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Implementation/ClrServiceEntryFactory.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Implementation/ClrServiceEntryFactory.cs index ecbd2d770..bdddeb25c 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Implementation/ClrServiceEntryFactory.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceDiscovery/Implementation/ClrServiceEntryFactory.cs @@ -19,13 +19,33 @@ namespace Surging.Core.CPlatform.Runtime.Server.Implementation.ServiceDiscovery. /// public class ClrServiceEntryFactory : IClrServiceEntryFactory { - #region Field - private readonly CPlatformContainer _serviceProvider; + #region ֶ + + /// + /// Defines the _serviceIdGenerator + /// private readonly IServiceIdGenerator _serviceIdGenerator; + + /// + /// Defines the _serviceProvider + /// + private readonly CPlatformContainer _serviceProvider; + + /// + /// Defines the _typeConvertibleService + /// private readonly ITypeConvertibleService _typeConvertibleService; - #endregion Field - #region Constructor + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The serviceProvider + /// The serviceIdGenerator + /// The typeConvertibleService public ClrServiceEntryFactory(CPlatformContainer serviceProvider, IServiceIdGenerator serviceIdGenerator, ITypeConvertibleService typeConvertibleService) { _serviceProvider = serviceProvider; @@ -33,15 +53,14 @@ public ClrServiceEntryFactory(CPlatformContainer serviceProvider, IServiceIdGene _typeConvertibleService = typeConvertibleService; } - #endregion Constructor + #endregion 캯 - #region Implementation of IClrServiceEntryFactory + #region /// /// Ŀ /// /// ͡ - /// ʵ͡ /// Ŀϡ public IEnumerable CreateServiceEntry(Type service) { @@ -51,16 +70,20 @@ public IEnumerable CreateServiceEntry(Type service) var serviceRoute = methodInfo.GetCustomAttribute(); var routeTemplateVal = routeTemplate.RouteTemplate; if (!routeTemplate.IsPrefix && serviceRoute != null) - routeTemplateVal = serviceRoute.Template; + routeTemplateVal = serviceRoute.Template; else if (routeTemplate.IsPrefix && serviceRoute != null) - routeTemplateVal = $"{ routeTemplate.RouteTemplate}/{ serviceRoute.Template}"; + routeTemplateVal = $"{ routeTemplate.RouteTemplate}/{ serviceRoute.Template}"; yield return Create(methodInfo, service.Name, routeTemplateVal); } } - #endregion Implementation of IClrServiceEntryFactory - - #region Private Method + /// + /// The Create + /// + /// The method + /// The serviceName + /// The routeTemplate + /// The private ServiceEntry Create(MethodInfo method, string serviceName, string routeTemplate) { var serviceId = _serviceIdGenerator.GenerateServiceId(method); @@ -118,7 +141,13 @@ private ServiceEntry Create(MethodInfo method, string serviceName, string routeT } }; } - + + /// + /// The GetHandler + /// + /// The key + /// The method + /// The private FastInvokeHandler GetHandler(string key, MethodInfo method) { var objInstance = ServiceResolver.Current.GetService(null, key); @@ -129,6 +158,7 @@ private FastInvokeHandler GetHandler(string key, MethodInfo method) } return objInstance as FastInvokeHandler; } - #endregion Private Method + + #endregion } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceHostAbstract.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceHostAbstract.cs index 24c616624..9956d2600 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceHostAbstract.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceHostAbstract.cs @@ -11,37 +11,50 @@ namespace Surging.Core.CPlatform.Runtime.Server.Implementation /// public abstract class ServiceHostAbstract : IServiceHost { - #region Field - - private readonly IServiceExecutor _serviceExecutor; - - public IServiceExecutor ServiceExecutor { get => _serviceExecutor; } + #region 字段 /// - /// 消息监听者。 + /// Defines the _serviceExecutor /// - protected IMessageListener MessageListener { get; } = new MessageListener(); + private readonly IServiceExecutor _serviceExecutor; - #endregion Field + #endregion 字段 - #region Constructor + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The serviceExecutor protected ServiceHostAbstract(IServiceExecutor serviceExecutor) { _serviceExecutor = serviceExecutor; MessageListener.Received += MessageListener_Received; } - #endregion Constructor + #endregion 构造函数 - #region Implementation of IDisposable + #region 属性 - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - public abstract void Dispose(); + /// + /// Gets the ServiceExecutor + /// + public IServiceExecutor ServiceExecutor { get => _serviceExecutor; } + + /// + /// Gets the MessageListener + /// 消息监听者。 + /// + protected IMessageListener MessageListener { get; } = new MessageListener(); - #endregion Implementation of IDisposable + #endregion 属性 - #region Implementation of IServiceHost + #region 方法 + + /// + /// The Dispose + /// + public abstract void Dispose(); /// /// 启动主机。 @@ -50,17 +63,25 @@ protected ServiceHostAbstract(IServiceExecutor serviceExecutor) /// 一个任务。 public abstract Task StartAsync(EndPoint endPoint); - #endregion Implementation of IServiceHost - - #region Private Method + /// + /// The StartAsync + /// + /// The ip + /// The port + /// The + public abstract Task StartAsync(string ip, int port); + /// + /// The MessageListener_Received + /// + /// The sender + /// The message + /// The private async Task MessageListener_Received(IMessageSender sender, TransportMessage message) { await _serviceExecutor.ExecuteAsync(sender, message); } - public abstract Task StartAsync(string ip,int port); - - #endregion Private Method + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceTokenGenerator.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceTokenGenerator.cs index 410afc2b7..312ea0058 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceTokenGenerator.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/Implementation/ServiceTokenGenerator.cs @@ -2,9 +2,27 @@ namespace Surging.Core.CPlatform.Runtime.Server.Implementation { + /// + /// Defines the + /// public class ServiceTokenGenerator : IServiceTokenGenerator { + #region 字段 + + /// + /// Defines the _serviceToken + /// public string _serviceToken; + + #endregion 字段 + + #region 方法 + + /// + /// The GeneratorToken + /// + /// The code + /// The public string GeneratorToken(string code) { bool enableToken; @@ -20,9 +38,15 @@ public string GeneratorToken(string code) return _serviceToken; } + /// + /// The GetToken + /// + /// The public string GetToken() { return _serviceToken; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/ServiceEntry.cs b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/ServiceEntry.cs index 83c599a4a..2a934e0bf 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/ServiceEntry.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Runtime/Server/ServiceEntry.cs @@ -9,18 +9,40 @@ namespace Surging.Core.CPlatform.Runtime.Server /// public class ServiceEntry { + #region 属性 + /// - /// 执行委托。 + /// Gets or sets the Attributes /// - public Func, Task> Func { get; set; } - - public string RoutePath { get; set; } - public Type Type { get; set; } - public string MethodName { get; set; } public List Attributes { get; set; } + /// + /// Gets or sets the Descriptor /// 服务描述符。 /// public ServiceDescriptor Descriptor { get; set; } + + /// + /// Gets or sets the Func + /// 执行委托。 + /// + public Func, Task> Func { get; set; } + + /// + /// Gets or sets the MethodName + /// + public string MethodName { get; set; } + + /// + /// Gets or sets the RoutePath + /// + public string RoutePath { get; set; } + + /// + /// Gets or sets the Type + /// + public Type Type { get; set; } + + #endregion 属性 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Serialization/ISerializer.cs b/src/Surging.Core/Surging.Core.CPlatform/Serialization/ISerializer.cs index a78174601..3bbcd4001 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Serialization/ISerializer.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Serialization/ISerializer.cs @@ -2,18 +2,15 @@ namespace Surging.Core.CPlatform.Serialization { + #region 接口 + /// /// 一个抽象的序列化器。 /// /// 序列化内容类型。 public interface ISerializer { - /// - /// 序列化。 - /// - /// 需要序列化的对象。 - /// 序列化之后的结果。 - T Serialize(object instance); + #region 方法 /// /// 反序列化。 @@ -22,7 +19,16 @@ public interface ISerializer /// 对象类型。 /// 一个对象实例。 object Deserialize(T content, Type type); + + /// + /// 序列化。 + /// + /// 需要序列化的对象。 + /// 序列化之后的结果。 + T Serialize(object instance); + + #endregion 方法 } - + #endregion 接口 } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Serialization/Implementation/JsonSerializer.cs b/src/Surging.Core/Surging.Core.CPlatform/Serialization/Implementation/JsonSerializer.cs index 53a0a56ca..b097ca85a 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Serialization/Implementation/JsonSerializer.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Serialization/Implementation/JsonSerializer.cs @@ -8,17 +8,7 @@ namespace Surging.Core.CPlatform.Serialization.Implementation /// public sealed class JsonSerializer : ISerializer { - #region Implementation of ISerializer - - /// - /// 序列化。 - /// - /// 需要序列化的对象。 - /// 序列化之后的结果。 - public string Serialize(object instance) - { - return JsonConvert.SerializeObject(instance); - } + #region 方法 /// /// 反序列化。 @@ -31,6 +21,16 @@ public object Deserialize(string content, Type type) return JsonConvert.DeserializeObject(content, type); } - #endregion Implementation of ISerializer + /// + /// 序列化。 + /// + /// 需要序列化的对象。 + /// 序列化之后的结果。 + public string Serialize(object instance) + { + return JsonConvert.SerializeObject(instance); + } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Serialization/Implementation/StringByteArraySerializer.cs b/src/Surging.Core/Surging.Core.CPlatform/Serialization/Implementation/StringByteArraySerializer.cs index 5983111f3..420367fa3 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Serialization/Implementation/StringByteArraySerializer.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Serialization/Implementation/StringByteArraySerializer.cs @@ -8,32 +8,29 @@ namespace Surging.Core.CPlatform.Serialization.Implementation /// public class StringByteArraySerializer : ISerializer { - #region Field + #region 字段 + /// + /// Defines the _serializer + /// private readonly ISerializer _serializer; - #endregion Field + #endregion 字段 - #region Constructor + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The serializer public StringByteArraySerializer(ISerializer serializer) { _serializer = serializer; } - #endregion Constructor - - #region Implementation of ISerializer + #endregion 构造函数 - /// - /// 序列化。 - /// - /// 需要序列化的对象。 - /// 序列化之后的结果。 - public byte[] Serialize(object instance) - { - return Encoding.UTF8.GetBytes(_serializer.Serialize(instance)); - } + #region 方法 /// /// 反序列化。 @@ -46,6 +43,16 @@ public object Deserialize(byte[] content, Type type) return _serializer.Deserialize(Encoding.UTF8.GetString(content), type); } - #endregion Implementation of ISerializer + /// + /// 序列化。 + /// + /// 需要序列化的对象。 + /// 序列化之后的结果。 + public byte[] Serialize(object instance) + { + return Encoding.UTF8.GetBytes(_serializer.Serialize(instance)); + } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Serialization/Implementation/StringObjectSerializer.cs b/src/Surging.Core/Surging.Core.CPlatform/Serialization/Implementation/StringObjectSerializer.cs index 762cb0ef7..5b7db26c5 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Serialization/Implementation/StringObjectSerializer.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Serialization/Implementation/StringObjectSerializer.cs @@ -7,32 +7,29 @@ namespace Surging.Core.CPlatform.Serialization.Implementation /// public class StringObjectSerializer : ISerializer { - #region Field + #region 字段 + /// + /// Defines the _serializer + /// private readonly ISerializer _serializer; - #endregion Field + #endregion 字段 - #region Constructor + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The serializer public StringObjectSerializer(ISerializer serializer) { _serializer = serializer; } - #endregion Constructor - - #region Implementation of ISerializer + #endregion 构造函数 - /// - /// 序列化。 - /// - /// 需要序列化的对象。 - /// 序列化之后的结果。 - public object Serialize(object instance) - { - return _serializer.Serialize(instance); - } + #region 方法 /// /// 反序列化。 @@ -45,6 +42,16 @@ public object Deserialize(object content, Type type) return _serializer.Deserialize(content.ToString(), type); } - #endregion Implementation of ISerializer + /// + /// 序列化。 + /// + /// 需要序列化的对象。 + /// 序列化之后的结果。 + public object Serialize(object instance) + { + return _serializer.Serialize(instance); + } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Serialization/SerializerExtensions.cs b/src/Surging.Core/Surging.Core.CPlatform/Serialization/SerializerExtensions.cs index 4d4121973..165b44049 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Serialization/SerializerExtensions.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Serialization/SerializerExtensions.cs @@ -5,6 +5,8 @@ /// public static class SerializerExtensions { + #region 方法 + /// /// 反序列化方法 /// @@ -17,5 +19,7 @@ public static TResult Deserialize(this ISerializer serializer, T { return (TResult)serializer.Deserialize(content, typeof(TResult)); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/ServiceDescriptor.cs b/src/Surging.Core/Surging.Core.CPlatform/ServiceDescriptor.cs index 3d62aba1f..45808e863 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/ServiceDescriptor.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/ServiceDescriptor.cs @@ -8,99 +8,128 @@ namespace Surging.Core.CPlatform { /// - /// 服务描述符扩展方法。 + /// 服务描述符。 /// - public static class ServiceDescriptorExtensions + [Serializable] + public class ServiceDescriptor { + #region 构造函数 + /// - /// 获取组名称。 + /// Initializes a new instance of the class. /// - /// 服务描述符。 - /// 组名称。 - public static string GroupName(this ServiceDescriptor descriptor) + public ServiceDescriptor() { - return descriptor.GetMetadata("GroupName"); + Metadatas = new Dictionary(StringComparer.OrdinalIgnoreCase); } - + + #endregion 构造函数 + + #region 属性 + /// - /// 设置组名称。 + /// Gets or sets the Id + /// 服务Id。 /// - /// 服务描述符。 - /// 组名称。 - /// 服务描述符。 - public static ServiceDescriptor GroupName(this ServiceDescriptor descriptor, string groupName) - { - descriptor.Metadatas["GroupName"] = groupName; - return descriptor; - } + public string Id { get; set; } /// - /// 设置是否等待执行。 + /// Gets or sets the Metadatas + /// 元数据。 /// - /// 服务描述符。 - /// 如果需要等待执行则为true,否则为false,默认为true。 - /// 服务描述符。 - public static ServiceDescriptor WaitExecution(this ServiceDescriptor descriptor, bool waitExecution) - { - descriptor.Metadatas["WaitExecution"] = waitExecution; - return descriptor; - } + public IDictionary Metadatas { get; set; } /// - /// 获取负责人 + /// Gets or sets the RoutePath + /// 路由 /// - /// 服务描述符。 - /// 负责人名字 - /// 服务描述符。 - public static ServiceDescriptor Director(this ServiceDescriptor descriptor, string director) - { - descriptor.Metadatas["Director"] = director; - return descriptor; - } + public string RoutePath { get; set; } /// - /// 设置是否启用授权 + /// Gets or sets the Token + /// 访问的令牌 /// - /// 服务描述符。 - /// 是否启用 - /// 服务描述符。 - public static ServiceDescriptor EnableAuthorization(this ServiceDescriptor descriptor, bool enable) - { - descriptor.Metadatas["EnableAuthorization"] = enable; - return descriptor; - } + public string Token { get; set; } + + #endregion 属性 + + #region 方法 /// - /// 获取是否启用授权 + /// The Equals /// - /// 服务描述符。 - /// 服务描述符。 - public static bool EnableAuthorization(this ServiceDescriptor descriptor) + /// The object to compare with the current object. + /// true if the specified object is equal to the current object; otherwise, false. + public override bool Equals(object obj) { - return descriptor.GetMetadata("EnableAuthorization", false); + var model = obj as ServiceDescriptor; + if (model == null) + return false; + + if (obj.GetType() != GetType()) + return false; + + if (model.Id != Id) + return false; + + return model.Metadatas.Count == Metadatas.Count && model.Metadatas.All(metadata => + { + object value; + if (!Metadatas.TryGetValue(metadata.Key, out value)) + return false; + + if (metadata.Value == null && value == null) + return true; + if (metadata.Value == null || value == null) + return false; + + return metadata.Value.Equals(value); + }); } /// - /// 设置是否禁用外网访问 + /// The GetHashCode /// - /// 服务描述符。 - /// 是否禁用 - /// 服务描述符。 - public static ServiceDescriptor DisableNetwork(this ServiceDescriptor descriptor, bool enable) + /// A hash code for the current object. + public override int GetHashCode() { - descriptor.Metadatas["DisableNetwork"] = enable; - return descriptor; + return ToString().GetHashCode(); } /// - /// 获取是否禁用外网访问 + /// 获取一个元数据。 /// - /// 服务描述符。 - /// 服务描述符。 - public static bool DisableNetwork(this ServiceDescriptor descriptor) + /// 元数据类型。 + /// 元数据名称。 + /// 如果指定名称的元数据不存在则返回这个参数。 + /// 元数据值。 + public T GetMetadata(string name, T def = default(T)) { - return descriptor.GetMetadata("DisableNetwork", false); + if (!Metadatas.ContainsKey(name)) + return def; + + return (T)Metadatas[name]; + } + + #endregion 方法 + + public static bool operator ==(ServiceDescriptor model1, ServiceDescriptor model2) + { + return Equals(model1, model2); + } + + public static bool operator !=(ServiceDescriptor model1, ServiceDescriptor model2) + { + return !Equals(model1, model2); } + } + + /// + /// 服务描述符扩展方法。 + /// + public static class ServiceDescriptorExtensions + { + #region 方法 /// /// 获取授权类型 @@ -111,7 +140,6 @@ public static string AuthType(this ServiceDescriptor descriptor) { return descriptor.GetMetadata("AuthType", AuthorizationType.AppSecret.ToString()); } - /// /// 设置授权类型 @@ -126,15 +154,15 @@ public static ServiceDescriptor AuthType(this ServiceDescriptor descriptor, Auth } /// - /// 获取负责人 + /// 获取日期 /// /// 服务描述符。 - /// - public static string Director(this ServiceDescriptor descriptor) + /// 服务描述符。 + public static string Date(this ServiceDescriptor descriptor) { - return descriptor.GetMetadata("Director"); + return descriptor.GetMetadata("Date"); } - + /// /// 获取日期 /// @@ -146,126 +174,117 @@ public static ServiceDescriptor Date(this ServiceDescriptor descriptor, string d descriptor.Metadatas["Date"] = date; return descriptor; } - + /// - /// 获取日期 + /// 获取负责人 /// /// 服务描述符。 - /// 服务描述符。 - public static string Date(this ServiceDescriptor descriptor) + /// + public static string Director(this ServiceDescriptor descriptor) { - return descriptor.GetMetadata("Date"); + return descriptor.GetMetadata("Director"); } /// - /// 获取释放等待执行的设置。 + /// 获取负责人 /// /// 服务描述符。 - /// 如果需要等待执行则为true,否则为false,默认为true。 - public static bool WaitExecution(this ServiceDescriptor descriptor) + /// The director + /// 服务描述符。 + public static ServiceDescriptor Director(this ServiceDescriptor descriptor, string director) { - return descriptor.GetMetadata("WaitExecution", true); + descriptor.Metadatas["Director"] = director; + return descriptor; } - } - /// - /// 服务描述符。 - /// - [Serializable] - public class ServiceDescriptor - { /// - /// 初始化一个新的服务描述符。 + /// 获取是否禁用外网访问 /// - public ServiceDescriptor() + /// 服务描述符。 + /// 服务描述符。 + public static bool DisableNetwork(this ServiceDescriptor descriptor) { - Metadatas = new Dictionary(StringComparer.OrdinalIgnoreCase); + return descriptor.GetMetadata("DisableNetwork", false); } /// - /// 服务Id。 - /// - public string Id { get; set; } - - /// - /// 访问的令牌 + /// 设置是否禁用外网访问 /// - public string Token { get; set; } + /// 服务描述符。 + /// 是否禁用 + /// 服务描述符。 + public static ServiceDescriptor DisableNetwork(this ServiceDescriptor descriptor, bool enable) + { + descriptor.Metadatas["DisableNetwork"] = enable; + return descriptor; + } /// - /// 路由 + /// 获取是否启用授权 /// - public string RoutePath { get; set; } - - /// - /// 元数据。 - /// - public IDictionary Metadatas { get; set; } + /// 服务描述符。 + /// 服务描述符。 + public static bool EnableAuthorization(this ServiceDescriptor descriptor) + { + return descriptor.GetMetadata("EnableAuthorization", false); + } /// - /// 获取一个元数据。 + /// 设置是否启用授权 /// - /// 元数据类型。 - /// 元数据名称。 - /// 如果指定名称的元数据不存在则返回这个参数。 - /// 元数据值。 - public T GetMetadata(string name, T def = default(T)) + /// 服务描述符。 + /// 是否启用 + /// 服务描述符。 + public static ServiceDescriptor EnableAuthorization(this ServiceDescriptor descriptor, bool enable) { - if (!Metadatas.ContainsKey(name)) - return def; - - return (T)Metadatas[name]; + descriptor.Metadatas["EnableAuthorization"] = enable; + return descriptor; } - #region Equality members - - /// Determines whether the specified object is equal to the current object. - /// true if the specified object is equal to the current object; otherwise, false. - /// The object to compare with the current object. - public override bool Equals(object obj) + /// + /// 获取组名称。 + /// + /// 服务描述符。 + /// 组名称。 + public static string GroupName(this ServiceDescriptor descriptor) { - var model = obj as ServiceDescriptor; - if (model == null) - return false; - - if (obj.GetType() != GetType()) - return false; - - if (model.Id != Id) - return false; - - return model.Metadatas.Count == Metadatas.Count && model.Metadatas.All(metadata => - { - object value; - if (!Metadatas.TryGetValue(metadata.Key, out value)) - return false; - - if (metadata.Value == null && value == null) - return true; - if (metadata.Value == null || value == null) - return false; - - return metadata.Value.Equals(value); - }); + return descriptor.GetMetadata("GroupName"); } - /// Serves as the default hash function. - /// A hash code for the current object. - public override int GetHashCode() + /// + /// 设置组名称。 + /// + /// 服务描述符。 + /// 组名称。 + /// 服务描述符。 + public static ServiceDescriptor GroupName(this ServiceDescriptor descriptor, string groupName) { - return ToString().GetHashCode(); + descriptor.Metadatas["GroupName"] = groupName; + return descriptor; } - public static bool operator ==(ServiceDescriptor model1, ServiceDescriptor model2) + /// + /// 获取释放等待执行的设置。 + /// + /// 服务描述符。 + /// 如果需要等待执行则为true,否则为false,默认为true。 + public static bool WaitExecution(this ServiceDescriptor descriptor) { - return Equals(model1, model2); + return descriptor.GetMetadata("WaitExecution", true); } - public static bool operator !=(ServiceDescriptor model1, ServiceDescriptor model2) + /// + /// 设置是否等待执行。 + /// + /// 服务描述符。 + /// 如果需要等待执行则为true,否则为false,默认为true。 + /// 服务描述符。 + public static ServiceDescriptor WaitExecution(this ServiceDescriptor descriptor, bool waitExecution) { - return !Equals(model1, model2); + descriptor.Metadatas["WaitExecution"] = waitExecution; + return descriptor; } - #endregion Equality members + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/ServiceHostBuilderExtensions.cs b/src/Surging.Core/Surging.Core.CPlatform/ServiceHostBuilderExtensions.cs index 4d99e80c9..e57d200bd 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/ServiceHostBuilderExtensions.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/ServiceHostBuilderExtensions.cs @@ -1,63 +1,79 @@ using Autofac; -using Surging.Core.CPlatform.Support; -using System.Linq; -using Surging.Core.CPlatform.Routing; -using Surging.Core.CPlatform.Address; -using System.Threading.Tasks; -using Surging.Core.ServiceHosting.Internal; -using Surging.Core.CPlatform.Runtime.Server; -using System.Net; -using System.Net.NetworkInformation; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; -using Surging.Core.CPlatform.Runtime.Client; -using System; +using Surging.Core.CPlatform.Address; using Surging.Core.CPlatform.Configurations; -using Surging.Core.CPlatform.Module; -using System.Diagnostics; using Surging.Core.CPlatform.Engines; +using Surging.Core.CPlatform.Module; +using Surging.Core.CPlatform.Routing; +using Surging.Core.CPlatform.Runtime.Client; +using Surging.Core.CPlatform.Runtime.Server; +using Surging.Core.CPlatform.Support; +using Surging.Core.CPlatform.Transport.Implementation; using Surging.Core.CPlatform.Utilities; +using Surging.Core.ServiceHosting.Internal; +using System; using System.Collections.Generic; -using Microsoft.Extensions.Configuration; +using System.Diagnostics; using System.IO; -using Surging.Core.CPlatform.Transport.Implementation; +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Threading.Tasks; namespace Surging.Core.CPlatform { + /// + /// Defines the + /// public static class ServiceHostBuilderExtensions { - public static IServiceHostBuilder UseServer(this IServiceHostBuilder hostBuilder, string ip, int port, string token = "True") + #region 方法 + + /// + /// The BuildServiceEngine + /// + /// The container + public static void BuildServiceEngine(IContainer container) { - return hostBuilder.MapServices(async mapper => + if (container.IsRegistered()) { - BuildServiceEngine(mapper); - await mapper.Resolve().SetServiceCommandsAsync(); - string serviceToken = mapper.Resolve().GeneratorToken(token); - int _port = AppConfig.ServerOptions.Port = AppConfig.ServerOptions.Port == 0 ? port : AppConfig.ServerOptions.Port; - string _ip = AppConfig.ServerOptions.Ip = AppConfig.ServerOptions.Ip ?? ip; - _port = AppConfig.ServerOptions.Port = AppConfig.ServerOptions.IpEndpoint?.Port ?? _port; - _ip = AppConfig.ServerOptions.Ip = AppConfig.ServerOptions.IpEndpoint?.Address.ToString() ?? _ip; - _ip = NetUtils.GetHostAddress(_ip); + var builder = new ContainerBuilder(); - await ConfigureRoute(mapper, serviceToken); - mapper.Resolve().Initialize(); - var serviceHosts = mapper.Resolve>(); - Task.Factory.StartNew(async () => - { - foreach (var serviceHost in serviceHosts) - await serviceHost.StartAsync(_ip, _port); - mapper.Resolve().NotifyStarted(); - }).Wait(); - }); + container.Resolve().Build(builder); + var configBuilder = container.Resolve(); + var appSettingPath = Path.Combine(AppConfig.ServerOptions.RootPath, "appsettings.json"); + configBuilder.AddCPlatformFile("${appsettingspath}|" + appSettingPath, optional: false, reloadOnChange: true); + builder.Update(container); + } } - public static IServiceHostBuilder UseServer(this IServiceHostBuilder hostBuilder, Action options) + /// + /// The ConfigureRoute + /// + /// The mapper + /// The serviceToken + /// The + public static async Task ConfigureRoute(IContainer mapper, string serviceToken) { - var serverOptions = new SurgingServerOptions(); - options.Invoke(serverOptions); - AppConfig.ServerOptions = serverOptions; - return hostBuilder.UseServer(serverOptions.Ip, serverOptions.Port, serverOptions.Token); + if (AppConfig.ServerOptions.Protocol == CommunicationProtocol.Tcp || + AppConfig.ServerOptions.Protocol == CommunicationProtocol.None) + { + var routeProvider = mapper.Resolve(); + if (AppConfig.ServerOptions.EnableRouteWatch) + new ServiceRouteWatch(mapper.Resolve(), + async () => await routeProvider.RegisterRoutes( + Math.Round(Convert.ToDecimal(Process.GetCurrentProcess().TotalProcessorTime.TotalSeconds), 2, MidpointRounding.AwayFromZero))); + else + await routeProvider.RegisterRoutes(0); + } } + /// + /// The UseClient + /// + /// The hostBuilder + /// The public static IServiceHostBuilder UseClient(this IServiceHostBuilder hostBuilder) { return hostBuilder.MapServices(mapper => @@ -80,34 +96,53 @@ public static IServiceHostBuilder UseClient(this IServiceHostBuilder hostBuilder }); } - public static void BuildServiceEngine(IContainer container) + /// + /// The UseServer + /// + /// The hostBuilder + /// The options + /// The + public static IServiceHostBuilder UseServer(this IServiceHostBuilder hostBuilder, Action options) { - if (container.IsRegistered()) - { - var builder = new ContainerBuilder(); - - container.Resolve().Build(builder); - var configBuilder = container.Resolve(); - var appSettingPath = Path.Combine(AppConfig.ServerOptions.RootPath, "appsettings.json"); - configBuilder.AddCPlatformFile("${appsettingspath}|" + appSettingPath, optional: false, reloadOnChange: true); - builder.Update(container); - } + var serverOptions = new SurgingServerOptions(); + options.Invoke(serverOptions); + AppConfig.ServerOptions = serverOptions; + return hostBuilder.UseServer(serverOptions.Ip, serverOptions.Port, serverOptions.Token); } - public static async Task ConfigureRoute(IContainer mapper, string serviceToken) + /// + /// The UseServer + /// + /// The hostBuilder + /// The ip + /// The port + /// The token + /// The + public static IServiceHostBuilder UseServer(this IServiceHostBuilder hostBuilder, string ip, int port, string token = "True") { - if (AppConfig.ServerOptions.Protocol == CommunicationProtocol.Tcp || - AppConfig.ServerOptions.Protocol == CommunicationProtocol.None) + return hostBuilder.MapServices(async mapper => { - var routeProvider = mapper.Resolve(); - if (AppConfig.ServerOptions.EnableRouteWatch) - new ServiceRouteWatch(mapper.Resolve(), - async () => await routeProvider.RegisterRoutes( - Math.Round(Convert.ToDecimal(Process.GetCurrentProcess().TotalProcessorTime.TotalSeconds), 2, MidpointRounding.AwayFromZero))); - else - await routeProvider.RegisterRoutes(0); - } + BuildServiceEngine(mapper); + await mapper.Resolve().SetServiceCommandsAsync(); + string serviceToken = mapper.Resolve().GeneratorToken(token); + int _port = AppConfig.ServerOptions.Port = AppConfig.ServerOptions.Port == 0 ? port : AppConfig.ServerOptions.Port; + string _ip = AppConfig.ServerOptions.Ip = AppConfig.ServerOptions.Ip ?? ip; + _port = AppConfig.ServerOptions.Port = AppConfig.ServerOptions.IpEndpoint?.Port ?? _port; + _ip = AppConfig.ServerOptions.Ip = AppConfig.ServerOptions.IpEndpoint?.Address.ToString() ?? _ip; + _ip = NetUtils.GetHostAddress(_ip); + + await ConfigureRoute(mapper, serviceToken); + mapper.Resolve().Initialize(); + var serviceHosts = mapper.Resolve>(); + Task.Factory.StartNew(async () => + { + foreach (var serviceHost in serviceHosts) + await serviceHost.StartAsync(_ip, _port); + mapper.Resolve().NotifyStarted(); + }).Wait(); + }); } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Support/Attributes/CommandAttribute.cs b/src/Surging.Core/Surging.Core.CPlatform/Support/Attributes/CommandAttribute.cs index 0c490ea77..478e9e4aa 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Support/Attributes/CommandAttribute.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Support/Attributes/CommandAttribute.cs @@ -5,10 +5,17 @@ namespace Surging.Core.CPlatform.Support.Attributes { - + /// + /// Defines the + /// [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)] public class CommandAttribute : Attribute { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public CommandAttribute() { if (AppConfig.ServerOptions != null) @@ -28,65 +35,94 @@ public CommandAttribute() FallBackName = AppConfig.ServerOptions.FallBackName; } } + + #endregion 构造函数 + + #region 属性 + /// - /// 故障转移次数 + /// Gets or sets the BreakeErrorThresholdPercentage + /// 错误率达到多少开启熔断保护 /// - public int FailoverCluster { get; set; } = 3; - public bool CircuitBreakerForceOpen { get; set; } + public int BreakeErrorThresholdPercentage { get; set; } = 50; + /// - /// 容错策略 + /// Gets or sets a value indicating whether BreakerForceClosed + /// 是否强制关闭熔断 /// - public StrategyType Strategy { get; set; } + public bool BreakerForceClosed { get; set; } + /// + /// Gets or sets the BreakerRequestVolumeThreshold + /// 10秒钟内至少多少请求失败,熔断器才发挥起作用 + /// + public int BreakerRequestVolumeThreshold { get; set; } = 20; + + /// + /// Gets or sets the BreakeSleepWindowInMilliseconds + /// 熔断多少秒后去尝试请求 + /// + public int BreakeSleepWindowInMilliseconds { get; set; } = 60000; + + /// + /// Gets or sets a value indicating whether CircuitBreakerForceOpen + /// + public bool CircuitBreakerForceOpen { get; set; } + + /// + /// Gets or sets the ExecutionTimeoutInMilliseconds /// 执行超时时间 /// public int ExecutionTimeoutInMilliseconds { get; set; } = 1000; + /// - /// 是否开启缓存 + /// Gets or sets the FailoverCluster + /// 故障转移次数 /// - public bool RequestCacheEnabled { get; set; } + public int FailoverCluster { get; set; } = 3; + + /// + /// Gets or sets the FallBackName + /// IFallbackInvoker 实例名称 + /// + public string FallBackName { get; set; } + /// + /// Gets or sets the Injection /// 注入 /// public string Injection { get; set; } = "return null"; /// + /// Gets or sets the InjectionNamespaces /// 注入命名空间 /// public string[] InjectionNamespaces { get; set; } /// - /// 错误率达到多少开启熔断保护 - /// - public int BreakeErrorThresholdPercentage { get; set; } = 50; - /// - /// 熔断多少秒后去尝试请求 + /// Gets or sets the MaxConcurrentRequests + /// 信号量最大并发度 /// - public int BreakeSleepWindowInMilliseconds { get; set; } = 60000; + public int MaxConcurrentRequests { get; set; } = 10; + /// - /// 是否强制关闭熔断 + /// Gets or sets a value indicating whether RequestCacheEnabled + /// 是否开启缓存 /// - public bool BreakerForceClosed { get; set; } + public bool RequestCacheEnabled { get; set; } /// + /// Gets or sets the ShuntStrategy /// 负载分流策略 /// public AddressSelectorMode ShuntStrategy { get; set; } = AddressSelectorMode.Polling; - - /// - /// IFallbackInvoker 实例名称 - /// - public string FallBackName { get; set; } - /// /// - /// 10秒钟内至少多少请求失败,熔断器才发挥起作用 + /// Gets or sets the Strategy + /// 容错策略 /// - public int BreakerRequestVolumeThreshold { get; set; } = 20; + public StrategyType Strategy { get; set; } - /// - /// 信号量最大并发度 - /// - public int MaxConcurrentRequests { get; set; } = 10; + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Support/IBreakeRemoteInvokeService.cs b/src/Surging.Core/Surging.Core.CPlatform/Support/IBreakeRemoteInvokeService.cs index 5dfc059e3..ed312f130 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Support/IBreakeRemoteInvokeService.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Support/IBreakeRemoteInvokeService.cs @@ -4,8 +4,27 @@ namespace Surging.Core.CPlatform.Support { + #region 接口 + + /// + /// Defines the + /// public interface IBreakeRemoteInvokeService { + #region 方法 + + /// + /// The InvokeAsync + /// + /// The parameters + /// The serviceId + /// The _serviceKey + /// The decodeJOject + /// The Task InvokeAsync(IDictionary parameters, string serviceId, string _serviceKey, bool decodeJOject); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Support/IClusterInvoker.cs b/src/Surging.Core/Surging.Core.CPlatform/Support/IClusterInvoker.cs index 421528473..7c3238f07 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Support/IClusterInvoker.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Support/IClusterInvoker.cs @@ -3,10 +3,38 @@ namespace Surging.Core.CPlatform.Support { + #region 接口 + + /// + /// Defines the + /// public interface IClusterInvoker { - Task Invoke(IDictionary parameters, string serviceId, string _serviceKey,bool decodeJOject); + #region 方法 + + /// + /// The Invoke + /// + /// The parameters + /// The serviceId + /// The _serviceKey + /// The decodeJOject + /// The + Task Invoke(IDictionary parameters, string serviceId, string _serviceKey, bool decodeJOject); - Task Invoke(IDictionary parameters, string serviceId, string _serviceKey,bool decodeJOject); + /// + /// The Invoke + /// + /// + /// The parameters + /// The serviceId + /// The _serviceKey + /// The decodeJOject + /// The + Task Invoke(IDictionary parameters, string serviceId, string _serviceKey, bool decodeJOject); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Support/IFallbackInvoker.cs b/src/Surging.Core/Surging.Core.CPlatform/Support/IFallbackInvoker.cs index 4130e17d3..11156e192 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Support/IFallbackInvoker.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Support/IFallbackInvoker.cs @@ -5,10 +5,36 @@ namespace Surging.Core.CPlatform.Support { + #region 接口 + + /// + /// Defines the + /// public interface IFallbackInvoker { + #region 方法 + + /// + /// The Invoke + /// + /// The parameters + /// The serviceId + /// The _serviceKey + /// The Task Invoke(IDictionary parameters, string serviceId, string _serviceKey); + /// + /// The Invoke + /// + /// + /// The parameters + /// The serviceId + /// The _serviceKey + /// The Task Invoke(IDictionary parameters, string serviceId, string _serviceKey); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Support/IServiceCommandManager.cs b/src/Surging.Core/Surging.Core.CPlatform/Support/IServiceCommandManager.cs index 83c0ee845..135757f85 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Support/IServiceCommandManager.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Support/IServiceCommandManager.cs @@ -7,8 +7,20 @@ namespace Surging.Core.CPlatform.Support { + #region 接口 + + /// + /// Defines the + /// public interface IServiceCommandManager { + #region 事件 + + /// + /// 服务命令被修改。 + /// + event EventHandler Changed; + /// /// 服务命令被创建。 /// @@ -19,10 +31,15 @@ public interface IServiceCommandManager /// event EventHandler Removed; + #endregion 事件 + + #region 方法 + /// - /// 服务命令被修改。 + /// 清空所有的服务命令。 /// - event EventHandler Changed; + /// 一个任务。 + Task ClearAsync(); /// /// 获取所有可用的服务命令信息。 @@ -30,6 +47,12 @@ public interface IServiceCommandManager /// 服务路由集合。 Task> GetServiceCommandsAsync(); + /// + /// The SetServiceCommandsAsync + /// + /// The + Task SetServiceCommandsAsync(); + /// /// 设置服务命令。 /// @@ -37,14 +60,10 @@ public interface IServiceCommandManager /// 一个任务。 Task SetServiceCommandsAsync(IEnumerable commands); - - Task SetServiceCommandsAsync(); - /// - /// 清空所有的服务命令。 - /// - /// 一个任务。 - Task ClearAsync(); + #endregion 方法 } + + #endregion 接口 } /// @@ -52,9 +71,13 @@ public interface IServiceCommandManager /// public static class ServiceCommandManagerExtensions { + #region 方法 + /// /// 获取所有可用的服务命令信息。 /// + /// The serviceCommandManager + /// The serviceIds /// 服务命令集合。 public static async Task> GetServiceCommandsAsync (this IServiceCommandManager serviceCommandManager, params string[] serviceIds) @@ -62,4 +85,6 @@ public static async Task> GetServiceComman var result = (await serviceCommandManager.GetServiceCommandsAsync()); return result.Where(p => serviceIds.Contains(p.ServiceId)); } + + #endregion 方法 } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Support/IServiceCommandProvider.cs b/src/Surging.Core/Surging.Core.CPlatform/Support/IServiceCommandProvider.cs index 4905dab7e..bf835285b 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Support/IServiceCommandProvider.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Support/IServiceCommandProvider.cs @@ -5,9 +5,32 @@ namespace Surging.Core.CPlatform.Support { + #region 接口 + + /// + /// Defines the + /// public interface IServiceCommandProvider { + #region 方法 + + /// + /// The GetCommand + /// + /// The serviceId + /// The ValueTask GetCommand(string serviceId); + + /// + /// The Run + /// + /// The text + /// The InjectionNamespaces + /// The Task Run(string text, params string[] InjectionNamespaces); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Support/Implementation/BreakeRemoteInvokeService.cs b/src/Surging.Core/Surging.Core.CPlatform/Support/Implementation/BreakeRemoteInvokeService.cs index 5d9bcac97..2e46c9b58 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Support/Implementation/BreakeRemoteInvokeService.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Support/Implementation/BreakeRemoteInvokeService.cs @@ -1,31 +1,71 @@ -using Microsoft.Extensions.Logging; +using Autofac; +using Microsoft.Extensions.Logging; +using Surging.Core.CPlatform.Filters; +using Surging.Core.CPlatform.Filters.Implementation; +using Surging.Core.CPlatform.HashAlgorithms; using Surging.Core.CPlatform.Messages; using Surging.Core.CPlatform.Runtime.Client; using Surging.Core.CPlatform.Runtime.Client.Address.Resolvers.Implementation.Selectors.Implementation; +using Surging.Core.CPlatform.Transport.Implementation; using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Threading.Tasks; using System.Linq; -using Surging.Core.CPlatform.HashAlgorithms; -using Surging.Core.CPlatform.Transport.Implementation; -using Surging.Core.CPlatform.Filters; -using Autofac; -using System.Threading; -using Surging.Core.CPlatform.Filters.Implementation; using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; namespace Surging.Core.CPlatform.Support.Implementation { + /// + /// Defines the + /// public class BreakeRemoteInvokeService : IBreakeRemoteInvokeService { + #region 字段 + + /// + /// Defines the _commandProvider + /// private readonly IServiceCommandProvider _commandProvider; - private readonly IRemoteInvokeService _remoteInvokeService; + + /// + /// Defines the _hashAlgorithm + /// + private readonly IHashAlgorithm _hashAlgorithm; + + /// + /// Defines the _logger + /// private readonly ILogger _logger; + + /// + /// Defines the _remoteInvokeService + /// + private readonly IRemoteInvokeService _remoteInvokeService; + + /// + /// Defines the _serviceInvokeListenInfo + /// private readonly ConcurrentDictionary _serviceInvokeListenInfo = new ConcurrentDictionary(); - private readonly IHashAlgorithm _hashAlgorithm; + + /// + /// Defines the exceptionFilters + /// private readonly IEnumerable exceptionFilters = new List(); + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The hashAlgorithm + /// The commandProvider + /// The logger + /// The remoteInvokeService + /// The serviceProvider public BreakeRemoteInvokeService(IHashAlgorithm hashAlgorithm, IServiceCommandProvider commandProvider, ILogger logger, IRemoteInvokeService remoteInvokeService, CPlatformContainer serviceProvider) @@ -38,6 +78,18 @@ public BreakeRemoteInvokeService(IHashAlgorithm hashAlgorithm, IServiceCommandPr exceptionFilters = serviceProvider.GetInstances>(); } + #endregion 构造函数 + + #region 方法 + + /// + /// The InvokeAsync + /// + /// The parameters + /// The serviceId + /// The serviceKey + /// The decodeJOject + /// The public async Task InvokeAsync(IDictionary parameters, string serviceId, string serviceKey, bool decodeJOject) { var serviceInvokeInfos = _serviceInvokeListenInfo.GetOrAdd(serviceId, @@ -81,6 +133,53 @@ bool reachErrorThresholdPercentage() => } } + /// + /// The ExecuteExceptionFilter + /// + /// The ex + /// The invokeMessage + /// The token + /// The + private async Task ExecuteExceptionFilter(Exception ex, RemoteInvokeMessage invokeMessage, CancellationToken token) + { + foreach (var filter in exceptionFilters) + { + await filter.ExecuteExceptionFilterAsync(new RpcActionExecutedContext + { + Exception = ex, + InvokeMessage = invokeMessage + }, token); + } + } + + /// + /// The GetHashItem + /// + /// The command + /// The parameters + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private string GetHashItem(ServiceCommand command, IDictionary parameters) + { + string result = ""; + if (command.ShuntStrategy == AddressSelectorMode.HashAlgorithm) + { + var parameter = parameters.Values.FirstOrDefault(); + result = parameter?.ToString(); + } + return result; + } + + /// + /// The MonitorRemoteInvokeAsync + /// + /// The parameters + /// The serviceId + /// The serviceKey + /// The decodeJOject + /// The requestTimeout + /// The item + /// The private async Task MonitorRemoteInvokeAsync(IDictionary parameters, string serviceId, string serviceKey, bool decodeJOject, int requestTimeout, string item) { CancellationTokenSource source = new CancellationTokenSource(); @@ -128,28 +227,6 @@ private async Task MonitorRemoteInvokeAsync(IDictiona } } - private async Task ExecuteExceptionFilter(Exception ex, RemoteInvokeMessage invokeMessage, CancellationToken token) - { - foreach (var filter in exceptionFilters) - { - await filter.ExecuteExceptionFilterAsync(new RpcActionExecutedContext - { - Exception = ex, - InvokeMessage = invokeMessage - }, token); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private string GetHashItem(ServiceCommand command, IDictionary parameters) - { - string result = ""; - if (command.ShuntStrategy == AddressSelectorMode.HashAlgorithm) - { - var parameter = parameters.Values.FirstOrDefault(); - result = parameter?.ToString(); - } - return result; - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Support/Implementation/FailoverHandoverInvoker.cs b/src/Surging.Core/Surging.Core.CPlatform/Support/Implementation/FailoverHandoverInvoker.cs index bbb6dbc5c..119c8f54e 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Support/Implementation/FailoverHandoverInvoker.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Support/Implementation/FailoverHandoverInvoker.cs @@ -6,17 +6,44 @@ namespace Surging.Core.CPlatform.Support.Implementation { - public class FailoverHandoverInvoker: IClusterInvoker + /// + /// Defines the + /// + public class FailoverHandoverInvoker : IClusterInvoker { - #region Field - private readonly IRemoteInvokeService _remoteInvokeService; - private readonly ITypeConvertibleService _typeConvertibleService; + #region 字段 + + /// + /// Defines the _breakeRemoteInvokeService + /// private readonly IBreakeRemoteInvokeService _breakeRemoteInvokeService; + + /// + /// Defines the _commandProvider + /// private readonly IServiceCommandProvider _commandProvider; - #endregion Field - #region Constructor + /// + /// Defines the _remoteInvokeService + /// + private readonly IRemoteInvokeService _remoteInvokeService; + + /// + /// Defines the _typeConvertibleService + /// + private readonly ITypeConvertibleService _typeConvertibleService; + + #endregion 字段 + + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The remoteInvokeService + /// The commandProvider + /// The typeConvertibleService + /// The breakeRemoteInvokeService public FailoverHandoverInvoker(IRemoteInvokeService remoteInvokeService, IServiceCommandProvider commandProvider, ITypeConvertibleService typeConvertibleService, IBreakeRemoteInvokeService breakeRemoteInvokeService) { @@ -26,8 +53,19 @@ public FailoverHandoverInvoker(IRemoteInvokeService remoteInvokeService, IServic _commandProvider = commandProvider; } - #endregion Constructor + #endregion 构造函数 + #region 方法 + + /// + /// The Invoke + /// + /// + /// The parameters + /// The serviceId + /// The _serviceKey + /// The decodeJOject + /// The public async Task Invoke(IDictionary parameters, string serviceId, string _serviceKey, bool decodeJOject) { var time = 0; @@ -44,6 +82,14 @@ public async Task Invoke(IDictionary parameters, string se return result; } + /// + /// The Invoke + /// + /// The parameters + /// The serviceId + /// The _serviceKey + /// The decodeJOject + /// The public async Task Invoke(IDictionary parameters, string serviceId, string _serviceKey, bool decodeJOject) { var time = 0; @@ -51,6 +97,7 @@ public async Task Invoke(IDictionary parameters, string serviceI var command = vtCommand.IsCompletedSuccessfully ? vtCommand.Result : await vtCommand; while (await _breakeRemoteInvokeService.InvokeAsync(parameters, serviceId, _serviceKey, decodeJOject) == null && ++time < command.FailoverCluster) ; } - } -} + #endregion 方法 + } +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Support/Implementation/FailoverInjectionInvoker.cs b/src/Surging.Core/Surging.Core.CPlatform/Support/Implementation/FailoverInjectionInvoker.cs index 4de72fa34..a3d62a412 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Support/Implementation/FailoverInjectionInvoker.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Support/Implementation/FailoverInjectionInvoker.cs @@ -7,12 +7,38 @@ namespace Surging.Core.CPlatform.Support.Implementation { + /// + /// Defines the + /// public class FailoverInjectionInvoker : IClusterInvoker { + #region 字段 + + /// + /// Defines the _serviceCommandProvider + /// public readonly IServiceCommandProvider _serviceCommandProvider; + + /// + /// Defines the _serviceEntryManager + /// public readonly IServiceEntryManager _serviceEntryManager; + + /// + /// Defines the _typeConvertibleService + /// private readonly ITypeConvertibleService _typeConvertibleService; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serviceCommandProvider + /// The serviceEntryManager + /// The typeConvertibleService public FailoverInjectionInvoker(IServiceCommandProvider serviceCommandProvider, IServiceEntryManager serviceEntryManager, ITypeConvertibleService typeConvertibleService) { _serviceCommandProvider = serviceCommandProvider; @@ -20,6 +46,18 @@ public FailoverInjectionInvoker(IServiceCommandProvider serviceCommandProvider, _typeConvertibleService = typeConvertibleService; } + #endregion 构造函数 + + #region 方法 + + /// + /// The Invoke + /// + /// The parameters + /// The serviceId + /// The serviceKey + /// The decodeJOject + /// The public async Task Invoke(IDictionary parameters, string serviceId, string serviceKey, bool decodeJOject) { var vt = _serviceCommandProvider.GetCommand(serviceId); @@ -36,6 +74,15 @@ public async Task Invoke(IDictionary parameters, string serviceI } } + /// + /// The Invoke + /// + /// + /// The parameters + /// The serviceId + /// The serviceKey + /// The decodeJOject + /// The public async Task Invoke(IDictionary parameters, string serviceId, string serviceKey, bool decodeJOject) { var vt = _serviceCommandProvider.GetCommand(serviceId); @@ -67,5 +114,7 @@ public async Task Invoke(IDictionary parameters, string se } return default(T); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Support/Implementation/ServiceCommandBase.cs b/src/Surging.Core/Surging.Core.CPlatform/Support/Implementation/ServiceCommandBase.cs index 0c3ad8ac6..d00ba3b67 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Support/Implementation/ServiceCommandBase.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Support/Implementation/ServiceCommandBase.cs @@ -5,11 +5,35 @@ namespace Surging.Core.CPlatform.Support.Implementation { - public abstract class ServiceCommandBase: IServiceCommandProvider - { + /// + /// Defines the + /// + public abstract class ServiceCommandBase : IServiceCommandProvider + { + #region 字段 + + /// + /// Defines the scripts + /// + internal ConcurrentDictionary scripts = new ConcurrentDictionary(); + + #endregion 字段 + + #region 方法 + + /// + /// The GetCommand + /// + /// The serviceId + /// The public abstract ValueTask GetCommand(string serviceId); - ConcurrentDictionary scripts = new ConcurrentDictionary(); + /// + /// The Run + /// + /// The text + /// The InjectionNamespaces + /// The public async Task Run(string text, params string[] InjectionNamespaces) { object result = scripts; @@ -31,5 +55,7 @@ public async Task Run(string text, params string[] InjectionNamespaces) } return result; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Support/Implementation/ServiceCommandManagerBase.cs b/src/Surging.Core/Surging.Core.CPlatform/Support/Implementation/ServiceCommandManagerBase.cs index 81e720455..29a285b13 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Support/Implementation/ServiceCommandManagerBase.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Support/Implementation/ServiceCommandManagerBase.cs @@ -8,37 +8,63 @@ namespace Surging.Core.CPlatform.Support.Implementation { - /// - /// 服务命令事件参数。 + /// 服务命令变更事件参数。 /// - public class ServiceCommandEventArgs + public class ServiceCommandChangedEventArgs : ServiceCommandEventArgs { - public ServiceCommandEventArgs(ServiceCommandDescriptor serviceCommand) + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serviceCommand + /// The oldServiceCommand + public ServiceCommandChangedEventArgs(ServiceCommandDescriptor serviceCommand, ServiceCommandDescriptor oldServiceCommand) : base(serviceCommand) { - Command = serviceCommand; + OldServiceCommand = oldServiceCommand; } + #endregion 构造函数 + + #region 属性 + /// - /// 服务命令信息。 + /// Gets or sets the OldServiceCommand + /// 旧的服务命令信息。 /// - public ServiceCommandDescriptor Command { get; private set; } + public ServiceCommandDescriptor OldServiceCommand { get; set; } + + #endregion 属性 } /// - /// 服务命令变更事件参数。 + /// 服务命令事件参数。 /// - public class ServiceCommandChangedEventArgs : ServiceCommandEventArgs + public class ServiceCommandEventArgs { - public ServiceCommandChangedEventArgs(ServiceCommandDescriptor serviceCommand, ServiceCommandDescriptor oldServiceCommand) : base(serviceCommand) + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serviceCommand + public ServiceCommandEventArgs(ServiceCommandDescriptor serviceCommand) { - OldServiceCommand = oldServiceCommand; + Command = serviceCommand; } + #endregion 构造函数 + + #region 属性 + /// - /// 旧的服务命令信息。 + /// Gets the Command + /// 服务命令信息。 /// - public ServiceCommandDescriptor OldServiceCommand { get; set; } + public ServiceCommandDescriptor Command { get; private set; } + + #endregion 属性 } public abstract class ServiceCommandManagerBase : IServiceCommandManager @@ -48,14 +74,13 @@ public abstract class ServiceCommandManagerBase : IServiceCommandManager private EventHandler _created; private EventHandler _removed; private EventHandler _changed; + protected ServiceCommandManagerBase(ISerializer serializer, IServiceEntryManager serviceEntryManager) { _serializer = serializer; _serviceEntryManager = serviceEntryManager; } - #region Implementation of IServiceRouteManager - /// /// 服务命令被创建。 /// @@ -91,7 +116,6 @@ public event EventHandler Changed protected abstract Task InitServiceCommandsAsync(IEnumerable routes); - public virtual async Task SetServiceCommandsAsync() { List serviceCommands = new List(); @@ -111,8 +135,6 @@ await Task.Run(() => /// 一个任务。 public abstract Task ClearAsync(); - #endregion Implementation of IServiceRouteManager - /// /// 设置服务命令。 /// @@ -161,7 +183,7 @@ private ServiceCommandDescriptor ConvertServiceCommand(string serviceId, Command Injection = command.Injection, RequestCacheEnabled = command.RequestCacheEnabled, Strategy = command.Strategy, - ShuntStrategy=command.ShuntStrategy, + ShuntStrategy = command.ShuntStrategy, InjectionNamespaces = command.InjectionNamespaces, BreakeErrorThresholdPercentage = command.BreakeErrorThresholdPercentage, BreakerForceClosed = command.BreakerForceClosed, diff --git a/src/Surging.Core/Surging.Core.CPlatform/Support/Implementation/ServiceCommandProvider.cs b/src/Surging.Core/Surging.Core.CPlatform/Support/Implementation/ServiceCommandProvider.cs index b0e9ef9ae..f347fac6f 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Support/Implementation/ServiceCommandProvider.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Support/Implementation/ServiceCommandProvider.cs @@ -1,20 +1,45 @@ -using Surging.Core.CPlatform.Runtime.Server; +using Microsoft.Extensions.DependencyInjection; +using Surging.Core.CPlatform.Runtime.Server; using Surging.Core.CPlatform.Support.Attributes; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -using Microsoft.Extensions.DependencyInjection; using System.Threading.Tasks; namespace Surging.Core.CPlatform.Support.Implementation { + /// + /// Defines the + /// public class ServiceCommandProvider : ServiceCommandBase { + #region 字段 + + /// + /// Defines the _serviceCommand + /// + private readonly ConcurrentDictionary _serviceCommand = new ConcurrentDictionary(); + + /// + /// Defines the _serviceEntryManager + /// private readonly IServiceEntryManager _serviceEntryManager; + + /// + /// Defines the _serviceProvider + /// private readonly IServiceProvider _serviceProvider; - private readonly ConcurrentDictionary _serviceCommand = new ConcurrentDictionary(); + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serviceEntryManager + /// The serviceProvider public ServiceCommandProvider(IServiceEntryManager serviceEntryManager, IServiceProvider serviceProvider) { _serviceEntryManager = serviceEntryManager; @@ -28,52 +53,15 @@ public ServiceCommandProvider(IServiceEntryManager serviceEntryManager, IService } } - public override async ValueTask GetCommand(string serviceId) - { - var result = _serviceCommand.GetValueOrDefault(serviceId); - if (result == null) - { - var task = GetCommandAsync(serviceId); - return task.IsCompletedSuccessfully ? task.Result : await task; - } - else - { - return result; - } - } - - public async Task GetCommandAsync(string serviceId) - { - var result = new ServiceCommand(); - var manager = _serviceProvider.GetService(); - if (manager == null) - { - var command = (from q in _serviceEntryManager.GetEntries() - let k = q.Attributes - where k.OfType().Count() > 0 && q.Descriptor.Id == serviceId - select k.OfType().FirstOrDefault()).FirstOrDefault(); - result = ConvertServiceCommand(command); - } - else - { - var commands = await manager.GetServiceCommandsAsync(); - result = ConvertServiceCommand(commands.Where(p => p.ServiceId == serviceId).FirstOrDefault()); - } - _serviceCommand.AddOrUpdate(serviceId, result, (s, r) => result); - return result; - } + #endregion 构造函数 - private void ServiceCommandManager_Removed(object sender, ServiceCommandEventArgs e) - { - ServiceCommand value; - _serviceCommand.TryRemove(e.Command.ServiceId, out value); - } - - public void ServiceCommandManager_Add(object sender, ServiceCommandEventArgs e) - { - _serviceCommand.GetOrAdd(e.Command.ServiceId, e.Command); - } + #region 方法 + /// + /// The ConvertServiceCommand + /// + /// The command + /// The public ServiceCommand ConvertServiceCommand(CommandAttribute command) { var result = new ServiceCommand(); @@ -99,6 +87,11 @@ public ServiceCommand ConvertServiceCommand(CommandAttribute command) return result; } + /// + /// The ConvertServiceCommand + /// + /// The command + /// The public ServiceCommand ConvertServiceCommand(ServiceCommandDescriptor command) { var result = new ServiceCommand(); @@ -123,5 +116,73 @@ public ServiceCommand ConvertServiceCommand(ServiceCommandDescriptor command) } return result; } + + /// + /// The GetCommand + /// + /// The serviceId + /// The + public override async ValueTask GetCommand(string serviceId) + { + var result = _serviceCommand.GetValueOrDefault(serviceId); + if (result == null) + { + var task = GetCommandAsync(serviceId); + return task.IsCompletedSuccessfully ? task.Result : await task; + } + else + { + return result; + } + } + + /// + /// The GetCommandAsync + /// + /// The serviceId + /// The + public async Task GetCommandAsync(string serviceId) + { + var result = new ServiceCommand(); + var manager = _serviceProvider.GetService(); + if (manager == null) + { + var command = (from q in _serviceEntryManager.GetEntries() + let k = q.Attributes + where k.OfType().Count() > 0 && q.Descriptor.Id == serviceId + select k.OfType().FirstOrDefault()).FirstOrDefault(); + result = ConvertServiceCommand(command); + } + else + { + var commands = await manager.GetServiceCommandsAsync(); + result = ConvertServiceCommand(commands.Where(p => p.ServiceId == serviceId).FirstOrDefault()); + } + _serviceCommand.AddOrUpdate(serviceId, result, (s, r) => result); + return result; + } + + /// + /// The ServiceCommandManager_Add + /// + /// The sender + /// The e + public void ServiceCommandManager_Add(object sender, ServiceCommandEventArgs e) + { + _serviceCommand.GetOrAdd(e.Command.ServiceId, e.Command); + } + + /// + /// The ServiceCommandManager_Removed + /// + /// The sender + /// The e + private void ServiceCommandManager_Removed(object sender, ServiceCommandEventArgs e) + { + ServiceCommand value; + _serviceCommand.TryRemove(e.Command.ServiceId, out value); + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Support/ServiceCommand.cs b/src/Surging.Core/Surging.Core.CPlatform/Support/ServiceCommand.cs index 8f4a680d6..f7696504b 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Support/ServiceCommand.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Support/ServiceCommand.cs @@ -4,8 +4,16 @@ namespace Surging.Core.CPlatform.Support { + /// + /// Defines the + /// public class ServiceCommand { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public ServiceCommand() { if (AppConfig.ServerOptions != null) @@ -25,67 +33,97 @@ public ServiceCommand() FallBackName = AppConfig.ServerOptions.FallBackName; } } + + #endregion 构造函数 + + #region 属性 + /// - /// 故障转移次数 + /// Gets or sets the BreakeErrorThresholdPercentage + /// 错误率达到多少开启熔断保护 /// - public int FailoverCluster { get; set; } = 3; + public int BreakeErrorThresholdPercentage { get; set; } = 50; + /// - /// 是否强制开启熔断 + /// Gets or sets a value indicating whether BreakerForceClosed + /// 是否强制关闭熔断 /// - public bool CircuitBreakerForceOpen { get; set; } + public bool BreakerForceClosed { get; set; } + /// - /// 容错策略 + /// Gets or sets the BreakerRequestVolumeThreshold + /// 10秒钟内至少多少请求失败,熔断器才发挥起作用 /// - [JsonConverter(typeof(StringEnumConverter))] - public StrategyType Strategy { get; set; } + public int BreakerRequestVolumeThreshold { get; set; } = 20; + /// - /// 执行超时时间 + /// Gets or sets the BreakeSleepWindowInMilliseconds + /// 熔断多少毫秒后去尝试请求 /// - public int ExecutionTimeoutInMilliseconds { get; set; } = 1000; + public int BreakeSleepWindowInMilliseconds { get; set; } = 60000; + /// - /// 是否开启缓存 + /// Gets or sets a value indicating whether CircuitBreakerForceOpen + /// 是否强制开启熔断 /// - public bool RequestCacheEnabled { get; set; } + public bool CircuitBreakerForceOpen { get; set; } + /// - /// 注入 + /// Gets or sets the ExecutionTimeoutInMilliseconds + /// 执行超时时间 /// - public string Injection { get; set; } = "return null"; - + public int ExecutionTimeoutInMilliseconds { get; set; } = 1000; + /// + /// Gets or sets the FailoverCluster + /// 故障转移次数 + /// + public int FailoverCluster { get; set; } = 3; + + /// + /// Gets or sets the FallBackName /// IFallbackInvoker 实例名称 /// public string FallBackName { get; set; } + /// - /// 负载分流策略 + /// Gets or sets the Injection + /// 注入 /// - [JsonConverter(typeof(StringEnumConverter))] - public AddressSelectorMode ShuntStrategy { get; set; } = AddressSelectorMode.Polling; + public string Injection { get; set; } = "return null"; + /// + /// Gets or sets the InjectionNamespaces /// 注入命名空间 /// public string[] InjectionNamespaces { get; set; } + /// - /// 错误率达到多少开启熔断保护 - /// - public int BreakeErrorThresholdPercentage { get; set; } = 50; - /// - /// 熔断多少毫秒后去尝试请求 + /// Gets or sets the MaxConcurrentRequests + /// 信号量最大并发度 /// - public int BreakeSleepWindowInMilliseconds { get; set; } = 60000; + public int MaxConcurrentRequests { get; set; } = 200; + /// - /// 是否强制关闭熔断 + /// Gets or sets a value indicating whether RequestCacheEnabled + /// 是否开启缓存 /// - public bool BreakerForceClosed { get; set; } + public bool RequestCacheEnabled { get; set; } /// - /// 10秒钟内至少多少请求失败,熔断器才发挥起作用 + /// Gets or sets the ShuntStrategy + /// 负载分流策略 /// - public int BreakerRequestVolumeThreshold { get; set; } = 20; + [JsonConverter(typeof(StringEnumConverter))] + public AddressSelectorMode ShuntStrategy { get; set; } = AddressSelectorMode.Polling; /// - /// 信号量最大并发度 + /// Gets or sets the Strategy + /// 容错策略 /// - public int MaxConcurrentRequests { get; set; } = 200; + [JsonConverter(typeof(StringEnumConverter))] + public StrategyType Strategy { get; set; } + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Support/ServiceCommandDescriptor.cs b/src/Surging.Core/Surging.Core.CPlatform/Support/ServiceCommandDescriptor.cs index da42ed5cb..eecd48eaf 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Support/ServiceCommandDescriptor.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Support/ServiceCommandDescriptor.cs @@ -1,7 +1,17 @@ namespace Surging.Core.CPlatform.Support { - public class ServiceCommandDescriptor:ServiceCommand + /// + /// Defines the + /// + public class ServiceCommandDescriptor : ServiceCommand { + #region 属性 + + /// + /// Gets or sets the ServiceId + /// public string ServiceId { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Support/ServiceInvokeListenInfo.cs b/src/Surging.Core/Surging.Core.CPlatform/Support/ServiceInvokeListenInfo.cs index c4a984adc..fac60b647 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Support/ServiceInvokeListenInfo.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Support/ServiceInvokeListenInfo.cs @@ -2,40 +2,55 @@ namespace Surging.Core.CPlatform.Support { + /// + /// Defines the + /// public class ServiceInvokeListenInfo { + #region 属性 /// - /// 远程调用请求数 + /// Gets or sets the ConcurrentRequests + /// 并发数 /// - public int? RemoteServiceRequests { get; set; } + public int ConcurrentRequests { get; set; } /// - /// 本地调用请求数 + /// Gets or sets the FaultRemoteServiceRequests + /// 失败调用请求数 /// - public int LocalServiceRequests { get; set; } + public int FaultRemoteServiceRequests { get; set; } + + /// + /// Gets or sets the FinalRemoteInvokeTime + /// 最后一次远程调用时间 + /// + public DateTime FinalRemoteInvokeTime { get; set; } /// + /// Gets or sets the FirstInvokeTime /// 首次调用时间 /// public DateTime FirstInvokeTime { get; set; } + /// - /// 最后一次远程调用时间 + /// Gets or sets the LocalServiceRequests + /// 本地调用请求数 /// - public DateTime FinalRemoteInvokeTime { get; set; } + public int LocalServiceRequests { get; set; } + /// - /// 失败调用请求数 + /// Gets or sets the RemoteServiceRequests + /// 远程调用请求数 /// - public int FaultRemoteServiceRequests { get; set; } + public int? RemoteServiceRequests { get; set; } /// + /// Gets or sets the SinceFaultRemoteServiceRequests /// 距上次失败调用次数 /// public int SinceFaultRemoteServiceRequests { get; set; } - /// - /// 并发数 - /// - public int ConcurrentRequests { get; set; } + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Support/StrategyType.cs b/src/Surging.Core/Surging.Core.CPlatform/Support/StrategyType.cs index 183ecfc93..2ccd155eb 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Support/StrategyType.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Support/StrategyType.cs @@ -1,5 +1,7 @@ namespace Surging.Core.CPlatform.Support { + #region 枚举 + /// /// 容错策略 /// @@ -9,13 +11,17 @@ public enum StrategyType /// 故障转移策略、失败切换远程服务机制 /// Failover = 0, + /// /// 脚本注入策略、失败执行注入脚本 /// Injection = 1, + /// /// 回退策略、失败时调用通过FallBackName指定的接口 /// FallBack = 2, } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Transport/Codec/ITransportMessageCodecFactory.cs b/src/Surging.Core/Surging.Core.CPlatform/Transport/Codec/ITransportMessageCodecFactory.cs index ce74c0a5d..605e12250 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Transport/Codec/ITransportMessageCodecFactory.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Transport/Codec/ITransportMessageCodecFactory.cs @@ -1,20 +1,28 @@ namespace Surging.Core.CPlatform.Transport.Codec { + #region 接口 + /// /// 一个抽象的传输消息编解码器工厂。 /// public interface ITransportMessageCodecFactory { - /// - /// 获取编码器。 - /// - /// 编码器实例。 - ITransportMessageEncoder GetEncoder(); + #region 方法 /// /// 获取解码器。 /// /// 解码器实例。 ITransportMessageDecoder GetDecoder(); + + /// + /// 获取编码器。 + /// + /// 编码器实例。 + ITransportMessageEncoder GetEncoder(); + + #endregion 方法 } + + #endregion 接口 } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Transport/Codec/ITransportMessageDecoder.cs b/src/Surging.Core/Surging.Core.CPlatform/Transport/Codec/ITransportMessageDecoder.cs index 9654db435..8415680b6 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Transport/Codec/ITransportMessageDecoder.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Transport/Codec/ITransportMessageDecoder.cs @@ -2,8 +2,24 @@ namespace Surging.Core.CPlatform.Transport.Codec { + #region 接口 + + /// + /// Defines the + /// public interface ITransportMessageDecoder { + #region 方法 + + /// + /// The Decode + /// + /// The data + /// The TransportMessage Decode(byte[] data); + + #endregion 方法 } + + #endregion 接口 } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Transport/Codec/ITransportMessageEncoder.cs b/src/Surging.Core/Surging.Core.CPlatform/Transport/Codec/ITransportMessageEncoder.cs index 1849a88a8..6618716c1 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Transport/Codec/ITransportMessageEncoder.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Transport/Codec/ITransportMessageEncoder.cs @@ -2,8 +2,24 @@ namespace Surging.Core.CPlatform.Transport.Codec { + #region 接口 + + /// + /// Defines the + /// public interface ITransportMessageEncoder { + #region 方法 + + /// + /// The Encode + /// + /// The message + /// The byte[] Encode(TransportMessage message); + + #endregion 方法 } + + #endregion 接口 } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Transport/Codec/Implementation/JsonTransportMessageCodecFactory.cs b/src/Surging.Core/Surging.Core.CPlatform/Transport/Codec/Implementation/JsonTransportMessageCodecFactory.cs index 7b1a066f1..186275474 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Transport/Codec/Implementation/JsonTransportMessageCodecFactory.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Transport/Codec/Implementation/JsonTransportMessageCodecFactory.cs @@ -2,22 +2,26 @@ namespace Surging.Core.CPlatform.Transport.Codec.Implementation { + /// + /// Defines the + /// public class JsonTransportMessageCodecFactory : ITransportMessageCodecFactory { - #region Field - private readonly ITransportMessageEncoder _transportMessageEncoder = new JsonTransportMessageEncoder(); + #region 字段 + + /// + /// Defines the _transportMessageDecoder + /// private readonly ITransportMessageDecoder _transportMessageDecoder = new JsonTransportMessageDecoder(); - #endregion Field - #region Implementation of ITransportMessageCodecFactory /// - /// 获取编码器。 + /// Defines the _transportMessageEncoder /// - /// 编码器实例。 - public ITransportMessageEncoder GetEncoder() - { - return _transportMessageEncoder; - } + private readonly ITransportMessageEncoder _transportMessageEncoder = new JsonTransportMessageEncoder(); + + #endregion 字段 + + #region 方法 /// /// 获取解码器。 @@ -28,6 +32,15 @@ public ITransportMessageDecoder GetDecoder() return _transportMessageDecoder; } - #endregion Implementation of ITransportMessageCodecFactory + /// + /// 获取编码器。 + /// + /// 编码器实例。 + public ITransportMessageEncoder GetEncoder() + { + return _transportMessageEncoder; + } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Transport/Codec/Implementation/JsonTransportMessageDecoder.cs b/src/Surging.Core/Surging.Core.CPlatform/Transport/Codec/Implementation/JsonTransportMessageDecoder.cs index 8044713cc..b84304444 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Transport/Codec/Implementation/JsonTransportMessageDecoder.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Transport/Codec/Implementation/JsonTransportMessageDecoder.cs @@ -4,10 +4,18 @@ namespace Surging.Core.CPlatform.Transport.Codec.Implementation { + /// + /// Defines the + /// public sealed class JsonTransportMessageDecoder : ITransportMessageDecoder { - #region Implementation of ITransportMessageDecoder + #region 方法 + /// + /// The Decode + /// + /// The data + /// The public TransportMessage Decode(byte[] data) { var content = Encoding.UTF8.GetString(data); @@ -23,6 +31,6 @@ public TransportMessage Decode(byte[] data) return message; } - #endregion Implementation of ITransportMessageDecoder + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Transport/Codec/Implementation/JsonTransportMessageEncoder.cs b/src/Surging.Core/Surging.Core.CPlatform/Transport/Codec/Implementation/JsonTransportMessageEncoder.cs index cf2ba83cc..031bec5d5 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Transport/Codec/Implementation/JsonTransportMessageEncoder.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Transport/Codec/Implementation/JsonTransportMessageEncoder.cs @@ -4,16 +4,24 @@ namespace Surging.Core.CPlatform.Transport.Codec.Implementation { + /// + /// Defines the + /// public sealed class JsonTransportMessageEncoder : ITransportMessageEncoder { - #region Implementation of ITransportMessageEncoder + #region 方法 + /// + /// The Encode + /// + /// The message + /// The public byte[] Encode(TransportMessage message) { var content = JsonConvert.SerializeObject(message); return Encoding.UTF8.GetBytes(content); } - #endregion Implementation of ITransportMessageEncoder + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Transport/IMessageListener.cs b/src/Surging.Core/Surging.Core.CPlatform/Transport/IMessageListener.cs index 6f4e146c0..da8fdcb5c 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Transport/IMessageListener.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Transport/IMessageListener.cs @@ -3,6 +3,8 @@ namespace Surging.Core.CPlatform.Transport { + #region 委托 + /// /// 接受到消息的委托。 /// @@ -10,16 +12,26 @@ namespace Surging.Core.CPlatform.Transport /// 接收到的消息。 public delegate Task ReceivedDelegate(IMessageSender sender, TransportMessage message); + #endregion 委托 + + #region 接口 + /// /// 一个抽象的消息监听者。 /// public interface IMessageListener { + #region 事件 + /// /// 接收到消息的事件。 /// event ReceivedDelegate Received; - + + #endregion 事件 + + #region 方法 + /// /// 触发接收到消息事件。 /// @@ -27,5 +39,9 @@ public interface IMessageListener /// 接收到的消息。 /// 一个任务。 Task OnReceived(IMessageSender sender, TransportMessage message); + + #endregion 方法 } + + #endregion 接口 } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Transport/IMessageSender.cs b/src/Surging.Core/Surging.Core.CPlatform/Transport/IMessageSender.cs index 23425e133..66eb3282b 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Transport/IMessageSender.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Transport/IMessageSender.cs @@ -3,23 +3,31 @@ namespace Surging.Core.CPlatform.Transport { + #region 接口 + /// /// 一个抽象的发送者。 /// public interface IMessageSender { + #region 方法 + /// - /// 发送消息。 + /// 发送消息并清空缓冲区。 /// /// 消息内容。 /// 一个任务。 - Task SendAsync(TransportMessage message); + Task SendAndFlushAsync(TransportMessage message); /// - /// 发送消息并清空缓冲区。 + /// 发送消息。 /// /// 消息内容。 /// 一个任务。 - Task SendAndFlushAsync(TransportMessage message); + Task SendAsync(TransportMessage message); + + #endregion 方法 } + + #endregion 接口 } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Transport/ITransportClient.cs b/src/Surging.Core/Surging.Core.CPlatform/Transport/ITransportClient.cs index 4149800cf..39251a60a 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Transport/ITransportClient.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Transport/ITransportClient.cs @@ -4,16 +4,25 @@ namespace Surging.Core.CPlatform.Transport { + #region 接口 + /// /// 一个抽象的传输客户端。 /// public interface ITransportClient { + #region 方法 + /// /// 发送消息。 /// /// 远程调用消息模型。 + /// The cancellationToken /// 远程调用消息的传输消息。 Task SendAsync(RemoteInvokeMessage message, CancellationToken cancellationToken); + + #endregion 方法 } + + #endregion 接口 } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Transport/ITransportClientFactory.cs b/src/Surging.Core/Surging.Core.CPlatform/Transport/ITransportClientFactory.cs index b29766e79..4fb316dd9 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Transport/ITransportClientFactory.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Transport/ITransportClientFactory.cs @@ -3,16 +3,24 @@ namespace Surging.Core.CPlatform.Transport { + #region 接口 + /// /// 一个抽象的传输客户端工厂。 /// public interface ITransportClientFactory { + #region 方法 + /// /// 创建客户端。 /// /// 终结点。 /// 传输客户端实例。 Task CreateClientAsync(EndPoint endPoint); + + #endregion 方法 } + + #endregion 接口 } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Transport/Implementation/MessageListener.cs b/src/Surging.Core/Surging.Core.CPlatform/Transport/Implementation/MessageListener.cs index 227a5537e..a45d61f0a 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Transport/Implementation/MessageListener.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Transport/Implementation/MessageListener.cs @@ -8,13 +8,17 @@ namespace Surging.Core.CPlatform.Transport.Implementation /// public class MessageListener : IMessageListener { - #region Implementation of IMessageListener + #region 事件 /// /// 接收到消息的事件。 /// public event ReceivedDelegate Received; + #endregion 事件 + + #region 方法 + /// /// 触发接收到消息事件。 /// @@ -28,6 +32,6 @@ public async Task OnReceived(IMessageSender sender, TransportMessage message) await Received(sender, message); } - #endregion Implementation of IMessageListener + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Transport/Implementation/RpcContext.cs b/src/Surging.Core/Surging.Core.CPlatform/Transport/Implementation/RpcContext.cs index d63788459..6098efbe7 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Transport/Implementation/RpcContext.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Transport/Implementation/RpcContext.cs @@ -5,22 +5,65 @@ namespace Surging.Core.CPlatform.Transport.Implementation { + /// + /// Defines the + /// public class RpcContext { + #region 字段 + + /// + /// Defines the rpcContextThreadLocal + /// + private static ThreadLocal rpcContextThreadLocal = new ThreadLocal(() => + { + RpcContext context = new RpcContext(); + context.SetContextParameters(new ConcurrentDictionary()); + return context; + }); + + /// + /// Defines the contextParameters + /// private ConcurrentDictionary contextParameters; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ConcurrentDictionary GetContextParameters() + #endregion 字段 + + #region 构造函数 + + /// + /// Prevents a default instance of the class from being created. + /// + private RpcContext() { - return contextParameters; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetAttachment(string key, object value) + #endregion 构造函数 + + #region 方法 + + /// + /// The GetContext + /// + /// The + public static RpcContext GetContext() { - contextParameters.AddOrUpdate(key, value, (k, v) => value); + return rpcContextThreadLocal.Value; + } + + /// + /// The RemoveContext + /// + public static void RemoveContext() + { + rpcContextThreadLocal.Dispose(); } + /// + /// The GetAttachment + /// + /// The key + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] public object GetAttachment(string key) { @@ -28,31 +71,37 @@ public object GetAttachment(string key) return result; } + /// + /// The GetContextParameters + /// + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetContextParameters(ConcurrentDictionary contextParameters) + public ConcurrentDictionary GetContextParameters() { - this.contextParameters = contextParameters; + return contextParameters; } - private static ThreadLocal rpcContextThreadLocal = new ThreadLocal(() => - { - RpcContext context = new RpcContext(); - context.SetContextParameters(new ConcurrentDictionary()); - return context; - }); - - public static RpcContext GetContext() + /// + /// The SetAttachment + /// + /// The key + /// The value + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetAttachment(string key, object value) { - return rpcContextThreadLocal.Value; + contextParameters.AddOrUpdate(key, value, (k, v) => value); } - public static void RemoveContext() + /// + /// The SetContextParameters + /// + /// The contextParameters + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetContextParameters(ConcurrentDictionary contextParameters) { - rpcContextThreadLocal.Dispose(); + this.contextParameters = contextParameters; } - private RpcContext() - { - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Transport/Implementation/TransportClient.cs b/src/Surging.Core/Surging.Core.CPlatform/Transport/Implementation/TransportClient.cs index 09bb877fa..ece136b30 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Transport/Implementation/TransportClient.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Transport/Implementation/TransportClient.cs @@ -16,20 +16,45 @@ namespace Surging.Core.CPlatform.Transport.Implementation /// public class TransportClient : ITransportClient, IDisposable { - #region Field + #region 字段 - private readonly IMessageSender _messageSender; - private readonly IMessageListener _messageListener; + /// + /// Defines the _logger + /// private readonly ILogger _logger; - private readonly IServiceExecutor _serviceExecutor; + /// + /// Defines the _messageListener + /// + private readonly IMessageListener _messageListener; + + /// + /// Defines the _messageSender + /// + private readonly IMessageSender _messageSender; + + /// + /// Defines the _resultDictionary + /// private readonly ConcurrentDictionary> _resultDictionary = new ConcurrentDictionary>(); - #endregion Field + /// + /// Defines the _serviceExecutor + /// + private readonly IServiceExecutor _serviceExecutor; + + #endregion 字段 - #region Constructor + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The messageSender + /// The messageListener + /// The logger + /// The serviceExecutor public TransportClient(IMessageSender messageSender, IMessageListener messageListener, ILogger logger, IServiceExecutor serviceExecutor) { @@ -40,14 +65,28 @@ public TransportClient(IMessageSender messageSender, IMessageListener messageLis messageListener.Received += MessageListener_Received; } - #endregion Constructor + #endregion 构造函数 - #region Implementation of ITransportClient + #region 方法 + + /// + /// The Dispose + /// + public void Dispose() + { + (_messageSender as IDisposable)?.Dispose(); + (_messageListener as IDisposable)?.Dispose(); + foreach (var taskCompletionSource in _resultDictionary.Values) + { + taskCompletionSource.SetCanceled(); + } + } /// /// 发送消息。 /// /// 远程调用消息模型。 + /// The cancellationToken /// 远程调用消息的传输消息。 [MethodImpl(MethodImplOptions.AggressiveInlining)] public async Task SendAsync(RemoteInvokeMessage message, CancellationToken cancellationToken) @@ -60,7 +99,7 @@ public async Task SendAsync(RemoteInvokeMessage messa var transportMessage = TransportMessage.CreateInvokeMessage(message); //注册结果回调 - var callbackTask = RegisterResultCallbackAsync(transportMessage.Id,cancellationToken); + var callbackTask = RegisterResultCallbackAsync(transportMessage.Id, cancellationToken); try { @@ -85,29 +124,42 @@ public async Task SendAsync(RemoteInvokeMessage messa } } - #endregion Implementation of ITransportClient + /// + /// The MessageListener_Received + /// + /// The sender + /// The message + /// The + private async Task MessageListener_Received(IMessageSender sender, TransportMessage message) + { + if (_logger.IsEnabled(LogLevel.Trace)) + _logger.LogTrace("服务消费者接收到消息。"); - #region Implementation of IDisposable + ManualResetValueTaskSource task; + if (!_resultDictionary.TryGetValue(message.Id, out task)) + return; - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - public void Dispose() - { - (_messageSender as IDisposable)?.Dispose(); - (_messageListener as IDisposable)?.Dispose(); - foreach (var taskCompletionSource in _resultDictionary.Values) + if (message.IsInvokeResultMessage()) { - taskCompletionSource.SetCanceled(); + var content = message.GetContent(); + if (!string.IsNullOrEmpty(content.ExceptionMessage)) + { + task.SetException(new CPlatformCommunicationException(content.ExceptionMessage, content.StatusCode)); + } + else + { + task.SetResult(message); + } } + if (_serviceExecutor != null && message.IsInvokeMessage()) + await _serviceExecutor.ExecuteAsync(sender, message); } - #endregion Implementation of IDisposable - - #region Private Method - /// /// 注册指定消息的回调任务。 /// /// 消息Id。 + /// The cancellationToken /// 远程调用结果消息模型。 [MethodImpl(MethodImplOptions.AggressiveInlining)] private async Task RegisterResultCallbackAsync(string id, CancellationToken cancellationToken) @@ -131,31 +183,6 @@ private async Task RegisterResultCallbackAsync(string } } - private async Task MessageListener_Received(IMessageSender sender, TransportMessage message) - { - if (_logger.IsEnabled(LogLevel.Trace)) - _logger.LogTrace("服务消费者接收到消息。"); - - ManualResetValueTaskSource task; - if (!_resultDictionary.TryGetValue(message.Id, out task)) - return; - - if (message.IsInvokeResultMessage()) - { - var content = message.GetContent(); - if (!string.IsNullOrEmpty(content.ExceptionMessage)) - { - task.SetException(new CPlatformCommunicationException(content.ExceptionMessage,content.StatusCode)); - } - else - { - task.SetResult(message); - } - } - if (_serviceExecutor != null && message.IsInvokeMessage()) - await _serviceExecutor.ExecuteAsync(sender, message); - } - - #endregion Private Method + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Utilities/CancellationTokenExtensions.cs b/src/Surging.Core/Surging.Core.CPlatform/Utilities/CancellationTokenExtensions.cs index 01d4b063a..a3bb7ecbb 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Utilities/CancellationTokenExtensions.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Utilities/CancellationTokenExtensions.cs @@ -6,8 +6,18 @@ namespace Surging.Core.CPlatform.Utilities { + /// + /// Defines the + /// public static class CancellationTokenExtensions { + #region 方法 + + /// + /// The WhenCanceled + /// + /// The cancellationToken + /// The public static Task WhenCanceled(this CancellationToken cancellationToken) { var tcs = new TaskCompletionSource(); @@ -15,6 +25,13 @@ public static Task WhenCanceled(this CancellationToken cancellationToken) return tcs.Task; } + /// + /// The WithCancellation + /// + /// + /// The task + /// The cancellationToken + /// The public static async Task WithCancellation( this Task task, CancellationToken cancellationToken) { @@ -26,6 +43,14 @@ public static async Task WithCancellation( return await task; } + /// + /// The WithCancellation + /// + /// + /// The task + /// The cts + /// The requestTimeout + /// The public static async Task WithCancellation( this Task task, CancellationTokenSource cts, int requestTimeout) { @@ -36,5 +61,7 @@ public static async Task WithCancellation( } throw new TimeoutException(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Utilities/Check.cs b/src/Surging.Core/Surging.Core.CPlatform/Utilities/Check.cs index ccb5fcb00..224d4ee89 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Utilities/Check.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Utilities/Check.cs @@ -2,29 +2,46 @@ namespace Surging.Core.CPlatform.Utilities { - + /// + /// Defines the + /// public sealed class Check { - public static T NotNull(T value, string parameterName) where T : class + #region 方法 + + /// + /// The CheckCondition + /// + /// The condition + /// The formatErrorText + /// The parameters + public static void CheckCondition(Func condition, string formatErrorText, params string[] parameters) { - if (value == null) + if (condition.Invoke()) { - throw new ArgumentNullException(parameterName); + throw new ArgumentException(string.Format(CPlatformResource.ArgumentIsNullOrWhitespace, parameters)); } - - return value; } - public static T? NotNull(T? value, string parameterName) where T : struct + /// + /// The CheckCondition + /// + /// The condition + /// The parameterName + public static void CheckCondition(Func condition, string parameterName) { - if (value == null) + if (condition.Invoke()) { - throw new ArgumentNullException(parameterName); + throw new ArgumentException(string.Format(CPlatformResource.ArgumentIsNullOrWhitespace, parameterName)); } - - return value; } + /// + /// The NotEmpty + /// + /// The value + /// The parameterName + /// The public static string NotEmpty(string value, string parameterName) { if (string.IsNullOrWhiteSpace(value)) @@ -35,20 +52,40 @@ public static string NotEmpty(string value, string parameterName) return value; } - public static void CheckCondition(Func condition, string parameterName) + /// + /// The NotNull + /// + /// + /// The value + /// The parameterName + /// The + public static T NotNull(T value, string parameterName) where T : class { - if (condition.Invoke()) + if (value == null) { - throw new ArgumentException(string.Format(CPlatformResource.ArgumentIsNullOrWhitespace, parameterName)); + throw new ArgumentNullException(parameterName); } + + return value; } - public static void CheckCondition(Func condition, string formatErrorText, params string[] parameters) + /// + /// The NotNull + /// + /// + /// The value + /// The parameterName + /// The + public static T? NotNull(T? value, string parameterName) where T : struct { - if (condition.Invoke()) + if (value == null) { - throw new ArgumentException(string.Format(CPlatformResource.ArgumentIsNullOrWhitespace, parameters)); + throw new ArgumentNullException(parameterName); } + + return value; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Utilities/DebugCheck.cs b/src/Surging.Core/Surging.Core.CPlatform/Utilities/DebugCheck.cs index 9e95b516e..59f0354d7 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Utilities/DebugCheck.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Utilities/DebugCheck.cs @@ -2,24 +2,45 @@ namespace Surging.Core.CPlatform.Utilities { + /// + /// Defines the + /// public sealed class DebugCheck { + #region 方法 + + /// + /// The NotEmpty + /// + /// The value [Conditional("DEBUG")] - public static void NotNull(T value) where T : class + public static void NotEmpty(string value) { - Debug.Assert(value != null); + Debug.Assert(!string.IsNullOrWhiteSpace(value)); } + /// + /// The NotNull + /// + /// + /// The value [Conditional("DEBUG")] - public static void NotNull(T? value) where T : struct + public static void NotNull(T value) where T : class { Debug.Assert(value != null); } + /// + /// The NotNull + /// + /// + /// The value [Conditional("DEBUG")] - public static void NotEmpty(string value) + public static void NotNull(T? value) where T : struct { - Debug.Assert(!string.IsNullOrWhiteSpace(value)); + Debug.Assert(value != null); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Utilities/EnvironmentHelper.cs b/src/Surging.Core/Surging.Core.CPlatform/Utilities/EnvironmentHelper.cs index 5f0e3d7ba..f96c65ca9 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Utilities/EnvironmentHelper.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Utilities/EnvironmentHelper.cs @@ -6,8 +6,18 @@ namespace Surging.Core.CPlatform.Utilities { + /// + /// Defines the + /// public class EnvironmentHelper { + #region 方法 + + /// + /// The GetEnvironmentVariable + /// + /// The value + /// The public static string GetEnvironmentVariable(string value) { var result = value; @@ -25,6 +35,12 @@ public static string GetEnvironmentVariable(string value) return result; } + /// + /// The GetEnvironmentVariableAsBool + /// + /// The name + /// The defaultValue + /// The public static bool GetEnvironmentVariableAsBool(string name, bool defaultValue = false) { var str = Environment.GetEnvironmentVariable(name); @@ -39,15 +55,22 @@ public static bool GetEnvironmentVariableAsBool(string name, bool defaultValue = case "1": case "yes": return true; + case "false": case "0": case "no": return false; + default: return defaultValue; } } + /// + /// The GetParameters + /// + /// The text + /// The private static List GetParameters(string text) { var matchVale = new List(); @@ -59,5 +82,7 @@ private static List GetParameters(string text) } return matchVale; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Utilities/FastInvoke.cs b/src/Surging.Core/Surging.Core.CPlatform/Utilities/FastInvoke.cs index 2ffa8a14d..d780b4b9b 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Utilities/FastInvoke.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Utilities/FastInvoke.cs @@ -6,18 +6,30 @@ namespace Surging.Core.CPlatform.Utilities { + /// + /// Defines the + /// public class FastInvoke { + #region 委托 + + /// + /// The FastInvokeHandler + /// + /// The target + /// The paramters + /// The public delegate object FastInvokeHandler(object target, object[] paramters); + #endregion 委托 - static object InvokeMethod(FastInvokeHandler invoke, object target, params object[] paramters) - { - - return invoke(null, paramters); - - } + #region 方法 + /// + /// The GetMethodInvoker + /// + /// The methodInfo + /// The public static FastInvokeHandler GetMethodInvoker(MethodInfo methodInfo) { DynamicMethod dynamicMethod = new DynamicMethod(string.Empty, typeof(object), new Type[] { typeof(object), typeof(object[]) }, methodInfo.DeclaringType.Module); @@ -83,26 +95,53 @@ public static FastInvokeHandler GetMethodInvoker(MethodInfo methodInfo) return invoder; } - private static void EmitCastToReference(ILGenerator il, System.Type type) + /// + /// The InvokeMethod + /// + /// The invoke + /// The target + /// The paramters + /// The + internal static object InvokeMethod(FastInvokeHandler invoke, object target, params object[] paramters) + { + return invoke(null, paramters); + } + + /// + /// The EmitBoxIfNeeded + /// + /// The il + /// The type + private static void EmitBoxIfNeeded(ILGenerator il, System.Type type) { if (type.IsValueType) { - il.Emit(OpCodes.Unbox_Any, type); - } - else - { - il.Emit(OpCodes.Castclass, type); + il.Emit(OpCodes.Box, type); } } - private static void EmitBoxIfNeeded(ILGenerator il, System.Type type) + /// + /// The EmitCastToReference + /// + /// The il + /// The type + private static void EmitCastToReference(ILGenerator il, System.Type type) { if (type.IsValueType) { - il.Emit(OpCodes.Box, type); + il.Emit(OpCodes.Unbox_Any, type); + } + else + { + il.Emit(OpCodes.Castclass, type); } } + /// + /// The EmitFastInt + /// + /// The il + /// The value private static void EmitFastInt(ILGenerator il, int value) { switch (value) @@ -110,30 +149,39 @@ private static void EmitFastInt(ILGenerator il, int value) case -1: il.Emit(OpCodes.Ldc_I4_M1); return; + case 0: il.Emit(OpCodes.Ldc_I4_0); return; + case 1: il.Emit(OpCodes.Ldc_I4_1); return; + case 2: il.Emit(OpCodes.Ldc_I4_2); return; + case 3: il.Emit(OpCodes.Ldc_I4_3); return; + case 4: il.Emit(OpCodes.Ldc_I4_4); return; + case 5: il.Emit(OpCodes.Ldc_I4_5); return; + case 6: il.Emit(OpCodes.Ldc_I4_6); return; + case 7: il.Emit(OpCodes.Ldc_I4_7); return; + case 8: il.Emit(OpCodes.Ldc_I4_8); return; @@ -148,5 +196,7 @@ private static void EmitFastInt(ILGenerator il, int value) il.Emit(OpCodes.Ldc_I4, value); } } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Utilities/ManualResetValueTaskSource.cs b/src/Surging.Core/Surging.Core.CPlatform/Utilities/ManualResetValueTaskSource.cs index 07a1897dd..9aeb0c721 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Utilities/ManualResetValueTaskSource.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Utilities/ManualResetValueTaskSource.cs @@ -6,111 +6,123 @@ namespace Surging.Core.CPlatform.Utilities { - internal interface IStrongBox - { - ref T Value { get; } - - bool RunContinuationsAsynchronously { get; set; } - } + #region ö + /// + /// Defines the ContinuationOptions + /// public enum ContinuationOptions { + /// + /// Defines the None + /// None, + /// + /// Defines the ForceDefaultTaskScheduler + /// ForceDefaultTaskScheduler } - public class ManualResetValueTaskSource : IStrongBox>, IValueTaskSource, IValueTaskSource - { - private ManualResetValueTaskSourceLogic _logic; - private readonly Action _cancellationCallback; + #endregion ö - public ManualResetValueTaskSource(ContinuationOptions options = ContinuationOptions.None) - { - _logic = new ManualResetValueTaskSourceLogic(this, options); - _cancellationCallback = SetCanceled; - } + #region ӿ - public short Version => _logic.Version; - - public bool SetResult(T result) - { - lock (_cancellationCallback) - { - if (_logic.Completed) - { - return false; - } + /// + /// Defines the + /// + /// + internal interface IStrongBox + { + #region - _logic.SetResult(result); - return true; - } - } + /// + /// Gets or sets a value indicating whether RunContinuationsAsynchronously + /// + bool RunContinuationsAsynchronously { get; set; } - public void SetException(Exception error) - { - if (Monitor.TryEnter(_cancellationCallback)) - { - if (_logic.Completed) - { - Monitor.Exit(_cancellationCallback); - return; - } + /// + /// Gets the Value + /// + ref T Value { get; } - _logic.SetException(error); - Monitor.Exit(_cancellationCallback); - } - } + #endregion + } - public void SetCanceled() => SetException(new TaskCanceledException()); + #endregion ӿ - public T GetResult(short token) => _logic.GetResult(token); + /// + /// Defines the + /// + /// + internal struct ManualResetValueTaskSourceLogic + { + #region ֶ - void IValueTaskSource.GetResult(short token) => _logic.GetResult(token); + /// + /// Defines the s_sentinel + /// + private static readonly Action s_sentinel = s => throw new InvalidOperationException(); - public ValueTaskSourceStatus GetStatus(short token) => _logic.GetStatus(token); + /// + /// Defines the _options + /// + private readonly ContinuationOptions _options; - public bool RunContinuationsAsynchronously { get; set; } = true; + /// + /// Defines the _parent + /// + private readonly IStrongBox> _parent; - public void OnCompleted(Action continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) => _logic.OnCompleted(continuation, state, token, flags); + /// + /// Defines the _capturedContext + /// + private object _capturedContext; - ref ManualResetValueTaskSourceLogic IStrongBox>.Value => ref _logic; + /// + /// Defines the _completed + /// + private bool _completed; + /// + /// Defines the _continuation + /// + private Action _continuation; - public ValueTask AwaitValue(CancellationToken cancellation) - { - CancellationTokenRegistration? registration = cancellation == CancellationToken.None - ? (CancellationTokenRegistration?)null - : cancellation.Register(_cancellationCallback); - return _logic.AwaitValue(this, registration); - } + /// + /// Defines the _continuationState + /// + private object _continuationState; - public ValueTask AwaitVoid(CancellationToken cancellation) - { - CancellationTokenRegistration? registration = cancellation == CancellationToken.None - ? (CancellationTokenRegistration?)null - : cancellation.Register(_cancellationCallback); - return _logic.AwaitVoid(this, registration); - } + /// + /// Defines the _error + /// + private ExceptionDispatchInfo _error; - public void Reset() => _logic.Reset(); - } + /// + /// Defines the _executionContext + /// + private ExecutionContext _executionContext; - internal struct ManualResetValueTaskSourceLogic - { - private static readonly Action s_sentinel = s => throw new InvalidOperationException(); + /// + /// Defines the _registration + /// + private CancellationTokenRegistration? _registration; - private readonly IStrongBox> _parent; - private readonly ContinuationOptions _options; - private Action _continuation; - private object _continuationState; - private object _capturedContext; - private ExecutionContext _executionContext; - private bool _completed; + /// + /// Defines the _result + /// private TResult _result; - private ExceptionDispatchInfo _error; - private CancellationTokenRegistration? _registration; + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The parent + /// The options public ManualResetValueTaskSourceLogic(IStrongBox> parent, ContinuationOptions options) { _parent = parent ?? throw new ArgumentNullException(nameof(parent)); @@ -126,29 +138,54 @@ public ManualResetValueTaskSourceLogic(IStrongBox + /// Gets a value indicating whether Completed + /// public bool Completed => _completed; - private void ValidateToken(short token) + /// + /// Gets the Version + /// + public short Version { get; private set; } + + #endregion + + #region + + /// + /// The AwaitValue + /// + /// + /// The source + /// The registration + /// The + public ValueTask AwaitValue(IValueTaskSource source, CancellationTokenRegistration? registration) { - if (token != Version) - { - throw new InvalidOperationException(); - } + _registration = registration; + return new ValueTask(source, Version); } - public ValueTaskSourceStatus GetStatus(short token) + /// + /// The AwaitVoid + /// + /// The source + /// The registration + /// The + public ValueTask AwaitVoid(IValueTaskSource source, CancellationTokenRegistration? registration) { - ValidateToken(token); - - return - !_completed ? ValueTaskSourceStatus.Pending : - _error == null ? ValueTaskSourceStatus.Succeeded : - _error.SourceException is OperationCanceledException ? ValueTaskSourceStatus.Canceled : - ValueTaskSourceStatus.Faulted; + _registration = registration; + return new ValueTask(source, Version); } + /// + /// The GetResult + /// + /// The token + /// The public TResult GetResult(short token) { ValidateToken(token); @@ -166,22 +203,29 @@ public TResult GetResult(short token) return result; } - public void Reset() + /// + /// The GetStatus + /// + /// The token + /// The + public ValueTaskSourceStatus GetStatus(short token) { - Version++; - - _registration?.Dispose(); + ValidateToken(token); - _completed = false; - _continuation = null; - _continuationState = null; - _result = default(TResult); - _error = null; - _executionContext = null; - _capturedContext = null; - _registration = null; + return + !_completed ? ValueTaskSourceStatus.Pending : + _error == null ? ValueTaskSourceStatus.Succeeded : + _error.SourceException is OperationCanceledException ? ValueTaskSourceStatus.Canceled : + ValueTaskSourceStatus.Faulted; } + /// + /// The OnCompleted + /// + /// The continuation + /// The state + /// The token + /// The flags public void OnCompleted(Action continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) { if (continuation == null) @@ -191,7 +235,6 @@ public void OnCompleted(Action continuation, object state, short token, ValidateToken(token); - if ((flags & ValueTaskSourceOnCompletedFlags.FlowExecutionContext) != 0) { _executionContext = ExecutionContext.Capture(); @@ -243,43 +286,48 @@ public void OnCompleted(Action continuation, object state, short token, } } - public void SetResult(TResult result) + /// + /// The Reset + /// + public void Reset() { - _result = result; - SignalCompletion(); + Version++; + + _registration?.Dispose(); + + _completed = false; + _continuation = null; + _continuationState = null; + _result = default(TResult); + _error = null; + _executionContext = null; + _capturedContext = null; + _registration = null; } + /// + /// The SetException + /// + /// The error public void SetException(Exception error) { _error = ExceptionDispatchInfo.Capture(error); SignalCompletion(); } - private void SignalCompletion() + /// + /// The SetResult + /// + /// The result + public void SetResult(TResult result) { - if (_completed) - { - throw new InvalidOperationException("Double completion of completion source is prohibited"); - } - - _completed = true; - - if (Interlocked.CompareExchange(ref _continuation, s_sentinel, null) != null) - { - if (_executionContext != null) - { - ExecutionContext.Run( - _executionContext, - s => ((IStrongBox>)s).Value.InvokeContinuation(), - _parent ?? throw new InvalidOperationException()); - } - else - { - InvokeContinuation(); - } - } + _result = result; + SignalCompletion(); } + /// + /// The InvokeContinuation + /// private void InvokeContinuation() { object cc = _capturedContext; @@ -302,7 +350,7 @@ private void InvokeContinuation() } else { - ThreadPool.UnsafeQueueUserWorkItem(s => c(s), _continuationState); + ThreadPool.UnsafeQueueUserWorkItem(s => c(s), _continuationState); } } else @@ -325,16 +373,207 @@ private void InvokeContinuation() } } - public ValueTask AwaitValue(IValueTaskSource source, CancellationTokenRegistration? registration) + /// + /// The SignalCompletion + /// + private void SignalCompletion() { - _registration = registration; - return new ValueTask(source, Version); + if (_completed) + { + throw new InvalidOperationException("Double completion of completion source is prohibited"); + } + + _completed = true; + + if (Interlocked.CompareExchange(ref _continuation, s_sentinel, null) != null) + { + if (_executionContext != null) + { + ExecutionContext.Run( + _executionContext, + s => ((IStrongBox>)s).Value.InvokeContinuation(), + _parent ?? throw new InvalidOperationException()); + } + else + { + InvokeContinuation(); + } + } } - public ValueTask AwaitVoid(IValueTaskSource source, CancellationTokenRegistration? registration) + /// + /// The ValidateToken + /// + /// The token + private void ValidateToken(short token) { - _registration = registration; - return new ValueTask(source, Version); + if (token != Version) + { + throw new InvalidOperationException(); + } + } + + #endregion + } + + /// + /// Defines the + /// + /// + public class ManualResetValueTaskSource : IStrongBox>, IValueTaskSource, IValueTaskSource + { + #region ֶ + + /// + /// Defines the _cancellationCallback + /// + private readonly Action _cancellationCallback; + + /// + /// Defines the _logic + /// + private ManualResetValueTaskSourceLogic _logic; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The options + public ManualResetValueTaskSource(ContinuationOptions options = ContinuationOptions.None) + { + _logic = new ManualResetValueTaskSourceLogic(this, options); + _cancellationCallback = SetCanceled; + } + + #endregion 캯 + + #region + + /// + /// Gets or sets a value indicating whether RunContinuationsAsynchronously + /// + public bool RunContinuationsAsynchronously { get; set; } = true; + + /// + /// Gets the Version + /// + public short Version => _logic.Version; + + /// + /// Gets the Value + /// + ref ManualResetValueTaskSourceLogic IStrongBox>.Value => ref _logic; + + #endregion + + #region + + /// + /// The AwaitValue + /// + /// The cancellation + /// The + public ValueTask AwaitValue(CancellationToken cancellation) + { + CancellationTokenRegistration? registration = cancellation == CancellationToken.None + ? (CancellationTokenRegistration?)null + : cancellation.Register(_cancellationCallback); + return _logic.AwaitValue(this, registration); } + + /// + /// The AwaitVoid + /// + /// The cancellation + /// The + public ValueTask AwaitVoid(CancellationToken cancellation) + { + CancellationTokenRegistration? registration = cancellation == CancellationToken.None + ? (CancellationTokenRegistration?)null + : cancellation.Register(_cancellationCallback); + return _logic.AwaitVoid(this, registration); + } + + /// + /// The GetResult + /// + /// The token + /// The + public T GetResult(short token) => _logic.GetResult(token); + + /// + /// The GetStatus + /// + /// The token + /// The + public ValueTaskSourceStatus GetStatus(short token) => _logic.GetStatus(token); + + /// + /// The OnCompleted + /// + /// The continuation + /// The state + /// The token + /// The flags + public void OnCompleted(Action continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) => _logic.OnCompleted(continuation, state, token, flags); + + /// + /// The Reset + /// + public void Reset() => _logic.Reset(); + + /// + /// The SetCanceled + /// + public void SetCanceled() => SetException(new TaskCanceledException()); + + /// + /// The SetException + /// + /// The error + public void SetException(Exception error) + { + if (Monitor.TryEnter(_cancellationCallback)) + { + if (_logic.Completed) + { + Monitor.Exit(_cancellationCallback); + return; + } + + _logic.SetException(error); + Monitor.Exit(_cancellationCallback); + } + } + + /// + /// The SetResult + /// + /// The result + /// The + public bool SetResult(T result) + { + lock (_cancellationCallback) + { + if (_logic.Completed) + { + return false; + } + + _logic.SetResult(result); + return true; + } + } + + /// + /// The GetResult + /// + /// The token + void IValueTaskSource.GetResult(short token) => _logic.GetResult(token); + + #endregion } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Utilities/NetUtils.cs b/src/Surging.Core/Surging.Core.CPlatform/Utilities/NetUtils.cs index 1195c7025..cccc028f2 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Utilities/NetUtils.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Utilities/NetUtils.cs @@ -6,49 +6,60 @@ namespace Surging.Core.CPlatform.Utilities { + /// + /// Defines the + /// public class NetUtils { - public const string LOCALHOST = "127.0.0.1"; + #region 常量 + + /// + /// Defines the ANYHOST + /// public const string ANYHOST = "0.0.0.0"; - private const int MIN_PORT = 0; - private const int MAX_PORT = 65535; - private const string LOCAL_IP_PATTERN = "127(\\.\\d{1,3}){3}$"; + + /// + /// Defines the LOCALHOST + /// + public const string LOCALHOST = "127.0.0.1"; + + /// + /// Defines the IP_PATTERN + /// private const string IP_PATTERN = "\\d{1,3}(\\.\\d{1,3}){3,5}$"; - private static AddressModel _host = null; - public static bool IsInvalidPort(int port) - { - return port <= MIN_PORT || port > MAX_PORT; - } + /// + /// Defines the LOCAL_IP_PATTERN + /// + private const string LOCAL_IP_PATTERN = "127(\\.\\d{1,3}){3}$"; - public static bool IsLocalHost(string host) - { - return host != null - && (host.IsMatch(LOCAL_IP_PATTERN) - || host.Equals("localhost", StringComparison.OrdinalIgnoreCase)); - } + /// + /// Defines the MAX_PORT + /// + private const int MAX_PORT = 65535; - public static bool IsAnyHost(String host) - { - return "0.0.0.0".Equals(host); - } + /// + /// Defines the MIN_PORT + /// + private const int MIN_PORT = 0; - private static bool IsValidAddress(string address) - { - return (address != null - && !ANYHOST.Equals(address) - && address.IsMatch(IP_PATTERN)); - } + #endregion 常量 - public static bool IsInvalidLocalHost(String host) - { - return host == null - || host.Length == 0 - || host.Equals("localhost", StringComparison.OrdinalIgnoreCase) - || host.Equals("0.0.0.0") - || (host.IsMatch(LOCAL_IP_PATTERN)); - } + #region 字段 + + /// + /// Defines the _host + /// + private static AddressModel _host = null; + + #endregion 字段 + #region 方法 + + /// + /// The GetAnyHostAddress + /// + /// The public static string GetAnyHostAddress() { string result = ""; @@ -71,16 +82,10 @@ public static string GetAnyHostAddress() return result; } - public static string GetHostAddress(string hostAddress) - { - var result = hostAddress; - if ((!IsValidAddress(hostAddress) && !IsLocalHost(hostAddress)) || IsAnyHost(hostAddress)) - { - result = GetAnyHostAddress(); - } - return result; - } - + /// + /// The GetHostAddress + /// + /// The public static AddressModel GetHostAddress() { if (_host != null) @@ -103,5 +108,80 @@ public static AddressModel GetHostAddress() }; return _host; } + + /// + /// The GetHostAddress + /// + /// The hostAddress + /// The + public static string GetHostAddress(string hostAddress) + { + var result = hostAddress; + if ((!IsValidAddress(hostAddress) && !IsLocalHost(hostAddress)) || IsAnyHost(hostAddress)) + { + result = GetAnyHostAddress(); + } + return result; + } + + /// + /// The IsAnyHost + /// + /// The host + /// The + public static bool IsAnyHost(String host) + { + return "0.0.0.0".Equals(host); + } + + /// + /// The IsInvalidLocalHost + /// + /// The host + /// The + public static bool IsInvalidLocalHost(String host) + { + return host == null + || host.Length == 0 + || host.Equals("localhost", StringComparison.OrdinalIgnoreCase) + || host.Equals("0.0.0.0") + || (host.IsMatch(LOCAL_IP_PATTERN)); + } + + /// + /// The IsInvalidPort + /// + /// The port + /// The + public static bool IsInvalidPort(int port) + { + return port <= MIN_PORT || port > MAX_PORT; + } + + /// + /// The IsLocalHost + /// + /// The host + /// The + public static bool IsLocalHost(string host) + { + return host != null + && (host.IsMatch(LOCAL_IP_PATTERN) + || host.Equals("localhost", StringComparison.OrdinalIgnoreCase)); + } + + /// + /// The IsValidAddress + /// + /// The address + /// The + private static bool IsValidAddress(string address) + { + return (address != null + && !ANYHOST.Equals(address) + && address.IsMatch(IP_PATTERN)); + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Utilities/ServiceLocator.cs b/src/Surging.Core/Surging.Core.CPlatform/Utilities/ServiceLocator.cs index 38b8543a3..9fc06ccd6 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Utilities/ServiceLocator.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Utilities/ServiceLocator.cs @@ -3,49 +3,106 @@ namespace Surging.Core.CPlatform.Utilities { + /// + /// Defines the + /// public class ServiceLocator { + #region 属性 + + /// + /// Gets or sets the Current + /// public static IContainer Current { get; set; } + #endregion 属性 + + #region 方法 + + /// + /// The GetService + /// + /// + /// The public static T GetService() { return Current.Resolve(); } - public static bool IsRegistered() + /// + /// The GetService + /// + /// + /// The key + /// The + public static T GetService(string key) { - return Current.IsRegistered(); + return Current.ResolveKeyed(key); } - public static bool IsRegistered(string key) + /// + /// The GetService + /// + /// The key + /// The type + /// The + public static object GetService(string key, Type type) { - return Current.IsRegisteredWithKey(key); + return Current.ResolveKeyed(key, type); } - public static bool IsRegistered(Type type) + /// + /// The GetService + /// + /// The type + /// The + public static object GetService(Type type) { - return Current.IsRegistered(type); + return Current.Resolve(type); } - public static bool IsRegisteredWithKey(string key, Type type) + /// + /// The IsRegistered + /// + /// + /// The + public static bool IsRegistered() { - return Current.IsRegisteredWithKey(key, type); + return Current.IsRegistered(); } - public static T GetService(string key) + /// + /// The IsRegistered + /// + /// + /// The key + /// The + public static bool IsRegistered(string key) { - - return Current.ResolveKeyed(key); + return Current.IsRegisteredWithKey(key); } - public static object GetService(Type type) + /// + /// The IsRegistered + /// + /// The type + /// The + public static bool IsRegistered(Type type) { - return Current.Resolve(type); + return Current.IsRegistered(type); } - public static object GetService(string key, Type type) + /// + /// The IsRegisteredWithKey + /// + /// The key + /// The type + /// The + public static bool IsRegisteredWithKey(string key, Type type) { - return Current.ResolveKeyed(key, type); + return Current.IsRegisteredWithKey(key, type); } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Utilities/StringExtensions.cs b/src/Surging.Core/Surging.Core.CPlatform/Utilities/StringExtensions.cs index 28bc9f514..6fc7828db 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Utilities/StringExtensions.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Utilities/StringExtensions.cs @@ -3,15 +3,33 @@ namespace Surging.Core.CPlatform.Utilities { + /// + /// Defines the + /// public static class StringExtensions { + #region 方法 + + /// + /// The IsIP + /// + /// The input + /// The public static bool IsIP(this string input) => input.IsMatch(@"\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\:\d{2,5}\b"); + /// + /// The IsMatch + /// + /// The str + /// The op + /// The public static bool IsMatch(this string str, string op) { if (str.Equals(String.Empty) || str == null) return false; var re = new Regex(op, RegexOptions.IgnoreCase); return re.IsMatch(str); } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Utilities/TaskHelpers.cs b/src/Surging.Core/Surging.Core.CPlatform/Utilities/TaskHelpers.cs index 51f23a028..2252e93f4 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Utilities/TaskHelpers.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Utilities/TaskHelpers.cs @@ -5,32 +5,71 @@ namespace Surging.Core.CPlatform.Utilities { + /// + /// Defines the + /// internal static class TaskHelpers { - private static readonly Task _defaultCompleted = Task.FromResult(default(AsyncVoid)); + #region 字段 + /// + /// Defines the _completedTaskReturningNull + /// private static readonly Task _completedTaskReturningNull = Task.FromResult(null); - + + /// + /// Defines the _defaultCompleted + /// + private static readonly Task _defaultCompleted = Task.FromResult(default(AsyncVoid)); + + #endregion 字段 + + #region 方法 + + /// + /// The Canceled + /// + /// The internal static Task Canceled() { return CancelCache.Canceled; } - + + /// + /// The Canceled + /// + /// + /// The internal static Task Canceled() { return CancelCache.Canceled; } - + + /// + /// The Completed + /// + /// The internal static Task Completed() { return _defaultCompleted; } - + + /// + /// The FromError + /// + /// The exception + /// The internal static Task FromError(Exception exception) { return FromError(exception); } - + + /// + /// The FromError + /// + /// + /// The exception + /// The internal static Task FromError(Exception exception) { TaskCompletionSource tcs = new TaskCompletionSource(); @@ -38,25 +77,53 @@ internal static Task FromError(Exception exception) return tcs.Task; } + /// + /// The NullResult + /// + /// The internal static Task NullResult() { return _completedTaskReturningNull; } - + + #endregion 方法 + + /// + /// Defines the + /// private struct AsyncVoid { } - + + /// + /// Defines the + /// + /// private static class CancelCache { + #region 字段 + + /// + /// Defines the Canceled + /// public static readonly Task Canceled = GetCancelledTask(); + #endregion 字段 + + #region 方法 + + /// + /// The GetCancelledTask + /// + /// The private static Task GetCancelledTask() { TaskCompletionSource tcs = new TaskCompletionSource(); tcs.SetCanceled(); return tcs.Task; } + + #endregion 方法 } } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.CPlatform/Utilities/UtilityType.cs b/src/Surging.Core/Surging.Core.CPlatform/Utilities/UtilityType.cs index 6cefba619..11482d673 100644 --- a/src/Surging.Core/Surging.Core.CPlatform/Utilities/UtilityType.cs +++ b/src/Surging.Core/Surging.Core.CPlatform/Utilities/UtilityType.cs @@ -5,14 +5,33 @@ namespace Surging.Core.CPlatform.Utilities { + /// + /// Defines the + /// public static class UtilityType { - public static Type JObjectType = typeof(JObject); + #region 字段 + + /// + /// Defines the ConvertibleType + /// + public static Type ConvertibleType = typeof(IConvertible); + /// + /// Defines the JArrayType + /// public static Type JArrayType = typeof(JArray); + /// + /// Defines the JObjectType + /// + public static Type JObjectType = typeof(JObject); + + /// + /// Defines the ObjectType + /// public static Type ObjectType = typeof(Object); - public static Type ConvertibleType = typeof(IConvertible); + #endregion 字段 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/AddressResolvers/IAddressResolver.cs b/src/Surging.Core/Surging.Core.Caching/AddressResolvers/IAddressResolver.cs index 392548c3c..1b4026c85 100644 --- a/src/Surging.Core/Surging.Core.Caching/AddressResolvers/IAddressResolver.cs +++ b/src/Surging.Core/Surging.Core.Caching/AddressResolvers/IAddressResolver.cs @@ -7,8 +7,25 @@ namespace Surging.Core.Caching.AddressResolvers { + #region 接口 + + /// + /// Defines the + /// public interface IAddressResolver { + #region 方法 + + /// + /// The Resolver + /// + /// The cacheId + /// The item + /// The ValueTask Resolver(string cacheId, string item); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/AddressResolvers/Implementation/DefaultAddressResolver.cs b/src/Surging.Core/Surging.Core.Caching/AddressResolvers/Implementation/DefaultAddressResolver.cs index 1c4125e73..8c882d116 100644 --- a/src/Surging.Core/Surging.Core.Caching/AddressResolvers/Implementation/DefaultAddressResolver.cs +++ b/src/Surging.Core/Surging.Core.Caching/AddressResolvers/Implementation/DefaultAddressResolver.cs @@ -19,15 +19,39 @@ namespace Surging.Core.Caching.AddressResolvers.Implementation /// public class DefaultAddressResolver : IAddressResolver { + #region 字段 - #region Field - private readonly ILogger _logger; - private readonly IHealthCheckService _healthCheckService; - private readonly IServiceCacheManager _serviceCacheManager; + /// + /// Defines the _concurrent + /// private readonly ConcurrentDictionary _concurrent = new ConcurrentDictionary(); - #endregion + /// + /// Defines the _healthCheckService + /// + private readonly IHealthCheckService _healthCheckService; + + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the _serviceCacheManager + /// + private readonly IServiceCacheManager _serviceCacheManager; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The healthCheckService + /// The logger + /// The serviceCacheManager public DefaultAddressResolver(IHealthCheckService healthCheckService, ILogger logger, IServiceCacheManager serviceCacheManager) { _healthCheckService = healthCheckService; @@ -38,9 +62,18 @@ public DefaultAddressResolver(IHealthCheckService healthCheckService, ILogger + /// The Resolver + /// + /// The cacheId + /// The item + /// The public async ValueTask Resolver(string cacheId, string item) { - _concurrent.TryGetValue(cacheId, out ServiceCache descriptor); if (descriptor == null) { @@ -86,27 +119,34 @@ public async ValueTask Resolver(string cacheId, string item) return hash != null ? hash.GetItemNode(item) : default(ConsistentHashNode); } - + /// + /// The GetKey + /// + /// The descriptor + /// The private static string GetKey(CacheDescriptor descriptor) { return descriptor.Id; } - private void ServiceCacheManager_Removed(object sender, ServiceCacheEventArgs e) + /// + /// The ServiceCacheManager_Add + /// + /// The sender + /// The e + private void ServiceCacheManager_Add(object sender, ServiceCacheEventArgs e) { var key = GetKey(e.Cache.CacheDescriptor); if (CacheContainer.IsRegistered(e.Cache.CacheDescriptor.Prefix)) { var redisContext = CacheContainer.GetService(e.Cache.CacheDescriptor.Prefix); - ServiceCache value; - _concurrent.TryRemove(key, out value); + _concurrent.GetOrAdd(key, e.Cache); ConsistentHash hash; redisContext.dicHash.TryGetValue(e.Cache.CacheDescriptor.Type, out hash); if (hash != null) foreach (var node in e.Cache.CacheEndpoint) { - - var hashNode = node as ConsistentHashNode; + var hashNode = node as ConsistentHashNode; var addr = string.Format("{0}:{1}", hashNode.Host, hashNode.Port); hash.Remove(addr); hash.Add(hashNode, addr); @@ -114,13 +154,19 @@ private void ServiceCacheManager_Removed(object sender, ServiceCacheEventArgs e) } } - private void ServiceCacheManager_Add(object sender, ServiceCacheEventArgs e) + /// + /// The ServiceCacheManager_Removed + /// + /// The sender + /// The e + private void ServiceCacheManager_Removed(object sender, ServiceCacheEventArgs e) { var key = GetKey(e.Cache.CacheDescriptor); if (CacheContainer.IsRegistered(e.Cache.CacheDescriptor.Prefix)) { var redisContext = CacheContainer.GetService(e.Cache.CacheDescriptor.Prefix); - _concurrent.GetOrAdd(key, e.Cache); + ServiceCache value; + _concurrent.TryRemove(key, out value); ConsistentHash hash; redisContext.dicHash.TryGetValue(e.Cache.CacheDescriptor.Type, out hash); if (hash != null) @@ -133,5 +179,7 @@ private void ServiceCacheManager_Add(object sender, ServiceCacheEventArgs e) } } } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/AppConfig.cs b/src/Surging.Core/Surging.Core.Caching/AppConfig.cs index 46d08a339..50ee383f5 100644 --- a/src/Surging.Core/Surging.Core.Caching/AppConfig.cs +++ b/src/Surging.Core/Surging.Core.Caching/AppConfig.cs @@ -11,13 +11,39 @@ namespace Surging.Core.Caching { + /// + /// Defines the + /// public class AppConfig { + #region 常量 + + /// + /// Defines the CacheSectionName + /// private const string CacheSectionName = "CachingProvider"; + + #endregion 常量 + + #region 字段 + + /// + /// Defines the _cacheWrapperSetting + /// private readonly CachingProvider _cacheWrapperSetting; + + /// + /// Defines the Path + /// internal static string Path; - internal static IConfigurationRoot Configuration { get; set; } + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public AppConfig() { ServiceResolver.Current.Register(null, Activator.CreateInstance(typeof(HashAlgorithm), new object[] { })); @@ -27,6 +53,18 @@ public AppConfig() InitSettingMethod(); } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets or sets the Configuration + /// + internal static IConfigurationRoot Configuration { get; set; } + + /// + /// Gets the DefaultInstance + /// internal static AppConfig DefaultInstance { get @@ -41,12 +79,27 @@ internal static AppConfig DefaultInstance } } + #endregion 属性 + + #region 方法 + + /// + /// The GetContextInstance + /// + /// + /// The public T GetContextInstance() where T : class { var context = ServiceResolver.Current.GetService(typeof(T)); return context; } + /// + /// The GetContextInstance + /// + /// + /// The name + /// The public T GetContextInstance(string name) where T : class { DebugCheck.NotEmpty(name); @@ -54,17 +107,64 @@ public T GetContextInstance(string name) where T : class return context; } - private void RegisterLocalInstance(string typeName) + /// + /// The GetTypedPropertyValue + /// + /// The obj + /// The + public object GetTypedPropertyValue(Property obj) { - var types = this.GetType().GetTypeInfo().Assembly.GetTypes().Where(p => p.GetTypeInfo().GetInterface(typeName) != null); - foreach (var t in types) + var mapCollections = obj.Maps; + if (mapCollections != null && mapCollections.Any()) { - var attribute = t.GetTypeInfo().GetCustomAttribute(); - ServiceResolver.Current.Register(attribute.Name.ToString(), - Activator.CreateInstance(t)); + var results = new List(); + foreach (var map in mapCollections) + { + object items = null; + if (map.Properties != null) items = map.Properties.Select(p => GetTypedPropertyValue(p)).ToArray(); + results.Add(new + { + Name = Convert.ChangeType(obj.Name, typeof(string)), + Value = Convert.ChangeType(map.Name, typeof(string)), + Items = items + }); + } + return results; } + else if (!string.IsNullOrEmpty(obj.Value)) + { + return new + { + Name = Convert.ChangeType(obj.Name ?? "", typeof(string)), + Value = Convert.ChangeType(obj.Value, typeof(string)), + }; + } + else if (!string.IsNullOrEmpty(obj.Ref)) + return Convert.ChangeType(obj.Ref, typeof(string)); + + return null; } + /// + /// The InitSettingMethod + /// + private void InitSettingMethod() + { + var settings = + _cacheWrapperSetting.CachingSettings + .Where(p => !string.IsNullOrEmpty(p.InitMethod)); + foreach (var setting in settings) + { + var bindingInstance = + ServiceResolver.Current.GetService(Type.GetType(setting.Class, throwOnError: true), + setting.Id); + bindingInstance.GetType().GetMethod(setting.InitMethod, System.Reflection.BindingFlags.InvokeMethod).Invoke(bindingInstance, new object[] { }); + } + } + + /// + /// The RegisterConfigInstance + /// private void RegisterConfigInstance() { var bingingSettings = _cacheWrapperSetting.CachingSettings; @@ -106,51 +206,21 @@ var mapsetting in catch { } } - public object GetTypedPropertyValue(Property obj) + /// + /// The RegisterLocalInstance + /// + /// The typeName + private void RegisterLocalInstance(string typeName) { - var mapCollections = obj.Maps; - if (mapCollections != null && mapCollections.Any()) - { - var results = new List(); - foreach (var map in mapCollections) - { - object items = null; - if (map.Properties != null) items = map.Properties.Select(p => GetTypedPropertyValue(p)).ToArray(); - results.Add(new - { - Name = Convert.ChangeType(obj.Name, typeof(string)), - Value = Convert.ChangeType(map.Name, typeof(string)), - Items = items - }); - } - return results; - } - else if (!string.IsNullOrEmpty(obj.Value)) + var types = this.GetType().GetTypeInfo().Assembly.GetTypes().Where(p => p.GetTypeInfo().GetInterface(typeName) != null); + foreach (var t in types) { - return new - { - Name = Convert.ChangeType(obj.Name ?? "", typeof(string)), - Value = Convert.ChangeType(obj.Value, typeof(string)), - }; + var attribute = t.GetTypeInfo().GetCustomAttribute(); + ServiceResolver.Current.Register(attribute.Name.ToString(), + Activator.CreateInstance(t)); } - else if (!string.IsNullOrEmpty(obj.Ref)) - return Convert.ChangeType(obj.Ref, typeof(string)); - - return null; } - private void InitSettingMethod() - { - var settings = - _cacheWrapperSetting.CachingSettings - .Where(p => !string.IsNullOrEmpty(p.InitMethod)); - foreach (var setting in settings) - { - var bindingInstance = - ServiceResolver.Current.GetService(Type.GetType(setting.Class, throwOnError: true), - setting.Id); - bindingInstance.GetType().GetMethod(setting.InitMethod, System.Reflection.BindingFlags.InvokeMethod).Invoke(bindingInstance, new object[] { }); - } - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/CacheContainer.cs b/src/Surging.Core/Surging.Core.Caching/CacheContainer.cs index 3658404d5..c19dc0417 100644 --- a/src/Surging.Core/Surging.Core.Caching/CacheContainer.cs +++ b/src/Surging.Core/Surging.Core.Caching/CacheContainer.cs @@ -5,51 +5,101 @@ namespace Surging.Core.Caching { - public class CacheContainer + /// + /// Defines the + /// + public class CacheContainer { - public static T GetInstances(string name) where T : class - { - var appConfig = AppConfig.DefaultInstance; - return appConfig.GetContextInstance(name); - } + #region 方法 + /// + /// The GetInstances + /// + /// + /// The public static T GetInstances() where T : class { var appConfig = AppConfig.DefaultInstance; return appConfig.GetContextInstance(); } - public static T GetService(string name) + /// + /// The GetInstances + /// + /// + /// The name + /// The + public static T GetInstances(string name) where T : class { - if (ServiceLocator.Current == null) return default(T); - return ServiceLocator.GetService(name); + var appConfig = AppConfig.DefaultInstance; + return appConfig.GetContextInstance(name); } + /// + /// The GetService + /// + /// + /// The public static T GetService() { if (ServiceLocator.Current == null) return default(T); return ServiceLocator.GetService(); } + /// + /// The GetService + /// + /// + /// The name + /// The + public static T GetService(string name) + { + if (ServiceLocator.Current == null) return default(T); + return ServiceLocator.GetService(name); + } + + /// + /// The IsRegistered + /// + /// + /// The public static bool IsRegistered() { return ServiceLocator.IsRegistered(); } + /// + /// The IsRegistered + /// + /// + /// The key + /// The public static bool IsRegistered(string key) { return ServiceLocator.IsRegistered(key); } + /// + /// The IsRegistered + /// + /// The type + /// The public static bool IsRegistered(Type type) { return ServiceLocator.IsRegistered(type); } + /// + /// The IsRegisteredWithKey + /// + /// The key + /// The type + /// The public static bool IsRegisteredWithKey(string key, Type type) { return ServiceLocator.IsRegisteredWithKey(key, type); } - } -} + #endregion 方法 + } +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/CacheTargetType.cs b/src/Surging.Core/Surging.Core.Caching/CacheTargetType.cs index c087b14b7..be7f5cd2d 100644 --- a/src/Surging.Core/Surging.Core.Caching/CacheTargetType.cs +++ b/src/Surging.Core/Surging.Core.Caching/CacheTargetType.cs @@ -4,11 +4,33 @@ namespace Surging.Core.Caching { + #region 枚举 + + /// + /// Defines the CacheTargetType + /// public enum CacheTargetType { + /// + /// Defines the Redis + /// Redis, + + /// + /// Defines the CouchBase + /// CouchBase, + + /// + /// Defines the Memcached + /// Memcached, + + /// + /// Defines the MemoryCache + /// MemoryCache, } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/CachingModule.cs b/src/Surging.Core/Surging.Core.Caching/CachingModule.cs index 0e97a04c4..63d1f8e02 100644 --- a/src/Surging.Core/Surging.Core.Caching/CachingModule.cs +++ b/src/Surging.Core/Surging.Core.Caching/CachingModule.cs @@ -20,8 +20,17 @@ namespace Surging.Core.Caching { + /// + /// Defines the + /// public class CachingModule : EnginePartModule { + #region 方法 + + /// + /// The Initialize + /// + /// The context public override void Initialize(AppModuleContext context) { var serviceProvider = context.ServiceProvoider; @@ -49,17 +58,48 @@ protected override void RegisterBuilder(ContainerBuilderWrapper builder) RegisterLocalInstance("ICacheClient`1", builder); } - private static void RegisterLocalInstance(string typeName, ContainerBuilderWrapper builder) + /// + /// The GetTypedPropertyValue + /// + /// The obj + /// The + private static object GetTypedPropertyValue(Property obj) { - var types = typeof(AppConfig) - .Assembly.GetTypes().Where(p => p.GetTypeInfo().GetInterface(typeName) != null); - foreach (var t in types) + var mapCollections = obj.Maps; + if (mapCollections != null && mapCollections.Any()) { - var attribute = t.GetTypeInfo().GetCustomAttribute(); - builder.RegisterGeneric(t).Named(attribute.Name.ToString(), typeof(ICacheClient<>)).SingleInstance(); + var results = new List(); + foreach (var map in mapCollections) + { + object items = null; + if (map.Properties != null) items = map.Properties.Select(p => GetTypedPropertyValue(p)).ToArray(); + results.Add(new + { + Name = Convert.ChangeType(obj.Name, typeof(string)), + Value = Convert.ChangeType(map.Name, typeof(string)), + Items = items + }); + } + return results; } + else if (!string.IsNullOrEmpty(obj.Value)) + { + return new + { + Name = Convert.ChangeType(obj.Name ?? "", typeof(string)), + Value = Convert.ChangeType(obj.Value, typeof(string)), + }; + } + else if (!string.IsNullOrEmpty(obj.Ref)) + return Convert.ChangeType(obj.Ref, typeof(string)); + + return null; } + /// + /// The RegisterConfigInstance + /// + /// The builder private static void RegisterConfigInstance(ContainerBuilderWrapper builder) { var cacheWrapperSetting = AppConfig.Configuration.Get(); @@ -100,37 +140,22 @@ var mapsetting in catch { } } - private static object GetTypedPropertyValue(Property obj) + /// + /// The RegisterLocalInstance + /// + /// The typeName + /// The builder + private static void RegisterLocalInstance(string typeName, ContainerBuilderWrapper builder) { - var mapCollections = obj.Maps; - if (mapCollections != null && mapCollections.Any()) - { - var results = new List(); - foreach (var map in mapCollections) - { - object items = null; - if (map.Properties != null) items = map.Properties.Select(p => GetTypedPropertyValue(p)).ToArray(); - results.Add(new - { - Name = Convert.ChangeType(obj.Name, typeof(string)), - Value = Convert.ChangeType(map.Name, typeof(string)), - Items = items - }); - } - return results; - } - else if (!string.IsNullOrEmpty(obj.Value)) + var types = typeof(AppConfig) + .Assembly.GetTypes().Where(p => p.GetTypeInfo().GetInterface(typeName) != null); + foreach (var t in types) { - return new - { - Name = Convert.ChangeType(obj.Name ?? "", typeof(string)), - Value = Convert.ChangeType(obj.Value, typeof(string)), - }; + var attribute = t.GetTypeInfo().GetCustomAttribute(); + builder.RegisterGeneric(t).Named(attribute.Name.ToString(), typeof(ICacheClient<>)).SingleInstance(); } - else if (!string.IsNullOrEmpty(obj.Ref)) - return Convert.ChangeType(obj.Ref, typeof(string)); - - return null; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/CachingResources.Designer.cs b/src/Surging.Core/Surging.Core.Caching/CachingResources.Designer.cs index 8505a2a59..1c8e7115e 100644 --- a/src/Surging.Core/Surging.Core.Caching/CachingResources.Designer.cs +++ b/src/Surging.Core/Surging.Core.Caching/CachingResources.Designer.cs @@ -8,120 +8,167 @@ // //------------------------------------------------------------------------------ -namespace Surging.Core.Caching { +namespace Surging.Core.Caching +{ using System; using System.Reflection; - - + /// - /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// 一个强类型的资源类,用于查找本地化的字符串等。 /// - // 此类是由 StronglyTypedResourceBuilder - // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 - // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen - // (以 /str 作为命令选项),或重新生成 VS 项目。 [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class CachingResources { - - private static global::System.Resources.ResourceManager resourceMan; - + internal class CachingResources + { + #region 字段 + + /// + /// Defines the resourceCulture + /// private static global::System.Globalization.CultureInfo resourceCulture; - + + /// + /// Defines the resourceMan + /// + private static global::System.Resources.ResourceManager resourceMan; + + #endregion + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal CachingResources() { + internal CachingResources() + { } - + + #endregion + + #region 属性 + /// - /// 返回此类使用的缓存的 ResourceManager 实例。 + /// Gets the ArgumentIsNullOrWhitespace + /// 查找类似 "{0}"参数不能为空 的本地化字符串。 /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Surging.Core.Caching.CachingResources", typeof(CachingResources).GetTypeInfo().Assembly); - resourceMan = temp; - } - return resourceMan; + internal static string ArgumentIsNullOrWhitespace + { + get + { + return ResourceManager.GetString("ArgumentIsNullOrWhitespace", resourceCulture); } } - + /// - /// 使用此强类型资源类,为所有资源查找 + /// Gets or sets the Culture + /// 使用此强类型资源类,为所有资源查找 /// 重写当前线程的 CurrentUICulture 属性。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { + internal static global::System.Globalization.CultureInfo Culture + { + get + { return resourceCulture; } - set { + set + { resourceCulture = value; } } - - /// - /// 查找类似 "{0}"参数不能为空 的本地化字符串。 - /// - internal static string ArgumentIsNullOrWhitespace { - get { - return ResourceManager.GetString("ArgumentIsNullOrWhitespace", resourceCulture); - } - } - + /// - /// 查找类似 调用远程配置终结点发生错误: {0} - {1} 的本地化字符串。 + /// Gets the HttpException + /// 查找类似 调用远程配置终结点发生错误: {0} - {1} 的本地化字符串。 /// - internal static string HttpException { - get { + internal static string HttpException + { + get + { return ResourceManager.GetString("HttpException", resourceCulture); } } - + /// - /// 查找类似 {0} 不能以'{1}'结束 的本地化字符串。 + /// Gets the InvalidEndCharacter + /// 查找类似 {0} 不能以'{1}'结束 的本地化字符串。 /// - internal static string InvalidEndCharacter { - get { + internal static string InvalidEndCharacter + { + get + { return ResourceManager.GetString("InvalidEndCharacter", resourceCulture); } } - + /// - /// 查找类似 初始化配置{0}与{1}类型不匹配 的本地化字符串。 + /// Gets the InvalidProviderTypeException + /// 查找类似 初始化配置{0}与{1}类型不匹配 的本地化字符串。 /// - internal static string InvalidProviderTypeException { - get { + internal static string InvalidProviderTypeException + { + get + { return ResourceManager.GetString("InvalidProviderTypeException", resourceCulture); } } - + /// - /// 查找类似 {0} 不能以'{1}'开始 的本地化字符串。 + /// Gets the InvalidStartCharacter + /// 查找类似 {0} 不能以'{1}'开始 的本地化字符串。 /// - internal static string InvalidStartCharacter { - get { + internal static string InvalidStartCharacter + { + get + { return ResourceManager.GetString("InvalidStartCharacter", resourceCulture); } } - + /// - /// 查找类似 不能解析JSON文本,行号 '{0}': '{1}'. 的本地化字符串。 + /// Gets the JSONParseException + /// 查找类似 不能解析JSON文本,行号 '{0}': '{1}'. 的本地化字符串。 /// - internal static string JSONParseException { - get { + internal static string JSONParseException + { + get + { return ResourceManager.GetString("JSONParseException", resourceCulture); } } - + /// - /// 查找类似 此'{0}' JSON 令牌不支持,路径'{1}',行'{2}',位置'{3}' 的本地化字符串。 + /// Gets the ResourceManager + /// 返回此类使用的缓存的 ResourceManager 实例。 /// - internal static string UnsupportedJSONToken { - get { + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if (object.ReferenceEquals(resourceMan, null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Surging.Core.Caching.CachingResources", typeof(CachingResources).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Gets the UnsupportedJSONToken + /// 查找类似 此'{0}' JSON 令牌不支持,路径'{1}',行'{2}',位置'{3}' 的本地化字符串。 + /// + internal static string UnsupportedJSONToken + { + get + { return ResourceManager.GetString("UnsupportedJSONToken", resourceCulture); } } + + #endregion } } diff --git a/src/Surging.Core/Surging.Core.Caching/Configurations/CacheConfigurationExtensions.cs b/src/Surging.Core/Surging.Core.Caching/Configurations/CacheConfigurationExtensions.cs index 0e636812f..08d0d5188 100644 --- a/src/Surging.Core/Surging.Core.Caching/Configurations/CacheConfigurationExtensions.cs +++ b/src/Surging.Core/Surging.Core.Caching/Configurations/CacheConfigurationExtensions.cs @@ -8,28 +8,23 @@ namespace Surging.Core.Caching.Configurations { + /// + /// Defines the + /// public static class CacheConfigurationExtensionsstatic { - public static IConfigurationBuilder AddCacheFile(this IConfigurationBuilder builder, string path) - { - return AddCacheFile(builder, provider: null, path: path, basePath: null, optional: false, reloadOnChange: false); - } - - public static IConfigurationBuilder AddCacheFile(this IConfigurationBuilder builder, string path, bool optional) - { - return AddCacheFile(builder, provider: null, path: path, basePath: null, optional: optional, reloadOnChange: false); - } - - public static IConfigurationBuilder AddCacheFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange) - { - return AddCacheFile(builder, provider: null, path: path, basePath: null, optional: optional, reloadOnChange: reloadOnChange); - } - - public static IConfigurationBuilder AddCacheFile(this IConfigurationBuilder builder, string path, string basePath, bool optional, bool reloadOnChange) - { - return AddCacheFile(builder, provider: null, path: path, basePath: basePath, optional: optional, reloadOnChange: reloadOnChange); - } + #region 方法 + /// + /// The AddCacheFile + /// + /// The builder + /// The provider + /// The path + /// The basePath + /// The optional + /// The reloadOnChange + /// The public static IConfigurationBuilder AddCacheFile(this IConfigurationBuilder builder, IFileProvider provider, string path, string basePath, bool optional, bool reloadOnChange) { Check.NotNull(builder, "builder"); @@ -56,5 +51,57 @@ public static IConfigurationBuilder AddCacheFile(this IConfigurationBuilder buil AppConfig.Configuration = builder.Build(); return builder; } + + /// + /// The AddCacheFile + /// + /// The builder + /// The path + /// The + public static IConfigurationBuilder AddCacheFile(this IConfigurationBuilder builder, string path) + { + return AddCacheFile(builder, provider: null, path: path, basePath: null, optional: false, reloadOnChange: false); + } + + /// + /// The AddCacheFile + /// + /// The builder + /// The path + /// The optional + /// The + public static IConfigurationBuilder AddCacheFile(this IConfigurationBuilder builder, string path, bool optional) + { + return AddCacheFile(builder, provider: null, path: path, basePath: null, optional: optional, reloadOnChange: false); + } + + /// + /// The AddCacheFile + /// + /// The builder + /// The path + /// The optional + /// The reloadOnChange + /// The + public static IConfigurationBuilder AddCacheFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange) + { + return AddCacheFile(builder, provider: null, path: path, basePath: null, optional: optional, reloadOnChange: reloadOnChange); + } + + /// + /// The AddCacheFile + /// + /// The builder + /// The path + /// The basePath + /// The optional + /// The reloadOnChange + /// The + public static IConfigurationBuilder AddCacheFile(this IConfigurationBuilder builder, string path, string basePath, bool optional, bool reloadOnChange) + { + return AddCacheFile(builder, provider: null, path: path, basePath: basePath, optional: optional, reloadOnChange: reloadOnChange); + } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/Configurations/CacheConfigurationProvider.cs b/src/Surging.Core/Surging.Core.Caching/Configurations/CacheConfigurationProvider.cs index bc29e99fe..8b84c9ab7 100644 --- a/src/Surging.Core/Surging.Core.Caching/Configurations/CacheConfigurationProvider.cs +++ b/src/Surging.Core/Surging.Core.Caching/Configurations/CacheConfigurationProvider.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration; using Surging.Core.CPlatform.Configurations.Remote; using System; using System.Collections; @@ -8,9 +8,24 @@ namespace Surging.Core.Caching.Configurations { - class CacheConfigurationProvider : FileConfigurationProvider + /// + /// Defines the + /// + internal class CacheConfigurationProvider : FileConfigurationProvider { - public CacheConfigurationProvider(CacheConfigurationSource source) : base(source) { } + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The source + public CacheConfigurationProvider(CacheConfigurationSource source) : base(source) + { + } + + #endregion 构造函数 + + #region 方法 /// /// 重写数据转换方法 @@ -21,5 +36,7 @@ public override void Load(Stream stream) var parser = new JsonConfigurationParser(); this.Data = parser.Parse(stream, null); } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/Configurations/CacheConfigurationSource.cs b/src/Surging.Core/Surging.Core.Caching/Configurations/CacheConfigurationSource.cs index 5639b143b..a6d6a80eb 100644 --- a/src/Surging.Core/Surging.Core.Caching/Configurations/CacheConfigurationSource.cs +++ b/src/Surging.Core/Surging.Core.Caching/Configurations/CacheConfigurationSource.cs @@ -5,14 +5,33 @@ namespace Surging.Core.Caching.Configurations { + /// + /// Defines the + /// public class CacheConfigurationSource : FileConfigurationSource { + #region 属性 + + /// + /// Gets or sets the ConfigurationKeyPrefix + /// public string ConfigurationKeyPrefix { get; set; } + #endregion 属性 + + #region 方法 + + /// + /// The Build + /// + /// The builder + /// The public override IConfigurationProvider Build(IConfigurationBuilder builder) { FileProvider = FileProvider ?? builder.GetFileProvider(); return new CacheConfigurationProvider(this); } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/Configurations/IConfigurationWatchProvider.cs b/src/Surging.Core/Surging.Core.Caching/Configurations/IConfigurationWatchProvider.cs index 20af6a803..c6096547c 100644 --- a/src/Surging.Core/Surging.Core.Caching/Configurations/IConfigurationWatchProvider.cs +++ b/src/Surging.Core/Surging.Core.Caching/Configurations/IConfigurationWatchProvider.cs @@ -4,7 +4,14 @@ namespace Surging.Core.Caching.Configurations { - public interface IConfigurationWatchProvider + #region 接口 + + /// + /// Defines the + /// + public interface IConfigurationWatchProvider { } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/Configurations/Implementation/ConfigurationWatchProvider.cs b/src/Surging.Core/Surging.Core.Caching/Configurations/Implementation/ConfigurationWatchProvider.cs index 332362e74..b13c93788 100644 --- a/src/Surging.Core/Surging.Core.Caching/Configurations/Implementation/ConfigurationWatchProvider.cs +++ b/src/Surging.Core/Surging.Core.Caching/Configurations/Implementation/ConfigurationWatchProvider.cs @@ -1,19 +1,19 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Newtonsoft.Json; +using Surging.Core.Caching.HashAlgorithms; using Surging.Core.Caching.Models; +using Surging.Core.CPlatform; using Surging.Core.CPlatform.Cache; using Surging.Core.CPlatform.Cache.Implementation; +using Surging.Core.CPlatform.Configurations; +using Surging.Core.CPlatform.Configurations.Watch; using System; using System.Collections.Generic; +using System.Linq; using System.Text; using System.Threading.Tasks; -using System.Linq; -using Surging.Core.Caching.HashAlgorithms; -using Microsoft.Extensions.Options; -using Surging.Core.CPlatform.Configurations.Watch; -using Surging.Core.CPlatform.Configurations; -using Surging.Core.CPlatform; -using Newtonsoft.Json; namespace Surging.Core.Caching.Configurations.Implementation { @@ -22,16 +22,40 @@ namespace Surging.Core.Caching.Configurations.Implementation /// public class ConfigurationWatchProvider : ConfigurationWatch, IConfigurationWatchProvider { - #region Field + #region 字段 + + /// + /// Defines the _cachingProvider + /// + private readonly CachingProvider _cachingProvider; + + /// + /// Defines the _logger + /// private readonly ILogger _logger; + + /// + /// Defines the _serviceCacheManager + /// private readonly IServiceCacheManager _serviceCacheManager; - private readonly CachingProvider _cachingProvider; + + /// + /// Defines the queue + /// private Queue queue = new Queue(); - #endregion + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serviceProvider + /// The logger + /// The serviceCacheManager public ConfigurationWatchProvider(CPlatformContainer serviceProvider, ILogger logger, IServiceCacheManager serviceCacheManager) { - if (serviceProvider.IsRegistered()) serviceProvider.GetInstances().Register(this); _logger = logger; @@ -42,17 +66,27 @@ public ConfigurationWatchProvider(CPlatformContainer serviceProvider, ILogger + /// The Process + /// + /// The + public override async Task Process() { - SaveConfiguration(e.Cache); + if (this.queue.Count > 0 && this.queue.Dequeue()) + { + var jsonString = JsonConvert.SerializeObject(_cachingProvider); + await System.IO.File.WriteAllTextAsync(AppConfig.Path, jsonString); + } } + /// + /// The SaveConfiguration + /// + /// The cache private void SaveConfiguration(ServiceCache cache) { if (this.queue.Count > 0) this.queue.Dequeue(); @@ -79,7 +113,6 @@ private void SaveConfiguration(ServiceCache cache) { Value = $"{hashNode.Host}:{hashNode.Port}::{hashNode.Db}" }; - }).ToList(); }); }); @@ -87,13 +120,26 @@ private void SaveConfiguration(ServiceCache cache) } } - public override async Task Process() - { - if (this.queue.Count>0 && this.queue.Dequeue()) - { - var jsonString = JsonConvert.SerializeObject(_cachingProvider); - await System.IO.File.WriteAllTextAsync(AppConfig.Path, jsonString); - } + /// + /// The ServiceCacheManager_Add + /// + /// The sender + /// The e + private void ServiceCacheManager_Add(object sender, ServiceCacheEventArgs e) + { + SaveConfiguration(e.Cache); } + + /// + /// The ServiceCacheManager_Removed + /// + /// The sender + /// The e + private void ServiceCacheManager_Removed(object sender, ServiceCacheEventArgs e) + { + SaveConfiguration(e.Cache); + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/Configurations/Remote/IConfigurationParser.cs b/src/Surging.Core/Surging.Core.Caching/Configurations/Remote/IConfigurationParser.cs index 2bf6c6489..7cae517a8 100644 --- a/src/Surging.Core/Surging.Core.Caching/Configurations/Remote/IConfigurationParser.cs +++ b/src/Surging.Core/Surging.Core.Caching/Configurations/Remote/IConfigurationParser.cs @@ -5,11 +5,15 @@ namespace Surging.Core.Caching.Configurations.Remote { + #region 接口 + /// - /// 对于配置进行解析 + /// 对于配置进行解析 /// public interface IConfigurationParser { + #region 方法 + /// /// 对于配置信息解析成键值对集合 /// @@ -17,5 +21,9 @@ public interface IConfigurationParser /// 配置信息KEY前缀 /// 返回键值对泛型集合 IDictionary Parse(Stream input, string initialContext); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/Configurations/Remote/JsonConfigurationParser.cs b/src/Surging.Core/Surging.Core.Caching/Configurations/Remote/JsonConfigurationParser.cs index 906041d5a..35b98d5f8 100644 --- a/src/Surging.Core/Surging.Core.Caching/Configurations/Remote/JsonConfigurationParser.cs +++ b/src/Surging.Core/Surging.Core.Caching/Configurations/Remote/JsonConfigurationParser.cs @@ -10,14 +10,43 @@ namespace Surging.Core.Caching.Configurations.Remote { - public class JsonConfigurationParser : IConfigurationParser + /// + /// Defines the + /// + public class JsonConfigurationParser : IConfigurationParser { - private readonly IDictionary _data = new SortedDictionary(StringComparer.OrdinalIgnoreCase); + #region 字段 + + /// + /// Defines the _context + /// private readonly Stack _context = new Stack(); + + /// + /// Defines the _data + /// + private readonly IDictionary _data = new SortedDictionary(StringComparer.OrdinalIgnoreCase); + + /// + /// Defines the _currentPath + /// private string _currentPath; + /// + /// Defines the _reader + /// private JsonTextReader _reader; - + + #endregion 字段 + + #region 方法 + + /// + /// The Parse + /// + /// The input + /// The initialContext + /// The public IDictionary Parse(Stream input, string initialContext) { try @@ -51,9 +80,82 @@ public IDictionary Parse(Stream input, string initialContext) errorLine), e); } + } + + /// + /// The ReadLines + /// + /// The streamReader + /// The + private static IEnumerable ReadLines(StreamReader streamReader) + { + string line; + do + { + line = streamReader.ReadLine(); + yield return line; + } while (line != null); + } + + /// + /// The RetrieveErrorContext + /// + /// The e + /// The fileContent + /// The + private static string RetrieveErrorContext(JsonReaderException e, IEnumerable fileContent) + { + string errorLine; + if (e.LineNumber >= 2) + { + var errorContext = fileContent.Skip(e.LineNumber - 2).Take(2).ToList(); + errorLine = errorContext[0].Trim() + Environment.NewLine + errorContext[1].Trim(); + } + else + { + var possibleLineContent = fileContent.Skip(e.LineNumber - 1).FirstOrDefault(); + errorLine = possibleLineContent ?? string.Empty; + } + return errorLine; + } + /// + /// The EnterContext + /// + /// The context + private void EnterContext(string context) + { + _context.Push(context); + _currentPath = ConfigurationPath.Combine(_context.Reverse()); } + /// + /// The ExitContext + /// + private void ExitContext() + { + _context.Pop(); + _currentPath = ConfigurationPath.Combine(_context.Reverse()); + } + + /// + /// The VisitArray + /// + /// The array + private void VisitArray(JArray array) + { + for (int index = 0; index < array.Count; index++) + { + EnterContext(index.ToString()); + VisitToken(array[index]); + ExitContext(); + } + } + + /// + /// The VisitJObject + /// + /// The jObject private void VisitJObject(JObject jObject) { foreach (var property in jObject.Properties()) @@ -64,11 +166,30 @@ private void VisitJObject(JObject jObject) } } + /// + /// The VisitPrimitive + /// + /// The data + private void VisitPrimitive(JToken data) + { + var key = _currentPath; + Check.CheckCondition(() => _data.ContainsKey(key), "key"); + _data[key] = data.ToString(); + } + + /// + /// The VisitProperty + /// + /// The property private void VisitProperty(JProperty property) { VisitToken(property.Value); } + /// + /// The VisitToken + /// + /// The token private void VisitToken(JToken token) { switch (token.Type) @@ -101,59 +222,6 @@ private void VisitToken(JToken token) } } - private void VisitArray(JArray array) - { - for (int index = 0; index < array.Count; index++) - { - EnterContext(index.ToString()); - VisitToken(array[index]); - ExitContext(); - } - } - - private void VisitPrimitive(JToken data) - { - var key = _currentPath; - Check.CheckCondition(() => _data.ContainsKey(key), "key"); - _data[key] = data.ToString(); - } - - private void EnterContext(string context) - { - _context.Push(context); - _currentPath = ConfigurationPath.Combine(_context.Reverse()); - } - - private void ExitContext() - { - _context.Pop(); - _currentPath = ConfigurationPath.Combine(_context.Reverse()); - } - - private static IEnumerable ReadLines(StreamReader streamReader) - { - string line; - do - { - line = streamReader.ReadLine(); - yield return line; - } while (line != null); - } - - private static string RetrieveErrorContext(JsonReaderException e, IEnumerable fileContent) - { - string errorLine; - if (e.LineNumber >= 2) - { - var errorContext = fileContent.Skip(e.LineNumber - 2).Take(2).ToList(); - errorLine = errorContext[0].Trim() + Environment.NewLine + errorContext[1].Trim(); - } - else - { - var possibleLineContent = fileContent.Skip(e.LineNumber - 1).FirstOrDefault(); - errorLine = possibleLineContent ?? string.Empty; - } - return errorLine; - } + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/Configurations/Remote/RemoteConfigurationEvents.cs b/src/Surging.Core/Surging.Core.Caching/Configurations/Remote/RemoteConfigurationEvents.cs index 2131fc3d7..f6d66302b 100644 --- a/src/Surging.Core/Surging.Core.Caching/Configurations/Remote/RemoteConfigurationEvents.cs +++ b/src/Surging.Core/Surging.Core.Caching/Configurations/Remote/RemoteConfigurationEvents.cs @@ -5,14 +5,40 @@ namespace Surging.Core.Caching.Configurations.Remote { - public class RemoteConfigurationEvents + /// + /// Defines the + /// + public class RemoteConfigurationEvents { - public Action OnSendingRequest { get; set; } = msg => { }; + #region 属性 + /// + /// Gets or sets the OnDataParsed + /// public Func, IDictionary> OnDataParsed { get; set; } = data => data; - - public virtual void SendingRequest(HttpRequestMessage msg) => OnSendingRequest(msg); + /// + /// Gets or sets the OnSendingRequest + /// + public Action OnSendingRequest { get; set; } = msg => { }; + + #endregion 属性 + + #region 方法 + + /// + /// The DataParsed + /// + /// The data + /// The public virtual IDictionary DataParsed(IDictionary data) => OnDataParsed(data); + + /// + /// The SendingRequest + /// + /// The msg + public virtual void SendingRequest(HttpRequestMessage msg) => OnSendingRequest(msg); + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/Configurations/Remote/RemoteConfigurationExtensions.cs b/src/Surging.Core/Surging.Core.Caching/Configurations/Remote/RemoteConfigurationExtensions.cs index ee607e367..a977af276 100644 --- a/src/Surging.Core/Surging.Core.Caching/Configurations/Remote/RemoteConfigurationExtensions.cs +++ b/src/Surging.Core/Surging.Core.Caching/Configurations/Remote/RemoteConfigurationExtensions.cs @@ -10,8 +10,32 @@ namespace Surging.Core.Caching.Configurations.Remote /// public static class RemoteConfigurationExtensions { + #region 方法 + + /// + /// 对于 添加远程配置方法 + /// + /// + /// + /// + public static IConfigurationBuilder AddRemoteSource(this IConfigurationBuilder builder, RemoteConfigurationSource source) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + builder.Add(source); + return builder; + } + /// - ///对于 添加远程配置方法 + /// 对于 添加远程配置方法 /// /// /// 远程地址 @@ -22,7 +46,7 @@ public static IConfigurationBuilder AddRemoteSource(this IConfigurationBuilder b } /// - ///对于 添加远程配置方法 + /// 对于 添加远程配置方法 /// /// /// 远程地址 @@ -50,11 +74,11 @@ public static IConfigurationBuilder AddRemoteSource(this IConfigurationBuilder b } /// - ///对于 添加远程配置方法 + /// 对于 添加远程配置方法 /// /// /// 远程地址 - /// 远程配置源是否可选 + /// 远程配置源是否可选 /// 添加事件 /// public static IConfigurationBuilder AddRemoteSource(this IConfigurationBuilder builder, Uri configurationUri, bool optional, RemoteConfigurationEvents events) @@ -80,27 +104,6 @@ public static IConfigurationBuilder AddRemoteSource(this IConfigurationBuilder b return builder.AddRemoteSource(source); } - - /// - ///对于 添加远程配置方法 - /// - /// - /// - /// - public static IConfigurationBuilder AddRemoteSource(this IConfigurationBuilder builder, RemoteConfigurationSource source) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (source == null) - { - throw new ArgumentNullException(nameof(source)); - } - - builder.Add(source); - return builder; - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/Configurations/Remote/RemoteConfigurationProvider.cs b/src/Surging.Core/Surging.Core.Caching/Configurations/Remote/RemoteConfigurationProvider.cs index 4b200e6a9..19047a8e2 100644 --- a/src/Surging.Core/Surging.Core.Caching/Configurations/Remote/RemoteConfigurationProvider.cs +++ b/src/Surging.Core/Surging.Core.Caching/Configurations/Remote/RemoteConfigurationProvider.cs @@ -8,30 +8,59 @@ namespace Surging.Core.Caching.Configurations.Remote { - class RemoteConfigurationProvider : ConfigurationProvider + /// + /// Defines the + /// + internal class RemoteConfigurationProvider : ConfigurationProvider { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The source public RemoteConfigurationProvider(RemoteConfigurationSource source) { Check.NotNull(source, "source"); if (!string.IsNullOrEmpty(source.ConfigurationKeyPrefix)) { Check.CheckCondition(() => source.ConfigurationKeyPrefix.Trim().StartsWith(":"), CachingResources.InvalidStartCharacter, "source.ConfigurationKeyPrefix", ":"); - Check.CheckCondition(() => source.ConfigurationKeyPrefix.Trim().EndsWith(":"), CachingResources.InvalidEndCharacter, "source.ConfigurationKeyPrefix",":"); + Check.CheckCondition(() => source.ConfigurationKeyPrefix.Trim().EndsWith(":"), CachingResources.InvalidEndCharacter, "source.ConfigurationKeyPrefix", ":"); } Source = source; Backchannel = new HttpClient(source.BackchannelHttpHandler ?? new HttpClientHandler()); Backchannel.DefaultRequestHeaders.UserAgent.ParseAdd("获取CacheConfiugration信息"); Backchannel.Timeout = source.BackchannelTimeout; - Backchannel.MaxResponseContentBufferSize = 1024 * 1024 * 10; + Backchannel.MaxResponseContentBufferSize = 1024 * 1024 * 10; Parser = source.Parser ?? new JsonConfigurationParser(); } - public RemoteConfigurationSource Source { get; } + #endregion 构造函数 - public IConfigurationParser Parser { get; } + #region 属性 + /// + /// Gets the Backchannel + /// public HttpClient Backchannel { get; } - + + /// + /// Gets the Parser + /// + public IConfigurationParser Parser { get; } + + /// + /// Gets the Source + /// + public RemoteConfigurationSource Source { get; } + + #endregion 属性 + + #region 方法 + + /// + /// The Load + /// public override void Load() { var requestMessage = new HttpRequestMessage(HttpMethod.Get, Source.ConfigurationUri); @@ -68,5 +97,7 @@ public override void Load() } } } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/Configurations/Remote/RemoteConfigurationSource.cs b/src/Surging.Core/Surging.Core.Caching/Configurations/Remote/RemoteConfigurationSource.cs index 362b9ae2a..575008b64 100644 --- a/src/Surging.Core/Surging.Core.Caching/Configurations/Remote/RemoteConfigurationSource.cs +++ b/src/Surging.Core/Surging.Core.Caching/Configurations/Remote/RemoteConfigurationSource.cs @@ -6,54 +6,75 @@ namespace Surging.Core.Caching.Configurations.Remote { - public class RemoteConfigurationSource : IConfigurationSource + /// + /// Defines the + /// + public class RemoteConfigurationSource : IConfigurationSource { + #region 属性 + /// - /// The uri to call to fetch + /// Gets or sets the BackchannelHttpHandler + /// The HttpMessageHandler used to communicate with remote configuration provider. /// - public Uri ConfigurationUri { get; set; } + public HttpMessageHandler BackchannelHttpHandler { get; set; } /// - /// Determines if the remote source is optional + /// Gets or sets the BackchannelTimeout + /// Gets or sets timeout value in milliseconds for back channel communications with the remote identity provider. /// - public bool Optional { get; set; } + public TimeSpan BackchannelTimeout { get; set; } = TimeSpan.FromSeconds(60); /// - /// The HttpMessageHandler used to communicate with remote configuration provider. + /// Gets or sets the ConfigurationKeyPrefix + /// If provided, keys loaded from endpoint will be prefixed with the provided value /// - public HttpMessageHandler BackchannelHttpHandler { get; set; } + public string ConfigurationKeyPrefix { get; set; } /// - /// Gets or sets timeout value in milliseconds for back channel communications with the remote identity provider. + /// Gets or sets the ConfigurationUri + /// The uri to call to fetch /// - /// - /// The back channel timeout. - /// - public TimeSpan BackchannelTimeout { get; set; } = TimeSpan.FromSeconds(60); + public Uri ConfigurationUri { get; set; } /// - /// Parser for parsing the returned data into the required configuration source + /// Gets or sets the Events + /// Events providing hooks into the remote call /// - public IConfigurationParser Parser { get; set; } + public RemoteConfigurationEvents Events { get; set; } = new RemoteConfigurationEvents(); /// + /// Gets or sets the MediaType /// The accept header used to create a MediaTypeWithQualityHeaderValue /// public string MediaType { get; set; } = "application/json"; /// - /// Events providing hooks into the remote call + /// Gets or sets a value indicating whether Optional + /// Determines if the remote source is optional /// - public RemoteConfigurationEvents Events { get; set; } = new RemoteConfigurationEvents(); + public bool Optional { get; set; } /// - /// If provided, keys loaded from endpoint will be prefixed with the provided value + /// Gets or sets the Parser + /// Parser for parsing the returned data into the required configuration source /// - public string ConfigurationKeyPrefix { get; set; } + public IConfigurationParser Parser { get; set; } + + #endregion 属性 + + #region 方法 + /// + /// The Build + /// + /// The builder + /// The public IConfigurationProvider Build(IConfigurationBuilder builder) { return new RemoteConfigurationProvider(this); } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/ContainerBuilderExtensions.cs b/src/Surging.Core/Surging.Core.Caching/ContainerBuilderExtensions.cs index 7bf0d3250..3dea92624 100644 --- a/src/Surging.Core/Surging.Core.Caching/ContainerBuilderExtensions.cs +++ b/src/Surging.Core/Surging.Core.Caching/ContainerBuilderExtensions.cs @@ -22,14 +22,23 @@ namespace Surging.Core.Caching { /// - /// 容器生成扩展 + /// 容器生成扩展 /// public static class ContainerBuilderExtensions { + #region 常量 + + /// + /// Defines the CacheSectionName + /// private const string CacheSectionName = "CachingProvider"; + #endregion 常量 + + #region 方法 + /// - /// 附加缓存注入 + /// 附加缓存注入 /// /// 服务构建者 /// 服务构建者 @@ -48,23 +57,45 @@ public static IServiceBuilder AddCache(this IServiceBuilder builder) } /// - /// 注册本地实例 + /// 获取类型的属性值 /// - /// - /// - private static void RegisterLocalInstance(string typeName, ContainerBuilder services) + /// + /// + private static object GetTypedPropertyValue(Property obj) { - var types = typeof(AppConfig) - .Assembly.GetTypes().Where(p => p.GetTypeInfo().GetInterface(typeName) != null); - foreach (var t in types) + var mapCollections = obj.Maps; + if (mapCollections != null && mapCollections.Any()) { - var attribute = t.GetTypeInfo().GetCustomAttribute(); - services.RegisterGeneric(t).Named(attribute.Name.ToString(), typeof(ICacheClient<>)).SingleInstance(); + var results = new List(); + foreach (var map in mapCollections) + { + object items = null; + if (map.Properties != null) items = map.Properties.Select(p => GetTypedPropertyValue(p)).ToArray(); + results.Add(new + { + Name = Convert.ChangeType(obj.Name, typeof(string)), + Value = Convert.ChangeType(map.Name, typeof(string)), + Items = items + }); + } + return results; } + else if (!string.IsNullOrEmpty(obj.Value)) + { + return new + { + Name = Convert.ChangeType(obj.Name ?? "", typeof(string)), + Value = Convert.ChangeType(obj.Value, typeof(string)), + }; + } + else if (!string.IsNullOrEmpty(obj.Ref)) + return Convert.ChangeType(obj.Ref, typeof(string)); + + return null; } /// - /// 注册配置实例 + /// 注册配置实例 /// /// private static void RegisterConfigInstance(ContainerBuilder services) @@ -108,41 +139,21 @@ var mapsetting in } /// - /// 获取类型的属性值 + /// 注册本地实例 /// - /// - /// - private static object GetTypedPropertyValue(Property obj) + /// + /// + private static void RegisterLocalInstance(string typeName, ContainerBuilder services) { - var mapCollections = obj.Maps; - if (mapCollections != null && mapCollections.Any()) - { - var results = new List(); - foreach (var map in mapCollections) - { - object items = null; - if (map.Properties != null) items = map.Properties.Select(p => GetTypedPropertyValue(p)).ToArray(); - results.Add(new - { - Name = Convert.ChangeType(obj.Name, typeof(string)), - Value = Convert.ChangeType(map.Name, typeof(string)), - Items = items - }); - } - return results; - } - else if (!string.IsNullOrEmpty(obj.Value)) + var types = typeof(AppConfig) + .Assembly.GetTypes().Where(p => p.GetTypeInfo().GetInterface(typeName) != null); + foreach (var t in types) { - return new - { - Name = Convert.ChangeType(obj.Name ?? "", typeof(string)), - Value = Convert.ChangeType(obj.Value, typeof(string)), - }; + var attribute = t.GetTypeInfo().GetCustomAttribute(); + services.RegisterGeneric(t).Named(attribute.Name.ToString(), typeof(ICacheClient<>)).SingleInstance(); } - else if (!string.IsNullOrEmpty(obj.Ref)) - return Convert.ChangeType(obj.Ref, typeof(string)); - - return null; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/DependencyResolution/DependencyResolverExtensions.cs b/src/Surging.Core/Surging.Core.Caching/DependencyResolution/DependencyResolverExtensions.cs index a6da9c186..c917bd49b 100644 --- a/src/Surging.Core/Surging.Core.Caching/DependencyResolution/DependencyResolverExtensions.cs +++ b/src/Surging.Core/Surging.Core.Caching/DependencyResolution/DependencyResolverExtensions.cs @@ -9,38 +9,34 @@ namespace Surging.Core.Caching.DependencyResolution /// /// 扩展依赖注入IOC容器 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public static class DependencyResolverExtensions { - #region 公共方法 + #region 方法 /// - /// 通过KEY获取实例 + /// 获取实例 /// /// 目标类型 /// IOC对象容器 - /// 键 /// 返回实例 - public static T GetService(this IDependencyResolver resolver, object key) + public static T GetService(this IDependencyResolver resolver) { Check.NotNull(resolver, "resolver"); - - return (T)resolver.GetService(typeof(T), key); + return (T)resolver.GetService(typeof(T), null); } /// - /// 获取实例 + /// 通过KEY获取实例 /// /// 目标类型 /// IOC对象容器 + /// 键 /// 返回实例 - public static T GetService(this IDependencyResolver resolver) + public static T GetService(this IDependencyResolver resolver, object key) { Check.NotNull(resolver, "resolver"); - return (T)resolver.GetService(typeof(T), null); + + return (T)resolver.GetService(typeof(T), key); } /// @@ -57,28 +53,28 @@ public static object GetService(this IDependencyResolver resolver, Type type) } /// - /// 通过KEY获取集合 + /// 获取集合 /// /// 目标类型 /// IOC对象容器 - /// 键 - /// 返回实例 - public static IEnumerable GetServices(this IDependencyResolver resolver, object key) + /// 返回集合 + public static IEnumerable GetServices(this IDependencyResolver resolver) { Check.NotNull(resolver, "resolver"); - return resolver.GetServices(typeof(T), key).OfType(); + return resolver.GetServices(typeof(T), null).OfType(); } /// - /// 获取集合 + /// 通过KEY获取集合 /// /// 目标类型 /// IOC对象容器 - /// 返回集合 - public static IEnumerable GetServices(this IDependencyResolver resolver) + /// 键 + /// 返回实例 + public static IEnumerable GetServices(this IDependencyResolver resolver, object key) { Check.NotNull(resolver, "resolver"); - return resolver.GetServices(typeof(T), null).OfType(); + return resolver.GetServices(typeof(T), key).OfType(); } /// @@ -94,8 +90,6 @@ public static IEnumerable GetServices(this IDependencyResolver resolver, return resolver.GetServices(type, null); } - #endregion - /// /// 通过KEY和TYPE获取实例对象集合 /// @@ -103,10 +97,6 @@ public static IEnumerable GetServices(this IDependencyResolver resolver, /// 类型 /// 键 /// 返回实例对象集合 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// internal static IEnumerable GetServiceAsServices(this IDependencyResolver resolver, Type type, object key) { @@ -115,5 +105,7 @@ internal static IEnumerable GetServiceAsServices(this IDependencyResolve var service = resolver.GetService(type, key); return service == null ? Enumerable.Empty() : new[] { service }; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/DependencyResolution/IDependencyResolver.cs b/src/Surging.Core/Surging.Core.Caching/DependencyResolution/IDependencyResolver.cs index 74083d539..0d77d0cc0 100644 --- a/src/Surging.Core/Surging.Core.Caching/DependencyResolution/IDependencyResolver.cs +++ b/src/Surging.Core/Surging.Core.Caching/DependencyResolution/IDependencyResolver.cs @@ -4,25 +4,21 @@ namespace Surging.Core.Caching.DependencyResolution { + #region 接口 + /// /// 注入IOC容器接口 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public interface IDependencyResolver { + #region 方法 + /// /// 通过KEY和TYPE获取实例对象 /// /// 类型 /// 键 /// 返回实例对象 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// object GetService(Type type, object key); /// @@ -31,10 +27,10 @@ public interface IDependencyResolver /// 类型 /// 键 /// 返回实例对象 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// IEnumerable GetServices(Type type, object key); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/DependencyResolution/ServiceResolver.cs b/src/Surging.Core/Surging.Core.Caching/DependencyResolution/ServiceResolver.cs index 6549c7fd8..b8afcbf7d 100644 --- a/src/Surging.Core/Surging.Core.Caching/DependencyResolution/ServiceResolver.cs +++ b/src/Surging.Core/Surging.Core.Caching/DependencyResolution/ServiceResolver.cs @@ -10,63 +10,44 @@ namespace Surging.Core.Caching.DependencyResolution /// /// IOC容器对象 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public class ServiceResolver : IDependencyResolver { #region 字段 + + /// + /// Defines the _defaultInstance + /// private static readonly ServiceResolver _defaultInstance = new ServiceResolver(); - private readonly ConcurrentDictionary, object> _initializers = - new ConcurrentDictionary, object>(); - #endregion - #region 公共方法 /// - /// 注册对象添加到IOC容器 + /// Defines the _initializers /// - /// 键 - /// 值 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public virtual void Register(string key, object value) - { - DebugCheck.NotNull(value); - // DebugCheck.NotNull(key); + private readonly ConcurrentDictionary, object> _initializers = + new ConcurrentDictionary, object>(); - _initializers.GetOrAdd(Tuple.Create(value.GetType(), key), value); - var interFaces = value.GetType().GetTypeInfo().GetInterfaces(); - foreach (var interFace in interFaces) - { - _initializers.GetOrAdd(Tuple.Create(interFace, key), value); - } - } + #endregion 字段 + + #region 属性 /// + /// Gets the Current /// 返回当前IOC容器 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public static ServiceResolver Current { get { return _defaultInstance; } } + #endregion 属性 + + #region 方法 + /// /// 通过KEY和TYPE获取实例对象 /// /// 类型 /// 键 /// 返回实例对象 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public virtual object GetService(Type type, object key) { object result; @@ -80,15 +61,29 @@ public virtual object GetService(Type type, object key) /// 类型 /// 键 /// 返回实例对象 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public IEnumerable GetServices(Type type, object key) { return this.GetServiceAsServices(type, key); } - #endregion - } -} + /// + /// 注册对象添加到IOC容器 + /// + /// 键 + /// 值 + public virtual void Register(string key, object value) + { + DebugCheck.NotNull(value); + // DebugCheck.NotNull(key); + + _initializers.GetOrAdd(Tuple.Create(value.GetType(), key), value); + var interFaces = value.GetType().GetTypeInfo().GetInterfaces(); + foreach (var interFace in interFaces) + { + _initializers.GetOrAdd(Tuple.Create(interFace, key), value); + } + } + + #endregion 方法 + } +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/HashAlgorithms/ConsistentHash.cs b/src/Surging.Core/Surging.Core.Caching/HashAlgorithms/ConsistentHash.cs index 33a852505..a601b506f 100644 --- a/src/Surging.Core/Surging.Core.Caching/HashAlgorithms/ConsistentHash.cs +++ b/src/Surging.Core/Surging.Core.Caching/HashAlgorithms/ConsistentHash.cs @@ -8,59 +8,98 @@ namespace Surging.Core.Caching.HashAlgorithms /// 针对哈希算法实现 /// /// 类型 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public class ConsistentHash { #region 字段 - private readonly SortedDictionary _ring = new SortedDictionary(); - private int[] _nodeKeysInRing = null; + + /// + /// Defines the _hashAlgorithm + /// private readonly IHashAlgorithm _hashAlgorithm; + + /// + /// Defines the _ring + /// + private readonly SortedDictionary _ring = new SortedDictionary(); + + /// + /// Defines the _virtualNodeReplicationFactor + /// private readonly int _virtualNodeReplicationFactor = 1000; - #endregion + /// + /// Defines the _nodeKeysInRing + /// + private int[] _nodeKeysInRing = null; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The hashAlgorithm public ConsistentHash(IHashAlgorithm hashAlgorithm) { _hashAlgorithm = hashAlgorithm; } + /// + /// Initializes a new instance of the class. + /// + /// The hashAlgorithm + /// The virtualNodeReplicationFactor public ConsistentHash(IHashAlgorithm hashAlgorithm, int virtualNodeReplicationFactor) : this(hashAlgorithm) { _virtualNodeReplicationFactor = virtualNodeReplicationFactor; } + #endregion 构造函数 + #region 属性 + /// + /// Gets the VirtualNodeReplicationFactor /// 复制哈希节点数 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public int VirtualNodeReplicationFactor { get { return _virtualNodeReplicationFactor; } } - #endregion + #endregion 属性 + + #region 方法 /// /// 添加节点 /// /// 节点 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// + /// The value public void Add(T node, string value) { AddNode(node, value); _nodeKeysInRing = _ring.Keys.ToArray(); } + /// + /// 通过哈希算法计算出对应的节点 + /// + /// 值 + /// 返回节点 + public T GetItemNode(string item) + { + var hashOfItem = _hashAlgorithm.Hash(item); + var nearestNodePosition = GetClockwiseNearestNode(_nodeKeysInRing, hashOfItem); + return _ring[_nodeKeysInRing[nearestNodePosition]]; + } + + /// + /// The GetNodes + /// + /// The public IEnumerable GetNodes() { return _ring.Values.Distinct().ToList(); @@ -70,40 +109,17 @@ public IEnumerable GetNodes() /// 删除节点 /// /// 节点 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public void Remove(string node) { RemoveNode(node); _nodeKeysInRing = _ring.Keys.ToArray(); } - /// - /// 通过哈希算法计算出对应的节点 - /// - /// 值 - /// 返回节点 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public T GetItemNode(string item) - { - var hashOfItem = _hashAlgorithm.Hash(item); - var nearestNodePosition = GetClockwiseNearestNode(_nodeKeysInRing, hashOfItem); - return _ring[_nodeKeysInRing[nearestNodePosition]]; - } - /// /// 添加节点 /// /// 节点 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// + /// The value private void AddNode(T node, string value) { for (var i = 0; i < _virtualNodeReplicationFactor; i++) @@ -113,34 +129,12 @@ private void AddNode(T node, string value) } } - /// - /// 删除节点 - /// - /// 节点 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - private void RemoveNode(string value) - { - for (var i = 0; i < _virtualNodeReplicationFactor; i++) - { - var hashOfVirtualNode = _hashAlgorithm.Hash(value.ToString() + i); - _ring.Remove(hashOfVirtualNode); - } - } - - /// /// 顺时针查找对应哈希的位置 /// /// 键集合数 /// 哈希值 /// 返回哈希的位置 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// private int GetClockwiseNearestNode(int[] keys, int hashOfItem) { var begin = 0; @@ -158,5 +152,20 @@ private int GetClockwiseNearestNode(int[] keys, int hashOfItem) } return end; } + + /// + /// 删除节点 + /// + /// The value + private void RemoveNode(string value) + { + for (var i = 0; i < _virtualNodeReplicationFactor; i++) + { + var hashOfVirtualNode = _hashAlgorithm.Hash(value.ToString() + i); + _ring.Remove(hashOfVirtualNode); + } + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/HashAlgorithms/ConsistentHashNode.cs b/src/Surging.Core/Surging.Core.Caching/HashAlgorithms/ConsistentHashNode.cs index d47a2a4d2..4d895d4ca 100644 --- a/src/Surging.Core/Surging.Core.Caching/HashAlgorithms/ConsistentHashNode.cs +++ b/src/Surging.Core/Surging.Core.Caching/HashAlgorithms/ConsistentHashNode.cs @@ -8,70 +8,39 @@ namespace Surging.Core.Caching.HashAlgorithms /// /// 哈希节点对象 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public class ConsistentHashNode: CacheEndpoint + public class ConsistentHashNode : CacheEndpoint { - /// - /// 缓存目标类型 - /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public CacheTargetType Type - { - get; - set; - } + #region 字段 /// - /// 主机 + /// Defines the _maxSize /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public new string Host { get; set; } + private string _maxSize = "50"; /// - /// 端口 + /// Defines the _minSize /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public new string Port { get; set; } + private string _minSize = "1"; + + #endregion 字段 + + #region 属性 /// - /// 用户名 + /// Gets or sets the Db + /// 数据库 /// - public string UserName { get; set; } + public string Db { get; set; } /// - /// 密码 + /// Gets or sets the Host + /// 主机 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public string Password { get; set; } + public new string Host { get; set; } /// - /// 数据库 + /// Gets or sets the MaxSize /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public string Db - { - get; set; - } - - private string _maxSize = "50"; public string MaxSize { get @@ -87,7 +56,9 @@ public string MaxSize } } - private string _minSize = "1"; + /// + /// Gets or sets the MinSize + /// public string MinSize { get @@ -103,9 +74,43 @@ public string MinSize } } + /// + /// Gets or sets the Password + /// 密码 + /// + public string Password { get; set; } + + /// + /// Gets or sets the Port + /// 端口 + /// + public new string Port { get; set; } + + /// + /// Gets or sets the Type + /// 缓存目标类型 + /// + public CacheTargetType Type { get; set; } + + /// + /// Gets or sets the UserName + /// 用户名 + /// + public string UserName { get; set; } + + #endregion 属性 + + #region 方法 + + /// + /// The ToString + /// + /// The public override string ToString() { return string.Concat(new string[] { Host, ":", Port.ToString() }); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/HashAlgorithms/HashAlgorithm.cs b/src/Surging.Core/Surging.Core.Caching/HashAlgorithms/HashAlgorithm.cs index ae15ecd7c..f04f5e3ad 100644 --- a/src/Surging.Core/Surging.Core.Caching/HashAlgorithms/HashAlgorithm.cs +++ b/src/Surging.Core/Surging.Core.Caching/HashAlgorithms/HashAlgorithm.cs @@ -7,26 +7,30 @@ namespace Surging.Core.Caching.HashAlgorithms /// /// 一致性哈希算法 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public class HashAlgorithm : IHashAlgorithm { - #region 构造函数 - public int Hash(string item) - { - var hash = Hash(Encoding.ASCII.GetBytes(item)); - return (int)hash; - } - #endregion - #region 常量 + + /// + /// Defines the m + /// private const UInt32 m = 0x5bd1e995; + + /// + /// Defines the r + /// private const Int32 r = 24; - #endregion - #region 公共方法 + #endregion 常量 + + #region 方法 + + /// + /// The Hash + /// + /// The data + /// The seed + /// The public static UInt32 Hash(Byte[] data, UInt32 seed = 0xc58f1a7b) { var length = data.Length; @@ -56,14 +60,17 @@ public static UInt32 Hash(Byte[] data, UInt32 seed = 0xc58f1a7b) h ^= (UInt32)(data[c] << 16); h *= m; break; + case 2: h ^= (UInt16)(data[c++] | data[c] << 8); h *= m; break; + case 1: h ^= data[c]; h *= m; break; + default: break; } @@ -73,6 +80,18 @@ public static UInt32 Hash(Byte[] data, UInt32 seed = 0xc58f1a7b) h ^= h >> 15; return h; } - #endregion + + /// + /// The Hash + /// + /// The item + /// The + public int Hash(string item) + { + var hash = Hash(Encoding.ASCII.GetBytes(item)); + return (int)hash; + } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/HashAlgorithms/IHashAlgorithm.cs b/src/Surging.Core/Surging.Core.Caching/HashAlgorithms/IHashAlgorithm.cs index bbd1c6ffb..9a585e310 100644 --- a/src/Surging.Core/Surging.Core.Caching/HashAlgorithms/IHashAlgorithm.cs +++ b/src/Surging.Core/Surging.Core.Caching/HashAlgorithms/IHashAlgorithm.cs @@ -4,24 +4,24 @@ namespace Surging.Core.Caching.HashAlgorithms { + #region 接口 + /// /// 一致性哈希的抽象接口 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public interface IHashAlgorithm { + #region 方法 + /// /// 获取哈希值 /// /// 字符串 /// 返回哈希值 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// int Hash(string item); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/HealthChecks/IHealthCheckService.cs b/src/Surging.Core/Surging.Core.Caching/HealthChecks/IHealthCheckService.cs index 181f5f05b..81c877c12 100644 --- a/src/Surging.Core/Surging.Core.Caching/HealthChecks/IHealthCheckService.cs +++ b/src/Surging.Core/Surging.Core.Caching/HealthChecks/IHealthCheckService.cs @@ -4,19 +4,20 @@ namespace Surging.Core.Caching.HealthChecks { + #region 接口 + + /// + /// Defines the + /// public interface IHealthCheckService { - /// - /// 监控一个地址。 - /// - /// 地址模型。 - /// 一个任务。 - void Monitor(CacheEndpoint address, string cacheId); + #region 方法 /// /// 判断一个地址是否健康。 /// /// 地址模型。 + /// The cacheId /// 健康返回true,否则返回false。 ValueTask IsHealth(CacheEndpoint address, string cacheId); @@ -24,7 +25,19 @@ public interface IHealthCheckService /// 标记一个地址为失败的。 /// /// 地址模型。 + /// The cacheId /// 一个任务。 Task MarkFailure(CacheEndpoint address, string cacheId); + + /// + /// 监控一个地址。 + /// + /// 地址模型。 + /// The cacheId + void Monitor(CacheEndpoint address, string cacheId); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/HealthChecks/Implementation/DefaultHealthCheckService.cs b/src/Surging.Core/Surging.Core.Caching/HealthChecks/Implementation/DefaultHealthCheckService.cs index cb6048ebc..369ccf823 100644 --- a/src/Surging.Core/Surging.Core.Caching/HealthChecks/Implementation/DefaultHealthCheckService.cs +++ b/src/Surging.Core/Surging.Core.Caching/HealthChecks/Implementation/DefaultHealthCheckService.cs @@ -1,13 +1,13 @@ -using System; +using Surging.Core.Caching.Interfaces; +using Surging.Core.CPlatform; +using Surging.Core.CPlatform.Cache; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; -using Surging.Core.Caching.Interfaces; -using Surging.Core.CPlatform; -using Surging.Core.CPlatform.Cache; namespace Surging.Core.Caching.HealthChecks.Implementation { @@ -16,12 +16,32 @@ namespace Surging.Core.Caching.HealthChecks.Implementation /// public class DefaultHealthCheckService : IHealthCheckService, IDisposable { - private readonly Timer _timer; + #region 字段 + + /// + /// Defines the _dictionary + /// private readonly ConcurrentDictionary, MonitorEntry> _dictionary = new ConcurrentDictionary, MonitorEntry>(); + + /// + /// Defines the _serviceCacheManager + /// private readonly IServiceCacheManager _serviceCacheManager; + /// + /// Defines the _timer + /// + private readonly Timer _timer; + + #endregion 字段 + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serviceCacheManager public DefaultHealthCheckService(IServiceCacheManager serviceCacheManager) { var timeSpan = TimeSpan.FromSeconds(10); @@ -58,12 +78,36 @@ public DefaultHealthCheckService(IServiceCacheManager serviceCacheManager) }; } + #endregion 构造函数 + + #region 方法 + + /// + /// The Dispose + /// + public void Dispose() + { + _timer.Dispose(); + } + + /// + /// The IsHealth + /// + /// The address + /// The cacheId + /// The public ValueTask IsHealth(CacheEndpoint address, string cacheId) { MonitorEntry entry; return !_dictionary.TryGetValue(new ValueTuple(address.Host, address.Port), out entry) ? new ValueTask(Check(address, cacheId)) : new ValueTask(entry.Health); } + /// + /// The MarkFailure + /// + /// The address + /// The cacheId + /// The public Task MarkFailure(CacheEndpoint address, string cacheId) { return Task.Run(() => @@ -73,22 +117,33 @@ public Task MarkFailure(CacheEndpoint address, string cacheId) }); } + /// + /// The Monitor + /// + /// The address + /// The cacheId public void Monitor(CacheEndpoint address, string cacheId) { _dictionary.GetOrAdd(new ValueTuple(address.Host, address.Port), k => new MonitorEntry(address, cacheId)); } - public void Dispose() - { - _timer.Dispose(); - } - + /// + /// The Check + /// + /// The address + /// The id + /// The private async Task Check(CacheEndpoint address, string id) { return await CacheContainer.GetService(id) .ConnectionAsync(address); } + /// + /// The Check + /// + /// The entrys + /// The private async Task Check(IEnumerable entrys) { foreach (var entry in entrys) @@ -107,6 +162,10 @@ private async Task Check(IEnumerable entrys) } } + /// + /// The Remove + /// + /// The cacheEndpoints private void Remove(IEnumerable cacheEndpoints) { foreach (var cacheEndpoint in cacheEndpoints) @@ -116,26 +175,38 @@ private void Remove(IEnumerable cacheEndpoints) } } - + /// + /// The RemoveUnhealthyAddress + /// + /// The monitorEntry private void RemoveUnhealthyAddress(IEnumerable monitorEntry) { if (monitorEntry.Any()) { var addresses = monitorEntry.Select(p => p.EndPoint).ToList(); _serviceCacheManager.RemveAddressAsync(addresses).Wait(); - addresses.ForEach(p => { - + addresses.ForEach(p => + { _dictionary.TryRemove(new ValueTuple(p.Host, p.Port), out MonitorEntry value); }); - } } + #endregion 方法 - #region Help Class - + /// + /// Defines the + /// protected class MonitorEntry { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The address + /// The cacheId + /// The health public MonitorEntry(CacheEndpoint address, string cacheId, bool health = true) { EndPoint = address; @@ -143,13 +214,31 @@ public MonitorEntry(CacheEndpoint address, string cacheId, bool health = true) CacheId = cacheId; } - public int UnhealthyTimes { get; set; } + #endregion 构造函数 + + #region 属性 + /// + /// Gets or sets the CacheId + /// public string CacheId { get; set; } + + /// + /// Gets or sets the EndPoint + /// public CacheEndpoint EndPoint { get; set; } + + /// + /// Gets or sets a value indicating whether Health + /// public bool Health { get; set; } - } - #endregion Help Class + /// + /// Gets or sets the UnhealthyTimes + /// + public int UnhealthyTimes { get; set; } + + #endregion 属性 + } } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/IdentifyCacheAttribute.cs b/src/Surging.Core/Surging.Core.Caching/IdentifyCacheAttribute.cs index 53bb9948a..b83379928 100644 --- a/src/Surging.Core/Surging.Core.Caching/IdentifyCacheAttribute.cs +++ b/src/Surging.Core/Surging.Core.Caching/IdentifyCacheAttribute.cs @@ -4,14 +4,32 @@ namespace Surging.Core.Caching { + /// + /// Defines the + /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)] public class IdentifyCacheAttribute : Attribute { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The name public IdentifyCacheAttribute(CacheTargetType name) { this.Name = name; } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets or sets the Name + /// public CacheTargetType Name { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/Interfaces/ICacheClient.cs b/src/Surging.Core/Surging.Core.Caching/Interfaces/ICacheClient.cs index ad57f2fb1..ce41deeef 100644 --- a/src/Surging.Core/Surging.Core.Caching/Interfaces/ICacheClient.cs +++ b/src/Surging.Core/Surging.Core.Caching/Interfaces/ICacheClient.cs @@ -7,11 +7,34 @@ namespace Surging.Core.Caching.Interfaces { + #region 接口 + + /// + /// Defines the + /// + /// public interface ICacheClient { - T GetClient(CacheEndpoint info, int connectTimeout); + #region 方法 + /// + /// The ConnectionAsync + /// + /// The endpoint + /// The connectTimeout + /// The Task ConnectionAsync(CacheEndpoint endpoint, int connectTimeout); + /// + /// The GetClient + /// + /// The info + /// The connectTimeout + /// The + T GetClient(CacheEndpoint info, int connectTimeout); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/Internal/Implementation/DefaultCacheNodeProvider.cs b/src/Surging.Core/Surging.Core.Caching/Internal/Implementation/DefaultCacheNodeProvider.cs index a6eb67c1c..b41730071 100644 --- a/src/Surging.Core/Surging.Core.Caching/Internal/Implementation/DefaultCacheNodeProvider.cs +++ b/src/Surging.Core/Surging.Core.Caching/Internal/Implementation/DefaultCacheNodeProvider.cs @@ -14,12 +14,34 @@ namespace Surging.Core.Caching.Internal.Implementation /// public class DefaultCacheNodeProvider : ICacheNodeProvider { + #region 字段 + + /// + /// Defines the _serviceProvider + /// private readonly CPlatformContainer _serviceProvider; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serviceProvider public DefaultCacheNodeProvider(CPlatformContainer serviceProvider) { _serviceProvider = serviceProvider; } + #endregion 构造函数 + + #region 方法 + + /// + /// The GetServiceCaches + /// + /// The public IEnumerable GetServiceCaches() { var cacheWrapperSetting = AppConfig.Configuration.Get(); @@ -50,5 +72,7 @@ public IEnumerable GetServiceCaches() } return serviceCaches; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/Internal/Implementation/DefaultServiceCacheFactory.cs b/src/Surging.Core/Surging.Core.Caching/Internal/Implementation/DefaultServiceCacheFactory.cs index a767f22c4..cc41f003c 100644 --- a/src/Surging.Core/Surging.Core.Caching/Internal/Implementation/DefaultServiceCacheFactory.cs +++ b/src/Surging.Core/Surging.Core.Caching/Internal/Implementation/DefaultServiceCacheFactory.cs @@ -14,15 +14,41 @@ namespace Surging.Core.Caching.Internal.Implementation /// public class DefaultServiceCacheFactory : IServiceCacheFactory { - private readonly ISerializer _serializer; + #region 字段 + + /// + /// Defines the _addressModel + /// private readonly ConcurrentDictionary _addressModel = new ConcurrentDictionary(); + /// + /// Defines the _serializer + /// + private readonly ISerializer _serializer; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serializer public DefaultServiceCacheFactory(ISerializer serializer) { _serializer = serializer; } + #endregion 构造函数 + + #region 方法 + + /// + /// The CreateServiceCachesAsync + /// + /// The descriptors + /// The public Task> CreateServiceCachesAsync(IEnumerable descriptors) { if (descriptors == null) @@ -33,14 +59,18 @@ public Task> CreateServiceCachesAsync(IEnumerable new ServiceCache { - CacheEndpoint = CreateAddress(descriptor.AddressDescriptors), - CacheDescriptor = descriptor.CacheDescriptor + CacheEndpoint = CreateAddress(descriptor.AddressDescriptors), + CacheDescriptor = descriptor.CacheDescriptor })); return Task.FromResult(routes.AsEnumerable()); } - + /// + /// The CreateAddress + /// + /// The descriptors + /// The private IEnumerable CreateAddress(IEnumerable descriptors) { if (descriptors == null) @@ -58,5 +88,7 @@ private IEnumerable CreateAddress(IEnumerable + /// Defines the + /// + public class CachingProvider { + #region 属性 + + /// + /// Gets or sets the CachingSettings + /// public List CachingSettings { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/Models/Map.cs b/src/Surging.Core/Surging.Core.Caching/Models/Map.cs index 627edd120..56a3f6c8c 100644 --- a/src/Surging.Core/Surging.Core.Caching/Models/Map.cs +++ b/src/Surging.Core/Surging.Core.Caching/Models/Map.cs @@ -4,10 +4,23 @@ namespace Surging.Core.Caching.Models { - public class Map + /// + /// Defines the + /// + public class Map { + #region 属性 + + /// + /// Gets or sets the Name + /// public string Name { get; set; } + /// + /// Gets or sets the Properties + /// public List Properties { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/Models/Property.cs b/src/Surging.Core/Surging.Core.Caching/Models/Property.cs index b792b4dd1..acf8cb597 100644 --- a/src/Surging.Core/Surging.Core.Caching/Models/Property.cs +++ b/src/Surging.Core/Surging.Core.Caching/Models/Property.cs @@ -4,14 +4,33 @@ namespace Surging.Core.Caching.Models { + /// + /// Defines the + /// public class Property { + #region 属性 + + /// + /// Gets or sets the Maps + /// + public List Maps { get; set; } + + /// + /// Gets or sets the Name + /// public string Name { get; set; } + /// + /// Gets or sets the Ref + /// public string Ref { get; set; } + /// + /// Gets or sets the Value + /// public string Value { get; set; } - public List Maps { get; set; } + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/Models/binding.cs b/src/Surging.Core/Surging.Core.Caching/Models/binding.cs index aebc6cc15..28718bfb8 100644 --- a/src/Surging.Core/Surging.Core.Caching/Models/binding.cs +++ b/src/Surging.Core/Surging.Core.Caching/Models/binding.cs @@ -4,16 +4,38 @@ namespace Surging.Core.Caching.Models { - public class Binding + /// + /// Defines the + /// + public class Binding { - public string Id { get; set; } + #region 属性 + /// + /// Gets or sets the Class + /// public string Class { get; set; } + /// + /// Gets or sets the Id + /// + public string Id { get; set; } + + /// + /// Gets or sets the InitMethod + /// public string InitMethod { get; set; } + /// + /// Gets or sets the Maps + /// public List Maps { get; set; } + /// + /// Gets or sets the Properties + /// public List Properties { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/NetCache/GCThreadProvider.cs b/src/Surging.Core/Surging.Core.Caching/NetCache/GCThreadProvider.cs index ed9960530..d7f4974b6 100644 --- a/src/Surging.Core/Surging.Core.Caching/NetCache/GCThreadProvider.cs +++ b/src/Surging.Core/Surging.Core.Caching/NetCache/GCThreadProvider.cs @@ -7,23 +7,21 @@ namespace Surging.Core.Caching.NetCache { + /// + /// Defines the + /// public class GCThreadProvider { - #region 私有字段 + #region 字段 + /// /// 本地缓存垃圾回收线程 /// private static readonly ConcurrentStack _globalThread = new ConcurrentStack(); - #endregion - - /// - /// 添加垃圾线程方法 - /// - /// 表示在 System.Threading.Thread 上执行的方法。 - public static void AddThread(ParameterizedThreadStart start) - { - AddThread(start, null); - } + + #endregion 字段 + + #region 方法 /// /// 添加垃圾线程方法 @@ -48,5 +46,16 @@ public static void AddThread(ParameterizedThreadStart paramThreadstart, object p throw new CacheException(err.Message, err); } } + + /// + /// 添加垃圾线程方法 + /// + /// 表示在 System.Threading.Thread 上执行的方法。 + public static void AddThread(ParameterizedThreadStart start) + { + AddThread(start, null); + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/NetCache/MemoryCache.cs b/src/Surging.Core/Surging.Core.Caching/NetCache/MemoryCache.cs index 9edec5b8e..dc647402e 100644 --- a/src/Surging.Core/Surging.Core.Caching/NetCache/MemoryCache.cs +++ b/src/Surging.Core/Surging.Core.Caching/NetCache/MemoryCache.cs @@ -1,20 +1,43 @@ using Surging.Core.Caching.Utilities; using System; +using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Text; -using System.Threading; using System.Linq; -using System.Collections; +using System.Text; using System.Text.RegularExpressions; +using System.Threading; namespace Surging.Core.Caching.NetCache { + /// + /// Defines the + /// public class MemoryCache { + #region 常量 + + /// + /// Defines the taskInterval + /// + private const int taskInterval = 5; + + #endregion 常量 + + #region 字段 + + /// + /// Defines the cache + /// private static readonly ConcurrentDictionary> cache = new ConcurrentDictionary>(); - private const int taskInterval = 5; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes static members of the class. + /// static MemoryCache() { try @@ -27,6 +50,13 @@ static MemoryCache() } } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the Count + /// public static int Count { get @@ -34,22 +64,44 @@ public static int Count return cache.Count; } } - + + #endregion 属性 + + #region 方法 + /// - /// 获得一个Cache对象 + /// 是否存在缓存 /// /// 标识 - public static object Get(string key) + /// The value + /// + public static bool Contains(string key, out object value) { - Check.CheckCondition(() => string.IsNullOrEmpty(key), "key"); - object result; - if (Contains(key, out result)) + bool isSuccess = false; + Tuple item; + value = null; + if (cache.TryGetValue(key, out item)) { - return result; + value = item.Item2; + isSuccess = item.Item3 > DateTime.Now; } - return null; + return isSuccess; + } + + /// + /// The Dispose + /// + public static void Dispose() + { + cache.Clear(); } + /// + /// The Get + /// + /// + /// The keys + /// The public static IDictionary Get(IEnumerable keys) { if (keys == null) @@ -70,13 +122,28 @@ public static IDictionary Get(IEnumerable keys) return dictionary; } - public static bool GetCacheTryParse(string key, out object obj) + /// + /// 获得一个Cache对象 + /// + /// 标识 + /// The + public static object Get(string key) { Check.CheckCondition(() => string.IsNullOrEmpty(key), "key"); - obj = Get(key); - return (obj != null); + object result; + if (Contains(key, out result)) + { + return result; + } + return null; } - + + /// + /// The Get + /// + /// + /// The key + /// The public static T Get(string key) { Check.CheckCondition(() => string.IsNullOrEmpty(key), "key"); @@ -89,30 +156,32 @@ public static T Get(string key) } /// - /// 是否存在缓存 + /// The GetCacheTryParse /// - /// 标识 - /// - public static bool Contains(string key, out object value) + /// The key + /// The obj + /// The + public static bool GetCacheTryParse(string key, out object obj) { - bool isSuccess = false; - Tuple item; - value = null; - if (cache.TryGetValue(key, out item)) - { - value = item.Item2; - isSuccess = item.Item3 > DateTime.Now; - } - return isSuccess; + Check.CheckCondition(() => string.IsNullOrEmpty(key), "key"); + obj = Get(key); + return (obj != null); } - public static void Set(string key, object value, double cacheSecond) + /// + /// The Remove + /// + /// The key + public static void Remove(string key) { - DateTime cacheTime = DateTime.Now.AddSeconds(cacheSecond); - var cacheValue = new Tuple(key, value, cacheTime); - cache.AddOrUpdate(key, cacheValue, (v, oldValue) => cacheValue); + Tuple item; + cache.TryRemove(key, out item); } + /// + /// The RemoveByPattern + /// + /// The pattern public static void RemoveByPattern(string pattern) { var enumerator = cache.GetEnumerator(); @@ -127,17 +196,23 @@ public static void RemoveByPattern(string pattern) } } - public static void Remove(string key) - { - Tuple item; - cache.TryRemove(key,out item); - } - - public static void Dispose() + /// + /// The Set + /// + /// The key + /// The value + /// The cacheSecond + public static void Set(string key, object value, double cacheSecond) { - cache.Clear(); + DateTime cacheTime = DateTime.Now.AddSeconds(cacheSecond); + var cacheValue = new Tuple(key, value, cacheTime); + cache.AddOrUpdate(key, cacheValue, (v, oldValue) => cacheValue); } + /// + /// The Collect + /// + /// The threadID private static void Collect(object threadID) { while (true) @@ -158,12 +233,12 @@ private static void Collect(object threadID) } catch { - Dispose(); + Dispose(); GCThreadProvider.AddThread(new ParameterizedThreadStart(Collect)); } - } - } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/NetCache/MemoryCacheProvider.cs b/src/Surging.Core/Surging.Core.Caching/NetCache/MemoryCacheProvider.cs index 6d61d60ed..40fffc719 100644 --- a/src/Surging.Core/Surging.Core.Caching/NetCache/MemoryCacheProvider.cs +++ b/src/Surging.Core/Surging.Core.Caching/NetCache/MemoryCacheProvider.cs @@ -9,9 +9,21 @@ namespace Surging.Core.Caching.NetCache { + /// + /// Defines the + /// [IdentifyCache(name: CacheTargetType.MemoryCache)] public sealed class MemoryCacheProvider : ICacheProvider { + #region 常量 + + /// + /// 配置失效时间 + /// + private const double ExpireTime = 60D; + + #endregion 常量 + #region 字段 /// @@ -24,23 +36,32 @@ public sealed class MemoryCacheProvider : ICacheProvider /// private Lazy _defaultExpireTime; - /// - /// 配置失效时间 - /// - private const double ExpireTime = 60D; - /// /// KEY键前缀 /// private string _keySuffix; - #endregion + #endregion 字段 #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + public MemoryCacheProvider() + { + _defaultExpireTime = new Lazy(() => 60); + _keySuffix = string.Empty; + } + + /// + /// Initializes a new instance of the class. + /// + /// The appName public MemoryCacheProvider(string appName) { - _context = new Lazy(() => { + _context = new Lazy(() => + { if (CacheContainer.IsRegistered(CacheTargetType.Redis.ToString())) return CacheContainer.GetService(appName); else @@ -51,149 +72,250 @@ public MemoryCacheProvider(string appName) _defaultExpireTime = new Lazy(() => long.Parse(_context.Value._defaultExpireTime)); } - public MemoryCacheProvider() + #endregion 构造函数 + + #region 属性 + + /// + /// Gets or sets the DefaultExpireTime + /// 默认缓存失效时间 + /// + public long DefaultExpireTime { - _defaultExpireTime = new Lazy(() => 60); - _keySuffix = string.Empty; + get + { + return _defaultExpireTime.Value; + } + set + { + _defaultExpireTime = new Lazy(() => value); + } } - public void Add(string key, object value) + /// + /// Gets or sets the KeySuffix + /// KEY前缀 + /// + public string KeySuffix { - MemoryCache.Set(GetKeySuffix(key), value, _defaultExpireTime.Value); + get + { + return _keySuffix; + } + set + { + _keySuffix = value; + } } - public async void AddAsync(string key, object value) + #endregion 属性 + + #region 方法 + + /// + /// The Add + /// + /// The key + /// The value + public void Add(string key, object value) { - await Task.Run(() => MemoryCache.Set(GetKeySuffix(key), value, DefaultExpireTime)); + MemoryCache.Set(GetKeySuffix(key), value, _defaultExpireTime.Value); } + /// + /// The Add + /// + /// The key + /// The value + /// The defaultExpire public void Add(string key, object value, bool defaultExpire) { MemoryCache.Set(GetKeySuffix(key), value, defaultExpire ? DefaultExpireTime : ExpireTime); } - public async void AddAsync(string key, object value, bool defaultExpire) + /// + /// The Add + /// + /// The key + /// The value + /// The numOfMinutes + public void Add(string key, object value, long numOfMinutes) { - await Task.Run(() => MemoryCache.Set(GetKeySuffix(key), value, defaultExpire ? DefaultExpireTime : ExpireTime)); + MemoryCache.Set(GetKeySuffix(key), value, numOfMinutes * 60); } - public void Add(string key, object value, long numOfMinutes) + /// + /// The Add + /// + /// The key + /// The value + /// The timeSpan + public void Add(string key, object value, TimeSpan timeSpan) { - MemoryCache.Set(GetKeySuffix(key), value, numOfMinutes*60); + MemoryCache.Set(GetKeySuffix(key), value, timeSpan.TotalSeconds); } - public async void AddAsync(string key, object value, long numOfMinutes) + /// + /// The AddAsync + /// + /// The key + /// The value + public async void AddAsync(string key, object value) { - await Task.Run(() => MemoryCache.Set(GetKeySuffix(key), value, numOfMinutes*60)); + await Task.Run(() => MemoryCache.Set(GetKeySuffix(key), value, DefaultExpireTime)); } - public void Add(string key, object value, TimeSpan timeSpan) + /// + /// The AddAsync + /// + /// The key + /// The value + /// The defaultExpire + public async void AddAsync(string key, object value, bool defaultExpire) { - MemoryCache.Set(GetKeySuffix(key), value, timeSpan.TotalSeconds); + await Task.Run(() => MemoryCache.Set(GetKeySuffix(key), value, defaultExpire ? DefaultExpireTime : ExpireTime)); + } + + /// + /// The AddAsync + /// + /// The key + /// The value + /// The numOfMinutes + public async void AddAsync(string key, object value, long numOfMinutes) + { + await Task.Run(() => MemoryCache.Set(GetKeySuffix(key), value, numOfMinutes * 60)); } + /// + /// The AddAsync + /// + /// The key + /// The value + /// The timeSpan public async void AddAsync(string key, object value, TimeSpan timeSpan) { await Task.Run(() => MemoryCache.Set(GetKeySuffix(key), value, timeSpan.TotalSeconds)); } - public IDictionary Get(IEnumerable keys) + /// + /// The ConnectionAsync + /// + /// The endpoint + /// The + public Task ConnectionAsync(CacheEndpoint endpoint) { - keys.ToList().ForEach(key => key = GetKeySuffix(key)); - return MemoryCache.Get(keys); + return Task.FromResult(true); } - public async Task> GetAsync(IEnumerable keys) + /// + /// The Get + /// + /// + /// The keys + /// The + public IDictionary Get(IEnumerable keys) { keys.ToList().ForEach(key => key = GetKeySuffix(key)); - var result = await Task.Run(() => MemoryCache.Get(keys)); - return result; + return MemoryCache.Get(keys); } + /// + /// The Get + /// + /// The key + /// The public object Get(string key) { return MemoryCache.Get(GetKeySuffix(key)); } - public async Task GetAsync(string key) + /// + /// The Get + /// + /// + /// The key + /// The + public T Get(string key) { - var result = await Task.Run(() => MemoryCache.Get(GetKeySuffix(key))); + return MemoryCache.Get(GetKeySuffix(key)); + } + + /// + /// The GetAsync + /// + /// + /// The keys + /// The + public async Task> GetAsync(IEnumerable keys) + { + keys.ToList().ForEach(key => key = GetKeySuffix(key)); + var result = await Task.Run(() => MemoryCache.Get(keys)); return result; } - public T Get(string key) + /// + /// The GetAsync + /// + /// The key + /// The + public async Task GetAsync(string key) { - return MemoryCache.Get(GetKeySuffix(key)); + var result = await Task.Run(() => MemoryCache.Get(GetKeySuffix(key))); + return result; } + /// + /// The GetAsync + /// + /// + /// The key + /// The public async Task GetAsync(string key) { var result = await Task.Run(() => MemoryCache.Get(GetKeySuffix(key))); return result; } + /// + /// The GetCacheTryParse + /// + /// The key + /// The obj + /// The public bool GetCacheTryParse(string key, out object obj) { return MemoryCache.GetCacheTryParse(GetKeySuffix(key), out obj); } + /// + /// The Remove + /// + /// The key public void Remove(string key) { MemoryCache.RemoveByPattern(GetKeySuffix(key)); } - public async void RemoveAsync(string key) - { - await Task.Run(() => MemoryCache.Remove(GetKeySuffix(key))); - } - - public Task ConnectionAsync(CacheEndpoint endpoint) - { - return Task.FromResult(true); - } - - #endregion - - #region 属性 - /// - /// 默认缓存失效时间 + /// The RemoveAsync /// - public long DefaultExpireTime + /// The key + public async void RemoveAsync(string key) { - get - { - return _defaultExpireTime.Value; - } - set - { - _defaultExpireTime = new Lazy(() => value); - } - + await Task.Run(() => MemoryCache.Remove(GetKeySuffix(key))); } /// - /// KEY前缀 + /// The GetKeySuffix /// - public string KeySuffix - { - get - { - return _keySuffix; - } - set - { - _keySuffix = value; - } - } - #endregion - - #region 私有变量 + /// The key + /// The private string GetKeySuffix(string key) { return string.IsNullOrEmpty(KeySuffix) ? key : string.Format("_{0}_{1}", KeySuffix, key); } - #endregion + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/ObjectPool.cs b/src/Surging.Core/Surging.Core.Caching/ObjectPool.cs index f868bcea5..6fae68663 100644 --- a/src/Surging.Core/Surging.Core.Caching/ObjectPool.cs +++ b/src/Surging.Core/Surging.Core.Caching/ObjectPool.cs @@ -6,33 +6,55 @@ namespace Surging.Core.Caching { + /// + /// Defines the + /// + /// public class ObjectPool { - #region + #region 字段 + + /// + /// Defines the maxSize + /// + private readonly int maxSize = 50; + + /// + /// Defines the minSize + /// + private readonly int minSize = 1; + + /// + /// Defines the currentResource + /// + private int currentResource = 0; + + /// + /// Defines the func + /// + private Func func = null; + + /// + /// Defines the isTaked + /// private int isTaked = 0; + + /// + /// Defines the queue + /// private Queue queue = new Queue(); - private Func func = null; - private int currentResource = 0; + + /// + /// Defines the tryNewObject + /// private int tryNewObject = 0; - private readonly int minSize = 1; - private readonly int maxSize = 50; - #endregion - #region private methods - private void Enter() - { - while (Interlocked.Exchange(ref isTaked, 1) != 0) - { - } - } - private void Leave() - { - Interlocked.Exchange(ref isTaked, 0); - } - #endregion + #endregion 字段 + + #region 构造函数 /// - /// 构造一个对象池 + /// Initializes a new instance of the class. /// /// 用来初始化对象的函数 /// 对象池下限 @@ -55,9 +77,14 @@ public ObjectPool(Func func, int minSize = 100, int maxSize = 100) this.func = func; } + #endregion 构造函数 + + #region 方法 + /// /// 从对象池中取一个对象出来, 执行完成以后会自动将对象放回池中 /// + /// The public T GetObject() { var t = default(T); @@ -87,5 +114,24 @@ public T GetObject() } } + /// + /// The Enter + /// + private void Enter() + { + while (Interlocked.Exchange(ref isTaked, 1) != 0) + { + } + } + + /// + /// The Leave + /// + private void Leave() + { + Interlocked.Exchange(ref isTaked, 0); + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/RedisCache/RedisCacheClient.cs b/src/Surging.Core/Surging.Core.Caching/RedisCache/RedisCacheClient.cs index 7edd136b9..e1df299e2 100644 --- a/src/Surging.Core/Surging.Core.Caching/RedisCache/RedisCacheClient.cs +++ b/src/Surging.Core/Surging.Core.Caching/RedisCache/RedisCacheClient.cs @@ -9,27 +9,51 @@ namespace Surging.Core.Caching.RedisCache { + /// + /// Defines the + /// + /// [IdentifyCache(name: CacheTargetType.Redis)] public class RedisCacheClient : ICacheClient where T : class - { + #region ֶ + + /// + /// Defines the _pool + /// private static readonly ConcurrentDictionary>> _pool = new ConcurrentDictionary>>(); + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// public RedisCacheClient() { - } + #endregion 캯 + + #region + + /// + /// The ConnectionAsync + /// + /// The endpoint + /// The connectTimeout + /// The public async Task ConnectionAsync(CacheEndpoint endpoint, int connectTimeout) { - ConnectionMultiplexer conn=null; + ConnectionMultiplexer conn = null; try { var info = endpoint as ConsistentHashNode; var point = string.Format("{0}:{1}", info.Host, info.Port); - conn = await ConnectionMultiplexer.ConnectAsync(new ConfigurationOptions() + conn = await ConnectionMultiplexer.ConnectAsync(new ConfigurationOptions() { EndPoints = { { point } }, ServiceName = point, @@ -44,11 +68,17 @@ public async Task ConnectionAsync(CacheEndpoint endpoint, int connectTimeo } finally { - if(conn !=null) - conn.Close(); + if (conn != null) + conn.Close(); } } + /// + /// The GetClient + /// + /// The endpoint + /// The connectTimeout + /// The public T GetClient(CacheEndpoint endpoint, int connectTimeout) { try @@ -57,22 +87,22 @@ public T GetClient(CacheEndpoint endpoint, int connectTimeout) Check.NotNull(info, "endpoint"); var key = string.Format("{0}{1}{2}{3}", info.Host, info.Port, info.Password, info.DbIndex); if (!_pool.ContainsKey(key)) - { - var objectPool = new Lazy>(()=>new ObjectPool(() => - { - var point = string.Format("{0}:{1}", info.Host, info.Port); - var redisClient = ConnectionMultiplexer.Connect(new ConfigurationOptions() - { - EndPoints = { { point } }, - ServiceName = point, - Password = info.Password, - ConnectTimeout = connectTimeout, - AbortOnConnectFail = false - }); - return redisClient.GetDatabase(info.DbIndex) as T; - }, info.MinSize, info.MaxSize)); - _pool.GetOrAdd(key, objectPool); - return objectPool.Value.GetObject(); + { + var objectPool = new Lazy>(() => new ObjectPool(() => + { + var point = string.Format("{0}:{1}", info.Host, info.Port); + var redisClient = ConnectionMultiplexer.Connect(new ConfigurationOptions() + { + EndPoints = { { point } }, + ServiceName = point, + Password = info.Password, + ConnectTimeout = connectTimeout, + AbortOnConnectFail = false + }); + return redisClient.GetDatabase(info.DbIndex) as T; + }, info.MinSize, info.MaxSize)); + _pool.GetOrAdd(key, objectPool); + return objectPool.Value.GetObject(); } else { @@ -82,7 +112,9 @@ public T GetClient(CacheEndpoint endpoint, int connectTimeout) catch (Exception e) { throw new CacheException(e.Message); - } + } } + + #endregion } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/RedisCache/RedisContext.cs b/src/Surging.Core/Surging.Core.Caching/RedisCache/RedisContext.cs index 238d3e0a4..6a1a49c42 100644 --- a/src/Surging.Core/Surging.Core.Caching/RedisCache/RedisContext.cs +++ b/src/Surging.Core/Surging.Core.Caching/RedisCache/RedisContext.cs @@ -4,97 +4,78 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Reflection; using System.Text; -using System.Linq; namespace Surging.Core.Caching.RedisCache { /// /// redis数据上下文 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public class RedisContext { + #region 字段 + + /// + /// Defines the _hashAlgorithm + /// private readonly IHashAlgorithm _hashAlgorithm; + + /// + /// Defines the _bucket + /// + internal string _bucket = null; + /// /// 缓存对象集合容器池 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// internal Lazy>> _cachingContextPool; /// - /// 密码 + /// 连接失效时间 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - internal string _password = null; + internal string _connectTimeout = null; - internal string _bucket = null; /// /// 默认缓存失效时间 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - internal string _defaultExpireTime=null; + internal string _defaultExpireTime = null; /// - /// 连接失效时间 + /// 对象池上限 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - internal string _connectTimeout = null; + internal string _maxSize = null; /// - /// 规则名(现在只实现哈希一致性) + /// 对象池下限 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - internal string _ruleName = null; + internal string _minSize = null; /// - /// 哈希节点容器 + /// 密码 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - internal ConcurrentDictionary> dicHash; + internal string _password = null; /// - /// 对象池上限 + /// 规则名(现在只实现哈希一致性) /// - internal string _maxSize = null; + internal string _ruleName = null; /// - /// 对象池下限 + /// 哈希节点容器 /// - internal string _minSize=null; + internal ConcurrentDictionary> dicHash; + + #endregion 字段 #region 构造函数 + /// - /// redis数据上下文 + /// Initializes a new instance of the class. /// /// 规则 /// 参数 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public RedisContext(string rule, params object[] args) { if (CacheContainer.IsRegistered()) @@ -142,10 +123,14 @@ into value dicHash = new ConcurrentDictionary>(); InitSettingHashStorage(); } - #endregion + + #endregion 构造函数 #region 属性 + /// + /// Gets the ConnectTimeout + /// public string ConnectTimeout { get @@ -154,35 +139,33 @@ public string ConnectTimeout } } - public string DefaultExpireTime - { - get - { - return _defaultExpireTime; - } - } /// + /// Gets the DataContextPool /// 缓存对象集合容器池 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public Dictionary> DataContextPool { get { return _cachingContextPool.Value; } } - #endregion - #region 私有方法 + /// + /// Gets the DefaultExpireTime + /// + public string DefaultExpireTime + { + get + { + return _defaultExpireTime; + } + } + + #endregion 属性 + + #region 方法 /// /// 初始化设置哈希节点容器 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// private void InitSettingHashStorage() { foreach (var dataContext in DataContextPool) @@ -217,12 +200,12 @@ private void InitSettingHashStorage() MinSize = this._minSize, Db = db.ToString() }; - hash.Add(node,string.Format("{0}:{1}",node.Host,node.Port)); + hash.Add(node, string.Format("{0}:{1}", node.Host, node.Port)); dicHash.GetOrAdd(targetType.ToString(), hash); }); } } - #endregion + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/RedisCache/RedisEndpoint.cs b/src/Surging.Core/Surging.Core.Caching/RedisCache/RedisEndpoint.cs index bab468d9b..b2abbf2ac 100644 --- a/src/Surging.Core/Surging.Core.Caching/RedisCache/RedisEndpoint.cs +++ b/src/Surging.Core/Surging.Core.Caching/RedisCache/RedisEndpoint.cs @@ -10,76 +10,57 @@ namespace Surging.Core.Caching.RedisCache /// /// redis 终端 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public class RedisEndpoint : CacheEndpoint { + #region 属性 + /// + /// Gets or sets the DbIndex + /// 数据库 + /// + public int DbIndex { get; set; } + + /// + /// Gets or sets the Host /// 主机 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public new string Host - { - get; set; - } + public new string Host { get; set; } /// - /// 端口 + /// Gets or sets the MaxSize /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public new int Port - { - get; set; - } + public int MaxSize { get; set; } /// - /// 密码 + /// Gets or sets the MinSize /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public string Password - { - get; set; - } + public int MinSize { get; set; } /// - /// 数据库 + /// Gets or sets the Password + /// 密码 /// - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public int DbIndex - { - get; set; - } + public string Password { get; set; } - public int MaxSize - { - get; set; - } + /// + /// Gets or sets the Port + /// 端口 + /// + public new int Port { get; set; } - public int MinSize - { - get; - set; - } + #endregion 属性 + #region 方法 + /// + /// The ToString + /// + /// The public override string ToString() { - return string.Concat(new string[] { Host, ":", Port.ToString(),"::" ,DbIndex.ToString()}); + return string.Concat(new string[] { Host, ":", Port.ToString(), "::", DbIndex.ToString() }); } - + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/RedisCache/RedisProvider.cs b/src/Surging.Core/Surging.Core.Caching/RedisCache/RedisProvider.cs index 4cbe77b43..65b062ccc 100644 --- a/src/Surging.Core/Surging.Core.Caching/RedisCache/RedisProvider.cs +++ b/src/Surging.Core/Surging.Core.Caching/RedisCache/RedisProvider.cs @@ -11,24 +11,72 @@ namespace Surging.Core.Caching.RedisCache { + /// + /// Defines the + /// [IdentifyCache(name: CacheTargetType.Redis)] public class RedisProvider : ICacheProvider { + #region 常量 + + /// + /// Defines the ExpireTime + /// + private const double ExpireTime = 60D; + + #endregion 常量 + #region 字段 + + /// + /// Defines the _cacheClient + /// + private readonly Lazy> _cacheClient; + + /// + /// Defines the _context + /// private readonly Lazy _context; + + /// + /// Defines the addressResolver + /// + private readonly IAddressResolver addressResolver; + + /// + /// Defines the _connectTimeout + /// + private Lazy _connectTimeout; + + /// + /// Defines the _defaultExpireTime + /// private Lazy _defaultExpireTime; - private const double ExpireTime = 60D; + + /// + /// Defines the _keySuffix + /// private string _keySuffix; - private Lazy _connectTimeout; - private readonly Lazy> _cacheClient; - private readonly IAddressResolver addressResolver; - #endregion + + #endregion 字段 #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + public RedisProvider() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The appName public RedisProvider(string appName) { - _context = new Lazy(() => { + _context = new Lazy(() => + { if (CacheContainer.IsRegistered(appName)) return CacheContainer.GetService(appName); else @@ -46,69 +94,67 @@ public RedisProvider(string appName) _cacheClient = new Lazy>(() => CacheContainer.GetInstances>(CacheTargetType.Redis.ToString())); } - public RedisProvider() - { + #endregion 构造函数 - } - #endregion + #region 属性 - #region 公共方法 /// - /// 添加K/V值 + /// Gets or sets the ConnectTimeout /// - /// 键 - /// 值 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public void Add(string key, object value) + public int ConnectTimeout { - this.Add(key, value, TimeSpan.FromSeconds(ExpireTime)); + get + { + return _connectTimeout.Value; + } + set + { + _connectTimeout = new Lazy(() => value); + } } /// - /// 异步添加K/V值 + /// Gets or sets the DefaultExpireTime /// - /// 键 - /// 值 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public void AddAsync(string key, object value) + public long DefaultExpireTime { - this.AddTaskAsync(key, value, TimeSpan.FromSeconds(ExpireTime)); + get + { + return _defaultExpireTime.Value; + } + set + { + _defaultExpireTime = new Lazy(() => value); + } } /// - /// 添加k/v值 + /// Gets or sets the KeySuffix /// - /// 键 - /// 值 - /// 默认配置失效时间 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public void Add(string key, object value, bool defaultExpire) + public string KeySuffix { - this.Add(key, value, TimeSpan.FromSeconds(defaultExpire ? DefaultExpireTime : ExpireTime)); + get + { + return _keySuffix; + } + set + { + _keySuffix = value; + } } + #endregion 属性 + + #region 方法 + /// - /// 异步添加K/V值 + /// 添加K/V值 /// /// 键 /// 值 - /// 默认配置失效时间 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public void AddAsync(string key, object value, bool defaultExpire) + public void Add(string key, object value) { - this.AddTaskAsync(key, value, TimeSpan.FromSeconds(defaultExpire ? DefaultExpireTime : ExpireTime)); + this.Add(key, value, TimeSpan.FromSeconds(ExpireTime)); } /// @@ -116,43 +162,29 @@ public void AddAsync(string key, object value, bool defaultExpire) /// /// 键 /// 值 - /// 默认配置失效时间 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public void Add(string key, object value, long numOfMinutes) + /// 默认配置失效时间 + public void Add(string key, object value, bool defaultExpire) { - this.Add(key, value, TimeSpan.FromMinutes(numOfMinutes)); + this.Add(key, value, TimeSpan.FromSeconds(defaultExpire ? DefaultExpireTime : ExpireTime)); } - /// - /// 异步添加K/V值 + /// 添加k/v值 /// /// 键 /// 值 /// 默认配置失效时间 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public void AddAsync(string key, object value, long numOfMinutes) + public void Add(string key, object value, long numOfMinutes) { - this.AddTaskAsync(key, value, TimeSpan.FromMinutes(numOfMinutes)); + this.Add(key, value, TimeSpan.FromMinutes(numOfMinutes)); } - /// /// 添加k/v值 /// /// 键 /// 值 /// 配置时间间隔 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public void Add(string key, object value, TimeSpan timeSpan) { var node = GetRedisNode(key); @@ -168,68 +200,72 @@ public void Add(string key, object value, TimeSpan timeSpan) redis.Set(GetKeySuffix(key), value, timeSpan); } + /// + /// 异步添加K/V值 + /// + /// 键 + /// 值 + public void AddAsync(string key, object value) + { + this.AddTaskAsync(key, value, TimeSpan.FromSeconds(ExpireTime)); + } + + /// + /// 异步添加K/V值 + /// + /// 键 + /// 值 + /// 默认配置失效时间 + public void AddAsync(string key, object value, bool defaultExpire) + { + this.AddTaskAsync(key, value, TimeSpan.FromSeconds(defaultExpire ? DefaultExpireTime : ExpireTime)); + } + + /// + /// 异步添加K/V值 + /// + /// 键 + /// 值 + /// 默认配置失效时间 + public void AddAsync(string key, object value, long numOfMinutes) + { + this.AddTaskAsync(key, value, TimeSpan.FromMinutes(numOfMinutes)); + } + /// /// 异步添加K/V值 /// /// 键 /// 值 /// 配置时间间隔 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public void AddAsync(string key, object value, TimeSpan timeSpan) { this.AddTaskAsync(key, value, timeSpan); } /// - /// 根据KEY键集合获取返回对象集合 + /// The ConnectionAsync /// - /// 返回类型 - /// KEY值集合 - /// 需要返回的对象集合 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public IDictionary Get(IEnumerable keys) + /// The endpoint + /// The + public async Task ConnectionAsync(CacheEndpoint endpoint) { - IDictionary result = null; - foreach (var key in keys) - { - - var node = GetRedisNode(key); - var redis = GetRedisClient(new RedisEndpoint() - { - DbIndex = int.Parse(node.Db), - Host = node.Host, - Password = node.Password, - Port = int.Parse(node.Port), - MinSize = int.Parse(node.MinSize), - MaxSize = int.Parse(node.MaxSize), - }); - result.Add(key, redis.Get(key)); - } - return result; + var connection = await _cacheClient + .Value.ConnectionAsync(endpoint, ConnectTimeout); + return connection; } /// - /// 根据KEY键集合异步获取返回对象集合 + /// 根据KEY键集合获取返回对象集合 /// /// 返回类型 /// KEY值集合 /// 需要返回的对象集合 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// - public async Task> GetAsync(IEnumerable keys) + public IDictionary Get(IEnumerable keys) { IDictionary result = null; foreach (var key in keys) { - var node = GetRedisNode(key); var redis = GetRedisClient(new RedisEndpoint() { @@ -240,7 +276,7 @@ public async Task> GetAsync(IEnumerable keys) MinSize = int.Parse(node.MinSize), MaxSize = int.Parse(node.MaxSize), }); - result.Add(key, await redis.GetAsync(key)); + result.Add(key, redis.Get(key)); } return result; } @@ -250,37 +286,18 @@ public async Task> GetAsync(IEnumerable keys) /// /// KEY值 /// 需要返回的对象 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public object Get(string key) { var o = this.Get(key); return o; } - /// - /// 根据KEY异步获取返回对象 - /// - /// - /// - public async Task GetAsync(string key) - { - var result = await this.GetTaskAsync(key); - return result; - } - /// /// 根据KEY键获取返回指定的类型对象 /// /// 返回类型 /// KEY值 /// 需要返回的对象 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public T Get(string key) { var node = GetRedisNode(key); @@ -298,7 +315,42 @@ public T Get(string key) return result; } + /// + /// 根据KEY键集合异步获取返回对象集合 + /// + /// 返回类型 + /// KEY值集合 + /// 需要返回的对象集合 + public async Task> GetAsync(IEnumerable keys) + { + IDictionary result = null; + foreach (var key in keys) + { + var node = GetRedisNode(key); + var redis = GetRedisClient(new RedisEndpoint() + { + DbIndex = int.Parse(node.Db), + Host = node.Host, + Password = node.Password, + Port = int.Parse(node.Port), + MinSize = int.Parse(node.MinSize), + MaxSize = int.Parse(node.MaxSize), + }); + result.Add(key, await redis.GetAsync(key)); + } + return result; + } + /// + /// 根据KEY异步获取返回对象 + /// + /// + /// + public async Task GetAsync(string key) + { + var result = await this.GetTaskAsync(key); + return result; + } /// /// 根据KEY异步获取指定的类型对象 @@ -321,7 +373,6 @@ public async Task GetAsync(string key) var result = await Task.Run(() => redis.Get(GetKeySuffix(key))); return result; - } /// @@ -330,10 +381,6 @@ public async Task GetAsync(string key) /// KEY键 /// 需要转化返回的对象 /// 是否成功 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public bool GetCacheTryParse(string key, out object obj) { obj = null; @@ -346,10 +393,6 @@ public bool GetCacheTryParse(string key, out object obj) /// 根据KEY键删除缓存 /// /// KEY键 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public void Remove(string key) { var node = GetRedisNode(key); @@ -363,63 +406,43 @@ public void Remove(string key) MaxSize = int.Parse(node.MaxSize), }); redis.Remove(GetKeySuffix(key)); - } /// /// 根据KEY键异步删除缓存 /// /// KEY键 - /// - /// 创建:范亮 - /// 日期:2016/4/2 - /// public void RemoveAsync(string key) { this.RemoveTaskAsync(key); } - public long DefaultExpireTime - { - get - { - return _defaultExpireTime.Value; - } - set - { - _defaultExpireTime = new Lazy(() => value); - } - - } - - public string KeySuffix + /// + /// The AddTaskAsync + /// + /// The key + /// The value + /// The timeSpan + private async void AddTaskAsync(string key, object value, TimeSpan timeSpan) { - get - { - return _keySuffix; - } - set - { - _keySuffix = value; - } + await Task.Run(() => this.Add(key, value, timeSpan)); } - - public int ConnectTimeout + /// + /// The GetKeySuffix + /// + /// The key + /// The + private string GetKeySuffix(string key) { - get - { - return _connectTimeout.Value; - } - set - { - _connectTimeout = new Lazy(() => value); - } + return string.IsNullOrEmpty(KeySuffix) ? key : string.Format("_{0}_{1}", KeySuffix, key); } - #endregion - - #region 私有方法 + /// + /// The GetRedisClient + /// + /// The info + /// The private IDatabase GetRedisClient(CacheEndpoint info) { return @@ -427,6 +450,11 @@ private IDatabase GetRedisClient(CacheEndpoint info) .GetClient(info, ConnectTimeout); } + /// + /// The GetRedisNode + /// + /// The item + /// The private ConsistentHashNode GetRedisNode(string item) { if (addressResolver != null) @@ -441,35 +469,26 @@ private ConsistentHashNode GetRedisNode(string item) } } + /// + /// The GetTaskAsync + /// + /// + /// The key + /// The private async Task GetTaskAsync(string key) { return await Task.Run(() => this.Get(key)); } - private async void AddTaskAsync(string key, object value, TimeSpan timeSpan) - { - await Task.Run(() => this.Add(key, value, timeSpan)); - } - + /// + /// The RemoveTaskAsync + /// + /// The key private async void RemoveTaskAsync(string key) { await Task.Run(() => this.Remove(key)); } - private string GetKeySuffix(string key) - { - return string.IsNullOrEmpty(KeySuffix) ? key : string.Format("_{0}_{1}", KeySuffix, key); - } - - public async Task ConnectionAsync(CacheEndpoint endpoint) - { - var connection = await _cacheClient - .Value.ConnectionAsync(endpoint, ConnectTimeout); - return connection; - } - - #endregion - - + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/RedisCache/StackExchangeRedisExtensions.cs b/src/Surging.Core/Surging.Core.Caching/RedisCache/StackExchangeRedisExtensions.cs index f2df9e022..b35c0c147 100644 --- a/src/Surging.Core/Surging.Core.Caching/RedisCache/StackExchangeRedisExtensions.cs +++ b/src/Surging.Core/Surging.Core.Caching/RedisCache/StackExchangeRedisExtensions.cs @@ -2,40 +2,83 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Runtime.Serialization.Formatters.Binary; using System.Text; using System.Threading.Tasks; -using System.Linq; namespace Surging.Core.Caching.RedisCache { + /// + /// Defines the + /// public static class StackExchangeRedisExtensions { + #region 方法 + + /// + /// The Get + /// + /// + /// The cache + /// The key + /// The public static T Get(this IDatabase cache, string key) - { + { return Deserialize(cache.StringGet(key)); } - public static void Remove(this IDatabase cache, string key) - { - cache.KeyDelete(key); + /// + /// The Get + /// + /// The cache + /// The key + /// The + public static object Get(this IDatabase cache, string key) + { + return Deserialize(cache.StringGet(key)); } - public static async Task RemoveAsync(this IDatabase cache, string key) + /// + /// The GetAsync + /// + /// + /// The cache + /// The key + /// The + public static async Task GetAsync(this IDatabase cache, string key) { - await cache.KeyDeleteAsync(key); + return Deserialize(await cache.StringGetAsync(key)); } - public static async Task> GetManyAsync(this IDatabase cache, IEnumerable cacheKeys) + /// + /// The GetAsync + /// + /// The cache + /// The key + /// The + public static async Task GetAsync(this IDatabase cache, string key) + { + return Deserialize(await cache.StringGetAsync(key)); + } + + /// + /// The GetMany + /// + /// + /// The cache + /// The cacheKeys + /// The + public static IDictionary GetMany(this IDatabase cache, IEnumerable cacheKeys) { var arrayKeys = cacheKeys.ToArray(); var result = new Dictionary(); var keys = new RedisKey[cacheKeys.Count()]; - for(var i=0;i< arrayKeys.Count();i++) + for (var i = 0; i < arrayKeys.Count(); i++) { keys[i] = arrayKeys[i]; } - var values= await cache.StringGetAsync(keys); + var values = cache.StringGet(keys); for (var i = 0; i < values.Length; i++) { result.Add(keys[i], Deserialize(values[i])); @@ -43,7 +86,14 @@ public static async Task> GetManyAsync(this IDatabase return result; } - public static IDictionary GetMany(this IDatabase cache, IEnumerable cacheKeys) + /// + /// The GetManyAsync + /// + /// + /// The cache + /// The cacheKeys + /// The + public static async Task> GetManyAsync(this IDatabase cache, IEnumerable cacheKeys) { var arrayKeys = cacheKeys.ToArray(); var result = new Dictionary(); @@ -52,7 +102,7 @@ public static IDictionary GetMany(this IDatabase cache, IEnumerabl { keys[i] = arrayKeys[i]; } - var values = cache.StringGet(keys); + var values = await cache.StringGetAsync(keys); for (var i = 0; i < values.Length; i++) { result.Add(keys[i], Deserialize(values[i])); @@ -60,71 +110,117 @@ public static IDictionary GetMany(this IDatabase cache, IEnumerabl return result; } - public static async Task GetAsync(this IDatabase cache, string key) - { - return Deserialize(await cache.StringGetAsync(key)); - } - - public static object Get(this IDatabase cache, string key) + /// + /// The Remove + /// + /// The cache + /// The key + public static void Remove(this IDatabase cache, string key) { - return Deserialize(cache.StringGet(key)); + cache.KeyDelete(key); } - public static async Task GetAsync(this IDatabase cache, string key) + /// + /// The RemoveAsync + /// + /// The cache + /// The key + /// The + public static async Task RemoveAsync(this IDatabase cache, string key) { - return Deserialize(await cache.StringGetAsync(key)); + await cache.KeyDeleteAsync(key); } + /// + /// The Set + /// + /// The cache + /// The key + /// The value public static void Set(this IDatabase cache, string key, object value) { cache.StringSet(key, Serialize(value)); } - public static async Task SetAsync(this IDatabase cache, string key, object value) + /// + /// The Set + /// + /// The cache + /// The key + /// The value + /// The timeSpan + public static void Set(this IDatabase cache, string key, object value, TimeSpan timeSpan) { - await cache.StringSetAsync(key, Serialize(value)); + cache.StringSet(key, Serialize(value), timeSpan); } - public static void Set(this IDatabase cache, string key, object value,TimeSpan timeSpan) + /// + /// The SetAsync + /// + /// The cache + /// The key + /// The value + /// The + public static async Task SetAsync(this IDatabase cache, string key, object value) { - cache.StringSet(key, Serialize(value),timeSpan); + await cache.StringSetAsync(key, Serialize(value)); } + /// + /// The SetAsync + /// + /// The cache + /// The key + /// The value + /// The timeSpan + /// The public static async Task SetAsync(this IDatabase cache, string key, object value, TimeSpan timeSpan) { - await cache.StringSetAsync(key, Serialize(value), timeSpan); + await cache.StringSetAsync(key, Serialize(value), timeSpan); } - - static byte[] Serialize(object o) + /// + /// The Deserialize + /// + /// + /// The stream + /// The + internal static T Deserialize(byte[] stream) { - if (o == null) + if (stream == null) { - return null; + return default(T); } BinaryFormatter binaryFormatter = new BinaryFormatter(); - using (MemoryStream memoryStream = new MemoryStream()) + using (MemoryStream memoryStream = new MemoryStream(stream)) { - binaryFormatter.Serialize(memoryStream, o); - byte[] objectDataAsStream = memoryStream.ToArray(); - return objectDataAsStream; + T result = (T)binaryFormatter.Deserialize(memoryStream); + return result; } } - static T Deserialize(byte[] stream) + /// + /// The Serialize + /// + /// The o + /// The + internal static byte[] Serialize(object o) { - if (stream == null) + if (o == null) { - return default(T); + return null; } BinaryFormatter binaryFormatter = new BinaryFormatter(); - using (MemoryStream memoryStream = new MemoryStream(stream)) + using (MemoryStream memoryStream = new MemoryStream()) { - T result = (T)binaryFormatter.Deserialize(memoryStream); - return result; + binaryFormatter.Serialize(memoryStream, o); + byte[] objectDataAsStream = memoryStream.ToArray(); + return objectDataAsStream; } } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/ServiceHostBuilderExtensions.cs b/src/Surging.Core/Surging.Core.Caching/ServiceHostBuilderExtensions.cs index 52f03ff2b..51615efe1 100644 --- a/src/Surging.Core/Surging.Core.Caching/ServiceHostBuilderExtensions.cs +++ b/src/Surging.Core/Surging.Core.Caching/ServiceHostBuilderExtensions.cs @@ -1,19 +1,29 @@ -using Microsoft.Extensions.Configuration; +using Autofac; +using Microsoft.Extensions.Configuration; +using Surging.Core.Caching.Configurations; +using Surging.Core.Caching.Interfaces; using Surging.Core.Caching.Models; +using Surging.Core.CPlatform.Cache; using Surging.Core.ServiceHosting.Internal; using System; using System.Collections.Generic; using System.Linq; -using Autofac; using System.Reflection; -using Surging.Core.Caching.Interfaces; -using Surging.Core.CPlatform.Cache; -using Surging.Core.Caching.Configurations; namespace Surging.Core.Caching { + /// + /// Defines the + /// public static class ServiceHostBuilderExtensions { + #region 方法 + + /// + /// The UseServiceCache + /// + /// The hostBuilder + /// The public static IServiceHostBuilder UseServiceCache(this IServiceHostBuilder hostBuilder) { return hostBuilder.MapServices(mapper => @@ -24,6 +34,7 @@ public static IServiceHostBuilder UseServiceCache(this IServiceHostBuilder hostB mapper.Resolve(); }); } - + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/Utilities/CacheException.cs b/src/Surging.Core/Surging.Core.Caching/Utilities/CacheException.cs index f087015f2..0324a80fd 100644 --- a/src/Surging.Core/Surging.Core.Caching/Utilities/CacheException.cs +++ b/src/Surging.Core/Surging.Core.Caching/Utilities/CacheException.cs @@ -4,17 +4,22 @@ namespace Surging.Core.Caching.Utilities { + /// + /// Defines the + /// internal class CacheException : Exception { + #region 构造函数 + /// - /// 初始化 System.Exception 类的新实例。 + /// Initializes a new instance of the class. /// public CacheException() { } /// - /// 使用指定的错误信息初始化 System.Exception 类的新实例。 + /// Initializes a new instance of the class. /// /// 错误信息 public CacheException(string message) @@ -24,7 +29,7 @@ public CacheException(string message) } /// - /// 使用指定错误消息和对作为此异常原因的内部异常的引用来初始化 System.Exception 类的新实例。 + /// Initializes a new instance of the class. /// /// 解释异常原因的错误信息。 /// 导致当前异常的异常;如果未指定内部异常,则是一个 null 引用 @@ -34,13 +39,16 @@ public CacheException(string message, Exception e) Message = message; } + #endregion 构造函数 + + #region 属性 + /// + /// Gets or sets the Message /// 错误信息 /// - private new string Message - { - get; - set; - } + private new string Message { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/Utilities/Check.cs b/src/Surging.Core/Surging.Core.Caching/Utilities/Check.cs index e00526e88..d0fc845ea 100644 --- a/src/Surging.Core/Surging.Core.Caching/Utilities/Check.cs +++ b/src/Surging.Core/Surging.Core.Caching/Utilities/Check.cs @@ -4,28 +4,46 @@ namespace Surging.Core.Caching.Utilities { + /// + /// Defines the + /// public sealed class Check { - public static T NotNull(T value, string parameterName) where T : class + #region 方法 + + /// + /// The CheckCondition + /// + /// The condition + /// The parameterName + public static void CheckCondition(Func condition, string parameterName) { - if (value == null) + if (condition.Invoke()) { - throw new ArgumentNullException(parameterName); + throw new ArgumentException(string.Format(CachingResources.ArgumentIsNullOrWhitespace, parameterName)); } - - return value; } - public static T? NotNull(T? value, string parameterName) where T : struct + /// + /// The CheckCondition + /// + /// The condition + /// The formatErrorText + /// The parameters + public static void CheckCondition(Func condition, string formatErrorText, params string[] parameters) { - if (value == null) + if (condition.Invoke()) { - throw new ArgumentNullException(parameterName); + throw new ArgumentException(string.Format(CachingResources.ArgumentIsNullOrWhitespace, parameters)); } - - return value; } + /// + /// The NotEmpty + /// + /// The value + /// The parameterName + /// The public static string NotEmpty(string value, string parameterName) { if (string.IsNullOrWhiteSpace(value)) @@ -36,20 +54,40 @@ public static string NotEmpty(string value, string parameterName) return value; } - public static void CheckCondition(Func condition, string parameterName) + /// + /// The NotNull + /// + /// + /// The value + /// The parameterName + /// The + public static T NotNull(T value, string parameterName) where T : class { - if (condition.Invoke()) + if (value == null) { - throw new ArgumentException(string.Format(CachingResources.ArgumentIsNullOrWhitespace, parameterName)); + throw new ArgumentNullException(parameterName); } + + return value; } - public static void CheckCondition(Func condition,string formatErrorText, params string [] parameters) + /// + /// The NotNull + /// + /// + /// The value + /// The parameterName + /// The + public static T? NotNull(T? value, string parameterName) where T : struct { - if (condition.Invoke()) + if (value == null) { - throw new ArgumentException(string.Format(CachingResources.ArgumentIsNullOrWhitespace, parameters)); + throw new ArgumentNullException(parameterName); } + + return value; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Caching/Utilities/DebugCheck.cs b/src/Surging.Core/Surging.Core.Caching/Utilities/DebugCheck.cs index d7fdf0844..8ac57437e 100644 --- a/src/Surging.Core/Surging.Core.Caching/Utilities/DebugCheck.cs +++ b/src/Surging.Core/Surging.Core.Caching/Utilities/DebugCheck.cs @@ -2,24 +2,45 @@ namespace Surging.Core.Caching.Utilities { + /// + /// Defines the + /// public sealed class DebugCheck { + #region 方法 + + /// + /// The NotEmpty + /// + /// The value [Conditional("DEBUG")] - public static void NotNull(T value) where T : class + public static void NotEmpty(string value) { - Debug.Assert(value != null); + Debug.Assert(!string.IsNullOrWhiteSpace(value)); } + /// + /// The NotNull + /// + /// + /// The value [Conditional("DEBUG")] - public static void NotNull(T? value) where T : struct + public static void NotNull(T value) where T : class { Debug.Assert(value != null); } + /// + /// The NotNull + /// + /// + /// The value [Conditional("DEBUG")] - public static void NotEmpty(string value) + public static void NotNull(T? value) where T : struct { - Debug.Assert(!string.IsNullOrWhiteSpace(value)); + Debug.Assert(value != null); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Codec.MessagePack/ContainerBuilderExtensions.cs b/src/Surging.Core/Surging.Core.Codec.MessagePack/ContainerBuilderExtensions.cs index 872250146..29dea0b40 100644 --- a/src/Surging.Core/Surging.Core.Codec.MessagePack/ContainerBuilderExtensions.cs +++ b/src/Surging.Core/Surging.Core.Codec.MessagePack/ContainerBuilderExtensions.cs @@ -2,10 +2,15 @@ namespace Surging.Core.Codec.MessagePack { + /// + /// Defines the + /// public static class ContainerBuilderExtensions { + #region 方法 + /// - /// 使用messagepack编码解码方式 + /// 使用messagepack编码解码方式 /// /// /// @@ -13,5 +18,7 @@ public static IServiceBuilder UseMessagePackCodec(this IServiceBuilder builder) { return builder.UseCodec(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Codec.MessagePack/MessagePackModule.cs b/src/Surging.Core/Surging.Core.Codec.MessagePack/MessagePackModule.cs index 33ebadba9..ba903172c 100644 --- a/src/Surging.Core/Surging.Core.Codec.MessagePack/MessagePackModule.cs +++ b/src/Surging.Core/Surging.Core.Codec.MessagePack/MessagePackModule.cs @@ -7,8 +7,17 @@ namespace Surging.Core.Codec.MessagePack { - public class MessagePackModule : EnginePartModule + /// + /// Defines the + /// + public class MessagePackModule : EnginePartModule { + #region 方法 + + /// + /// The Initialize + /// + /// The context public override void Initialize(AppModuleContext context) { base.Initialize(context); @@ -23,5 +32,7 @@ protected override void RegisterBuilder(ContainerBuilderWrapper builder) base.RegisterBuilder(builder); builder.RegisterType().As().SingleInstance(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Codec.MessagePack/MessagePackTransportMessageCodecFactory.cs b/src/Surging.Core/Surging.Core.Codec.MessagePack/MessagePackTransportMessageCodecFactory.cs index 7e4ddec46..381fe225c 100644 --- a/src/Surging.Core/Surging.Core.Codec.MessagePack/MessagePackTransportMessageCodecFactory.cs +++ b/src/Surging.Core/Surging.Core.Codec.MessagePack/MessagePackTransportMessageCodecFactory.cs @@ -3,36 +3,49 @@ namespace Surging.Core.Codec.MessagePack { + /// + /// Defines the + /// public sealed class MessagePackTransportMessageCodecFactory : ITransportMessageCodecFactory { - #region Field - private readonly ITransportMessageEncoder _transportMessageEncoder = new MessagePackTransportMessageEncoder(); + #region 字段 + + /// + /// Defines the _transportMessageDecoder + /// private readonly ITransportMessageDecoder _transportMessageDecoder = new MessagePackTransportMessageDecoder(); - #endregion Field - #region Implementation of ITransportMessageCodecFactory + /// + /// Defines the _transportMessageEncoder + /// + private readonly ITransportMessageEncoder _transportMessageEncoder = new MessagePackTransportMessageEncoder(); + + #endregion 字段 + + #region 方法 /// /// - /// 获取编码器 + /// 获取解码器 /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ITransportMessageEncoder GetEncoder() + public ITransportMessageDecoder GetDecoder() { - return _transportMessageEncoder; + return _transportMessageDecoder; } /// /// - /// 获取解码器 + /// 获取编码器 /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ITransportMessageDecoder GetDecoder() + public ITransportMessageEncoder GetEncoder() { - return _transportMessageDecoder; + return _transportMessageEncoder; } - #endregion Implementation of ITransportMessageCodecFactory + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Codec.MessagePack/MessagePackTransportMessageDecoder.cs b/src/Surging.Core/Surging.Core.Codec.MessagePack/MessagePackTransportMessageDecoder.cs index 9bf7e28d5..ee0614750 100644 --- a/src/Surging.Core/Surging.Core.Codec.MessagePack/MessagePackTransportMessageDecoder.cs +++ b/src/Surging.Core/Surging.Core.Codec.MessagePack/MessagePackTransportMessageDecoder.cs @@ -6,10 +6,18 @@ namespace Surging.Core.Codec.MessagePack { + /// + /// Defines the + /// public sealed class MessagePackTransportMessageDecoder : ITransportMessageDecoder { - #region Implementation of ITransportMessageDecoder + #region 方法 + /// + /// The Decode + /// + /// The data + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] public TransportMessage Decode(byte[] data) { @@ -17,6 +25,6 @@ public TransportMessage Decode(byte[] data) return message.GetTransportMessage(); } - #endregion Implementation of ITransportMessageDecoder + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Codec.MessagePack/MessagePackTransportMessageEncoder.cs b/src/Surging.Core/Surging.Core.Codec.MessagePack/MessagePackTransportMessageEncoder.cs index a7ae1366a..54007ef4c 100644 --- a/src/Surging.Core/Surging.Core.Codec.MessagePack/MessagePackTransportMessageEncoder.cs +++ b/src/Surging.Core/Surging.Core.Codec.MessagePack/MessagePackTransportMessageEncoder.cs @@ -6,10 +6,18 @@ namespace Surging.Core.Codec.MessagePack { - public sealed class MessagePackTransportMessageEncoder:ITransportMessageEncoder + /// + /// Defines the + /// + public sealed class MessagePackTransportMessageEncoder : ITransportMessageEncoder { - #region Implementation of ITransportMessageEncoder + #region 方法 + /// + /// The Encode + /// + /// The message + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] public byte[] Encode(TransportMessage message) { @@ -20,6 +28,7 @@ public byte[] Encode(TransportMessage message) }; return SerializerUtilitys.Serialize(transportMessage); } - #endregion Implementation of ITransportMessageEncoder + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Codec.MessagePack/Messages/DynamicItem.cs b/src/Surging.Core/Surging.Core.Codec.MessagePack/Messages/DynamicItem.cs index b502ad03a..d41d27735 100644 --- a/src/Surging.Core/Surging.Core.Codec.MessagePack/Messages/DynamicItem.cs +++ b/src/Surging.Core/Surging.Core.Codec.MessagePack/Messages/DynamicItem.cs @@ -8,14 +8,25 @@ namespace Surging.Core.Codec.MessagePack.Messages { + /// + /// Defines the + /// [MessagePackObject] public class DynamicItem { - #region Constructor + #region 캯 + /// + /// Initializes a new instance of the class. + /// public DynamicItem() - { } + { + } + /// + /// Initializes a new instance of the class. + /// + /// The value [MethodImpl(MethodImplOptions.AggressiveInlining)] public DynamicItem(object value) { @@ -25,7 +36,7 @@ public DynamicItem(object value) var valueType = value.GetType(); var code = Type.GetTypeCode(valueType); - if (code != TypeCode.Object && valueType.BaseType!=typeof(Enum)) + if (code != TypeCode.Object && valueType.BaseType != typeof(Enum)) TypeName = valueType.FullName; else TypeName = valueType.AssemblyQualifiedName; @@ -36,18 +47,30 @@ public DynamicItem(object value) Content = SerializerUtilitys.Serialize(value); } - #endregion Constructor + #endregion 캯 + + #region - #region Property + /// + /// Gets or sets the Content + /// + [Key(1)] + public byte[] Content { get; set; } + /// + /// Gets or sets the TypeName + /// [Key(0)] public string TypeName { get; set; } - [Key(1)] - public byte[] Content { get; set; } - #endregion Property + #endregion - #region Public Method + #region + + /// + /// The Get + /// + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] public object Get() { @@ -65,6 +88,7 @@ public object Get() return SerializerUtilitys.Deserialize(Content, typeName); } } - #endregion Public Method + + #endregion } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Codec.MessagePack/Messages/MessagePackRemoteInvokeMessage.cs b/src/Surging.Core/Surging.Core.Codec.MessagePack/Messages/MessagePackRemoteInvokeMessage.cs index c72619231..6ba9756af 100644 --- a/src/Surging.Core/Surging.Core.Codec.MessagePack/Messages/MessagePackRemoteInvokeMessage.cs +++ b/src/Surging.Core/Surging.Core.Codec.MessagePack/Messages/MessagePackRemoteInvokeMessage.cs @@ -7,33 +7,25 @@ namespace Surging.Core.Codec.MessagePack.Messages { + /// + /// Defines the + /// [MessagePackObject] - public class ParameterItem + public class MessagePackRemoteInvokeMessage { - #region Constructor + #region 캯 - public ParameterItem(KeyValuePair item) - { - Key = item.Key; - Value = item.Value == null ? null : new DynamicItem(item.Value); - } - - public ParameterItem() + /// + /// Initializes a new instance of the class. + /// + public MessagePackRemoteInvokeMessage() { } - #endregion Constructor - - [Key(0)] - public string Key { get; set; } - - [Key(1)] - public DynamicItem Value { get; set; } - } - - [MessagePackObject] - public class MessagePackRemoteInvokeMessage - { + /// + /// Initializes a new instance of the class. + /// + /// The message public MessagePackRemoteInvokeMessage(RemoteInvokeMessage message) { ServiceId = message.ServiceId; @@ -43,28 +35,54 @@ public MessagePackRemoteInvokeMessage(RemoteInvokeMessage message) Attachments = message.Attachments?.Select(i => new ParameterItem(i)).ToArray(); } - public MessagePackRemoteInvokeMessage() - { - } + #endregion 캯 - [Key(0)] - public string ServiceId { get; set; } + #region - [Key(1)] - public string Token { get; set; } + /// + /// Gets or sets the Attachments + /// + [Key(5)] + public ParameterItem[] Attachments { get; set; } + /// + /// Gets or sets a value indicating whether DecodeJOject + /// [Key(2)] public bool DecodeJOject { get; set; } + /// + /// Gets or sets the Parameters + /// + [Key(4)] + public ParameterItem[] Parameters { get; set; } + + /// + /// Gets or sets the ServiceId + /// + [Key(0)] + public string ServiceId { get; set; } + + /// + /// Gets or sets the ServiceKey + /// [Key(3)] public string ServiceKey { get; set; } - [Key(4)] - public ParameterItem[] Parameters { get; set; } + /// + /// Gets or sets the Token + /// + [Key(1)] + public string Token { get; set; } - [Key(5)] - public ParameterItem[] Attachments { get; set; } + #endregion + + #region + /// + /// The GetRemoteInvokeMessage + /// + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] public RemoteInvokeMessage GetRemoteInvokeMessage() { @@ -77,5 +95,51 @@ public RemoteInvokeMessage GetRemoteInvokeMessage() ServiceKey = ServiceKey, }; } + + #endregion + } + + /// + /// Defines the + /// + [MessagePackObject] + public class ParameterItem + { + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + public ParameterItem() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The item + public ParameterItem(KeyValuePair item) + { + Key = item.Key; + Value = item.Value == null ? null : new DynamicItem(item.Value); + } + + #endregion 캯 + + #region + + /// + /// Gets or sets the Key + /// + [Key(0)] + public string Key { get; set; } + + /// + /// Gets or sets the Value + /// + [Key(1)] + public DynamicItem Value { get; set; } + + #endregion } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Codec.MessagePack/Messages/MessagePackRemoteInvokeResultMessage.cs b/src/Surging.Core/Surging.Core.Codec.MessagePack/Messages/MessagePackRemoteInvokeResultMessage.cs index 52aa524b2..7d4b7dc03 100644 --- a/src/Surging.Core/Surging.Core.Codec.MessagePack/Messages/MessagePackRemoteInvokeResultMessage.cs +++ b/src/Surging.Core/Surging.Core.Codec.MessagePack/Messages/MessagePackRemoteInvokeResultMessage.cs @@ -4,29 +4,55 @@ namespace Surging.Core.Codec.MessagePack.Messages { + /// + /// Defines the + /// [MessagePackObject] public class MessagePackRemoteInvokeResultMessage { - #region Constructor + #region 캯 + /// + /// Initializes a new instance of the class. + /// + public MessagePackRemoteInvokeResultMessage() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message public MessagePackRemoteInvokeResultMessage(RemoteInvokeResultMessage message) { ExceptionMessage = message.ExceptionMessage; Result = message.Result == null ? null : new DynamicItem(message.Result); } - public MessagePackRemoteInvokeResultMessage() - { - } + #endregion 캯 - #endregion Constructor + #region + /// + /// Gets or sets the ExceptionMessage + /// [Key(0)] public string ExceptionMessage { get; set; } + /// + /// Gets or sets the Result + /// [Key(1)] public DynamicItem Result { get; set; } + #endregion + + #region + + /// + /// The GetRemoteInvokeResultMessage + /// + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] public RemoteInvokeResultMessage GetRemoteInvokeResultMessage() { @@ -36,6 +62,7 @@ public RemoteInvokeResultMessage GetRemoteInvokeResultMessage() Result = Result?.Get() }; } - } -} + #endregion + } +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Codec.MessagePack/Messages/MessagePackTransportMessage.cs b/src/Surging.Core/Surging.Core.Codec.MessagePack/Messages/MessagePackTransportMessage.cs index 947ddbc6a..2c0142420 100644 --- a/src/Surging.Core/Surging.Core.Codec.MessagePack/Messages/MessagePackTransportMessage.cs +++ b/src/Surging.Core/Surging.Core.Codec.MessagePack/Messages/MessagePackTransportMessage.cs @@ -6,9 +6,25 @@ namespace Surging.Core.Codec.MessagePack.Messages { + /// + /// Defines the + /// [MessagePackObject] public class MessagePackTransportMessage { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + public MessagePackTransportMessage() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The transportMessage public MessagePackTransportMessage(TransportMessage transportMessage) { Id = transportMessage.Id; @@ -31,31 +47,36 @@ public MessagePackTransportMessage(TransportMessage transportMessage) Content = SerializerUtilitys.Serialize(contentObject); } - public MessagePackTransportMessage() - { - } + #endregion 构造函数 - [Key(0)] - public string Id { get; set; } + #region 属性 + /// + /// Gets or sets the Content + /// [Key(1)] public byte[] Content { get; set; } + /// + /// Gets or sets the ContentType + /// [Key(2)] public string ContentType { get; set; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool IsInvokeMessage() - { - return ContentType == MessagePackTransportMessageType.remoteInvokeMessageTypeName; - } + /// + /// Gets or sets the Id + /// + [Key(0)] + public string Id { get; set; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool IsInvokeResultMessage() - { - return ContentType == MessagePackTransportMessageType.remoteInvokeResultMessageTypeName; - } + #endregion 属性 + + #region 方法 + /// + /// The GetTransportMessage + /// + /// The public TransportMessage GetTransportMessage() { var message = new TransportMessage @@ -84,5 +105,27 @@ public TransportMessage GetTransportMessage() message.Content = contentObject; return message; } + + /// + /// The IsInvokeMessage + /// + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsInvokeMessage() + { + return ContentType == MessagePackTransportMessageType.remoteInvokeMessageTypeName; + } + + /// + /// The IsInvokeResultMessage + /// + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsInvokeResultMessage() + { + return ContentType == MessagePackTransportMessageType.remoteInvokeResultMessageTypeName; + } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Codec.MessagePack/Utilities/SerializerUtilitys.cs b/src/Surging.Core/Surging.Core.Codec.MessagePack/Utilities/SerializerUtilitys.cs index d7472e9ec..2d732e91b 100644 --- a/src/Surging.Core/Surging.Core.Codec.MessagePack/Utilities/SerializerUtilitys.cs +++ b/src/Surging.Core/Surging.Core.Codec.MessagePack/Utilities/SerializerUtilitys.cs @@ -5,31 +5,69 @@ namespace Surging.Core.Codec.MessagePack.Utilities { + /// + /// Defines the + /// public class SerializerUtilitys { + #region 캯 + + /// + /// Initializes static members of the class. + /// static SerializerUtilitys() { CompositeResolver.RegisterAndSetAsDefault(NativeDateTimeResolver.Instance, ContractlessStandardResolverAllowPrivate.Instance); } - public static byte[] Serialize(T instance) - { - return MessagePackSerializer.Serialize(instance); - } + #endregion 캯 - public static byte[] Serialize(object instance, Type type) + #region + + /// + /// The Deserialize + /// + /// + /// The data + /// The + public static T Deserialize(byte[] data) { - return MessagePackSerializer.Serialize(instance); + return data == null ? default(T) : MessagePackSerializer.Deserialize(data); } + /// + /// The Deserialize + /// + /// The data + /// The type + /// The public static object Deserialize(byte[] data, Type type) { return data == null ? null : MessagePackSerializer.NonGeneric.Deserialize(type, data); } - public static T Deserialize(byte[] data) + /// + /// The Serialize + /// + /// The instance + /// The type + /// The + public static byte[] Serialize(object instance, Type type) { - return data == null ? default(T) : MessagePackSerializer.Deserialize(data); + return MessagePackSerializer.Serialize(instance); + } + + /// + /// The Serialize + /// + /// + /// The instance + /// The + public static byte[] Serialize(T instance) + { + return MessagePackSerializer.Serialize(instance); } + + #endregion } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/ContainerBuilderExtensions.cs b/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/ContainerBuilderExtensions.cs index c53509f9e..eeeffc4a8 100644 --- a/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/ContainerBuilderExtensions.cs +++ b/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/ContainerBuilderExtensions.cs @@ -5,11 +5,23 @@ namespace Surging.Core.Codec.ProtoBuffer { - public static class ContainerBuilderExtensions + /// + /// Defines the + /// + public static class ContainerBuilderExtensions { + #region 方法 + + /// + /// The UseProtoBufferCodec + /// + /// The builder + /// The public static IServiceBuilder UseProtoBufferCodec(this IServiceBuilder builder) { return builder.UseCodec(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/Messages/DynamicItem.cs b/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/Messages/DynamicItem.cs index fbfa286bc..75274cb9e 100644 --- a/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/Messages/DynamicItem.cs +++ b/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/Messages/DynamicItem.cs @@ -8,15 +8,25 @@ namespace Surging.Core.Codec.ProtoBuffer.Messages { + /// + /// Defines the + /// [ProtoContract] public class DynamicItem { - #region Constructor + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// public DynamicItem() { } + /// + /// Initializes a new instance of the class. + /// + /// The value public DynamicItem(object value) { if (value == null) @@ -35,17 +45,30 @@ public DynamicItem(object value) Content = SerializerUtilitys.Serialize(value); } - #endregion Constructor + #endregion 构造函数 - #region Property + #region 属性 - [ProtoMember(1)] - public string TypeName { get; set; } + /// + /// Gets or sets the Content + /// [ProtoMember(2)] public byte[] Content { get; set; } - #endregion Property - #region Public Method + /// + /// Gets or sets the TypeName + /// + [ProtoMember(1)] + public string TypeName { get; set; } + + #endregion 属性 + + #region 方法 + + /// + /// The Get + /// + /// The public object Get() { if (Content == null || TypeName == null) @@ -60,8 +83,8 @@ public object Get() { return SerializerUtilitys.Deserialize(Content, typeName); } - } - #endregion Public Method + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/Messages/ProtoBufferRemoteInvokeMessage.cs b/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/Messages/ProtoBufferRemoteInvokeMessage.cs index 2399c984a..a8fd769bc 100644 --- a/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/Messages/ProtoBufferRemoteInvokeMessage.cs +++ b/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/Messages/ProtoBufferRemoteInvokeMessage.cs @@ -7,66 +7,126 @@ namespace Surging.Core.Codec.ProtoBuffer.Messages { + /// + /// Defines the + /// [ProtoContract] public class ParameterItem { - #region Constructor + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + public ParameterItem() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The item public ParameterItem(KeyValuePair item) { Key = item.Key; Value = item.Value == null ? null : new DynamicItem(item.Value); } - public ParameterItem() - { - } + #endregion 构造函数 - #endregion Constructor + #region 属性 + /// + /// Gets or sets the Key + /// [ProtoMember(1)] public string Key { get; set; } + /// + /// Gets or sets the Value + /// [ProtoMember(2)] public DynamicItem Value { get; set; } + + #endregion 属性 } + /// + /// Defines the + /// [ProtoContract] public class ProtoBufferRemoteInvokeMessage { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + public ProtoBufferRemoteInvokeMessage() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message public ProtoBufferRemoteInvokeMessage(RemoteInvokeMessage message) { - ServiceId = message.ServiceId; + ServiceId = message.ServiceId; DecodeJOject = message.DecodeJOject; ServiceKey = message.ServiceKey; Parameters = message.Parameters?.Select(i => new ParameterItem(i)).ToArray(); Attachments = message.Attachments?.Select(i => new ParameterItem(i)).ToArray(); - } - public ProtoBufferRemoteInvokeMessage() - { - } + #endregion 构造函数 - [ProtoMember(1)] - public string ServiceId { get; set; } + #region 属性 - [ProtoMember(2)] - public string Token { get; set; } + /// + /// Gets or sets the Attachments + /// + [ProtoMember(6)] + public ParameterItem[] Attachments { get; set; } + /// + /// Gets or sets a value indicating whether DecodeJOject + /// [ProtoMember(3)] - public bool DecodeJOject{ get; set; } + public bool DecodeJOject { get; set; } + + /// + /// Gets or sets the Parameters + /// + [ProtoMember(5)] + public ParameterItem[] Parameters { get; set; } + + /// + /// Gets or sets the ServiceId + /// + [ProtoMember(1)] + public string ServiceId { get; set; } + /// + /// Gets or sets the ServiceKey + /// [ProtoMember(4)] public string ServiceKey { get; set; } - [ProtoMember(5)] - public ParameterItem[] Parameters { get; set; } + /// + /// Gets or sets the Token + /// + [ProtoMember(2)] + public string Token { get; set; } - [ProtoMember(6)] - public ParameterItem[] Attachments { get; set; } + #endregion 属性 + #region 方法 + /// + /// The GetRemoteInvokeMessage + /// + /// The public RemoteInvokeMessage GetRemoteInvokeMessage() { return new RemoteInvokeMessage @@ -75,8 +135,10 @@ public RemoteInvokeMessage GetRemoteInvokeMessage() Attachments = Attachments?.ToDictionary(i => i.Key, i => i.Value?.Get()), ServiceId = ServiceId, DecodeJOject = DecodeJOject, - ServiceKey = ServiceKey, + ServiceKey = ServiceKey, }; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/Messages/ProtoBufferRemoteInvokeResultMessage.cs b/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/Messages/ProtoBufferRemoteInvokeResultMessage.cs index 891c3ff8f..494c06665 100644 --- a/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/Messages/ProtoBufferRemoteInvokeResultMessage.cs +++ b/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/Messages/ProtoBufferRemoteInvokeResultMessage.cs @@ -6,30 +6,55 @@ namespace Surging.Core.Codec.ProtoBuffer.Messages { + /// + /// Defines the + /// [ProtoContract] - public class ProtoBufferRemoteInvokeResultMessage + public class ProtoBufferRemoteInvokeResultMessage { - #region Constructor + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + public ProtoBufferRemoteInvokeResultMessage() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message public ProtoBufferRemoteInvokeResultMessage(RemoteInvokeResultMessage message) { ExceptionMessage = message.ExceptionMessage; Result = message.Result == null ? null : new DynamicItem(message.Result); } - public ProtoBufferRemoteInvokeResultMessage() - { - } + #endregion 构造函数 + + #region 属性 - #endregion Constructor - + /// + /// Gets or sets the ExceptionMessage + /// [ProtoMember(1)] public string ExceptionMessage { get; set; } - + /// + /// Gets or sets the Result + /// [ProtoMember(2)] public DynamicItem Result { get; set; } + #endregion 属性 + + #region 方法 + + /// + /// The GetRemoteInvokeResultMessage + /// + /// The public RemoteInvokeResultMessage GetRemoteInvokeResultMessage() { return new RemoteInvokeResultMessage @@ -38,6 +63,7 @@ public RemoteInvokeResultMessage GetRemoteInvokeResultMessage() Result = Result?.Get() }; } - } -} + #endregion 方法 + } +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/Messages/ProtoBufferTransportMessage.cs b/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/Messages/ProtoBufferTransportMessage.cs index 9f737999b..284d8b0c7 100644 --- a/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/Messages/ProtoBufferTransportMessage.cs +++ b/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/Messages/ProtoBufferTransportMessage.cs @@ -7,9 +7,25 @@ namespace Surging.Core.Codec.ProtoBuffer.Messages { + /// + /// Defines the + /// [ProtoContract] public class ProtoBufferTransportMessage { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + public ProtoBufferTransportMessage() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The transportMessage public ProtoBufferTransportMessage(TransportMessage transportMessage) { Id = transportMessage.Id; @@ -32,30 +48,36 @@ public ProtoBufferTransportMessage(TransportMessage transportMessage) Content = SerializerUtilitys.Serialize(contentObject); } - public ProtoBufferTransportMessage() - { - } - - [ProtoMember(1)] - public string Id { get; set; } - + #endregion 构造函数 + + #region 属性 + + /// + /// Gets or sets the Content + /// [ProtoMember(2)] public byte[] Content { get; set; } + /// + /// Gets or sets the ContentType + /// [ProtoMember(3)] public string ContentType { get; set; } - - public bool IsInvokeMessage() - { - return ContentType == MessagePackTransportMessageType.remoteInvokeMessageTypeName; - } + /// + /// Gets or sets the Id + /// + [ProtoMember(1)] + public string Id { get; set; } - public bool IsInvokeResultMessage() - { - return ContentType == MessagePackTransportMessageType.remoteInvokeResultMessageTypeName; - } + #endregion 属性 + #region 方法 + + /// + /// The GetTransportMessage + /// + /// The public TransportMessage GetTransportMessage() { var message = new TransportMessage @@ -86,5 +108,25 @@ public TransportMessage GetTransportMessage() return message; } + + /// + /// The IsInvokeMessage + /// + /// The + public bool IsInvokeMessage() + { + return ContentType == MessagePackTransportMessageType.remoteInvokeMessageTypeName; + } + + /// + /// The IsInvokeResultMessage + /// + /// The + public bool IsInvokeResultMessage() + { + return ContentType == MessagePackTransportMessageType.remoteInvokeResultMessageTypeName; + } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/ProtoBufferModule.cs b/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/ProtoBufferModule.cs index 861843233..545d6e197 100644 --- a/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/ProtoBufferModule.cs +++ b/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/ProtoBufferModule.cs @@ -7,8 +7,17 @@ namespace Surging.Core.Codec.ProtoBuffer { - public class ProtoBufferModule : EnginePartModule + /// + /// Defines the + /// + public class ProtoBufferModule : EnginePartModule { + #region 方法 + + /// + /// The Initialize + /// + /// The context public override void Initialize(AppModuleContext context) { base.Initialize(context); @@ -23,5 +32,7 @@ protected override void RegisterBuilder(ContainerBuilderWrapper builder) base.RegisterBuilder(builder); builder.RegisterType().As().SingleInstance(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/ProtoBufferTransportMessageCodecFactory.cs b/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/ProtoBufferTransportMessageCodecFactory.cs index d277d5a53..0656915a2 100644 --- a/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/ProtoBufferTransportMessageCodecFactory.cs +++ b/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/ProtoBufferTransportMessageCodecFactory.cs @@ -5,28 +5,45 @@ namespace Surging.Core.Codec.ProtoBuffer { - public sealed class ProtoBufferTransportMessageCodecFactory : ITransportMessageCodecFactory + /// + /// Defines the + /// + public sealed class ProtoBufferTransportMessageCodecFactory : ITransportMessageCodecFactory { - #region Field + #region 字段 - private readonly ITransportMessageEncoder _transportMessageEncoder = new ProtoBufferTransportMessageEncoder(); + /// + /// Defines the _transportMessageDecoder + /// private readonly ITransportMessageDecoder _transportMessageDecoder = new ProtoBufferTransportMessageDecoder(); - #endregion Field + /// + /// Defines the _transportMessageEncoder + /// + private readonly ITransportMessageEncoder _transportMessageEncoder = new ProtoBufferTransportMessageEncoder(); - #region Implementation of ITransportMessageCodecFactory - - public ITransportMessageEncoder GetEncoder() - { - return _transportMessageEncoder; - } - + #endregion 字段 + + #region 方法 + + /// + /// The GetDecoder + /// + /// The public ITransportMessageDecoder GetDecoder() { return _transportMessageDecoder; } - #endregion Implementation of ITransportMessageCodecFactory - } -} + /// + /// The GetEncoder + /// + /// The + public ITransportMessageEncoder GetEncoder() + { + return _transportMessageEncoder; + } + #endregion 方法 + } +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/ProtoBufferTransportMessageDecoder.cs b/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/ProtoBufferTransportMessageDecoder.cs index b97a32d48..d55942666 100644 --- a/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/ProtoBufferTransportMessageDecoder.cs +++ b/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/ProtoBufferTransportMessageDecoder.cs @@ -8,16 +8,24 @@ namespace Surging.Core.Codec.ProtoBuffer { - public sealed class ProtoBufferTransportMessageDecoder : ITransportMessageDecoder + /// + /// Defines the + /// + public sealed class ProtoBufferTransportMessageDecoder : ITransportMessageDecoder { - #region Implementation of ITransportMessageDecoder + #region 方法 + /// + /// The Decode + /// + /// The data + /// The public TransportMessage Decode(byte[] data) { var message = SerializerUtilitys.Deserialize(data); return message.GetTransportMessage(); } - #endregion Implementation of ITransportMessageDecoder + #endregion 方法 } -} \ No newline at end of file +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/ProtoBufferTransportMessageEncoder.cs b/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/ProtoBufferTransportMessageEncoder.cs index 9c68165e6..33025a610 100644 --- a/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/ProtoBufferTransportMessageEncoder.cs +++ b/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/ProtoBufferTransportMessageEncoder.cs @@ -8,10 +8,18 @@ namespace Surging.Core.Codec.ProtoBuffer { + /// + /// Defines the + /// public sealed class ProtoBufferTransportMessageEncoder : ITransportMessageEncoder { - #region Implementation of ITransportMessageEncoder + #region 方法 + /// + /// The Encode + /// + /// The message + /// The public byte[] Encode(TransportMessage message) { var transportMessage = new ProtoBufferTransportMessage(message) @@ -23,6 +31,6 @@ public byte[] Encode(TransportMessage message) return SerializerUtilitys.Serialize(transportMessage); } - #endregion Implementation of ITransportMessageEncoder + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/Utilities/SerializerUtilitys.cs b/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/Utilities/SerializerUtilitys.cs index c8b28f6af..35d86f8b8 100644 --- a/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/Utilities/SerializerUtilitys.cs +++ b/src/Surging.Core/Surging.Core.Codec.ProtoBuffer/Utilities/SerializerUtilitys.cs @@ -6,17 +6,35 @@ namespace Surging.Core.Codec.ProtoBuffer.Utilities { + /// + /// Defines the + /// public static class SerializerUtilitys { - public static byte[] Serialize(object instance) + #region 方法 + + /// + /// The Deserialize + /// + /// + /// The data + /// The + public static T Deserialize(byte[] data) { - using (var stream = new MemoryStream()) + if (data == null) + return default(T); + using (var stream = new MemoryStream(data)) { - Serializer.Serialize(stream, instance); - return stream.ToArray(); + return Serializer.Deserialize(stream); } } + /// + /// The Deserialize + /// + /// The data + /// The type + /// The public static object Deserialize(byte[] data, Type type) { if (data == null) @@ -27,14 +45,20 @@ public static object Deserialize(byte[] data, Type type) } } - public static T Deserialize(byte[] data) + /// + /// The Serialize + /// + /// The instance + /// The + public static byte[] Serialize(object instance) { - if (data == null) - return default(T); - using (var stream = new MemoryStream(data)) + using (var stream = new MemoryStream()) { - return Serializer.Deserialize(stream); + Serializer.Serialize(stream, instance); + return stream.ToArray(); } } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Common/ApiResult.cs b/src/Surging.Core/Surging.Core.Common/ApiResult.cs index 95f2ff913..a06cc3228 100644 --- a/src/Surging.Core/Surging.Core.Common/ApiResult.cs +++ b/src/Surging.Core/Surging.Core.Common/ApiResult.cs @@ -5,14 +5,27 @@ namespace Surging.Core.Common { + /// + /// Defines the + /// + /// [DataContract] public class ApiResult { + #region 属性 + /// + /// Gets or sets the StatusCode + /// [DataMember] public int StatusCode { get; set; } + /// + /// Gets or sets the Value + /// [DataMember] public T Value { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Common/CommonModule.cs b/src/Surging.Core/Surging.Core.Common/CommonModule.cs index 32900cda3..8adfb380e 100644 --- a/src/Surging.Core/Surging.Core.Common/CommonModule.cs +++ b/src/Surging.Core/Surging.Core.Common/CommonModule.cs @@ -5,7 +5,10 @@ namespace Surging.Core.Common { - public class CommonModule:SystemModule + /// + /// Defines the + /// + public class CommonModule : SystemModule { } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Common/Extensions/EnumExtensions.cs b/src/Surging.Core/Surging.Core.Common/Extensions/EnumExtensions.cs index 162953ace..3366867f6 100644 --- a/src/Surging.Core/Surging.Core.Common/Extensions/EnumExtensions.cs +++ b/src/Surging.Core/Surging.Core.Common/Extensions/EnumExtensions.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -using System.Reflection; using System.ComponentModel.DataAnnotations; +using System.Reflection; namespace Surging.Core.Common.Extensions { @@ -10,16 +10,7 @@ namespace Surging.Core.Common.Extensions /// public static class EnumExtensions { - /// - /// 获取对枚举的描述信息 - /// - /// 枚举 - /// 返回枚举的描述信息 - public static string GetDisplay(this Enum value) - { - var attr = value.GetAttribute(); - return attr == null ? "" : attr.Name; - } + #region 方法 /// /// 获取枚举的自定义属性 @@ -31,17 +22,17 @@ public static T GetAttribute(this Enum value) where T : Attribute { FieldInfo field = value.GetType().GetField(value.ToString()); return field.GetCustomAttribute(typeof(T)) as T; - } /// - /// 获取枚举的值 + /// 获取对枚举的描述信息 /// /// 枚举 - /// 返回枚举的值 - public static int GetValue(this Enum value) + /// 返回枚举的描述信息 + public static string GetDisplay(this Enum value) { - return Convert.ToInt32(value); + var attr = value.GetAttribute(); + return attr == null ? "" : attr.Name; } /// @@ -68,5 +59,17 @@ public static List> GetEnumSource(this Type type) } return list; } + + /// + /// 获取枚举的值 + /// + /// 枚举 + /// 返回枚举的值 + public static int GetValue(this Enum value) + { + return Convert.ToInt32(value); + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Common/ServicesException/ServiceException.cs b/src/Surging.Core/Surging.Core.Common/ServicesException/ServiceException.cs index 86d2b9af5..d15032237 100644 --- a/src/Surging.Core/Surging.Core.Common/ServicesException/ServiceException.cs +++ b/src/Surging.Core/Surging.Core.Common/ServicesException/ServiceException.cs @@ -1,90 +1,100 @@ -using System; -using Surging.Core.Common.Extensions; +using Surging.Core.Common.Extensions; +using System; namespace Surging.Core.Common.ServicesException { - public sealed class ServiceException : Exception + /// + /// Defines the + /// + public sealed class ServiceException : Exception + { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + public ServiceException() { - /// - /// 初始化 System.Exception 类的新实例。 - /// - public ServiceException() - { - } + } - /// - /// 使用指定的错误信息初始化 System.Exception 类的新实例。 - /// - /// 错误信息 - public ServiceException(string message) - : base(message) - { - Message = message; - } + /// + /// Initializes a new instance of the class. + /// + /// 错误号 + public ServiceException(Enum sysCode) + : base(sysCode.GetDisplay()) + { + this.Code = (int)Enum.Parse(sysCode.GetType(), sysCode.ToString()); + this.Message = sysCode.GetDisplay(); + } + /// + /// Initializes a new instance of the class. + /// + /// The sysCode + /// The message + public ServiceException(Enum sysCode, string message) + { + this.Code = (int)Enum.Parse(sysCode.GetType(), sysCode.ToString()); + this.Message = string.Format(message, sysCode.GetDisplay()); + } + /// + /// Initializes a new instance of the class. + /// + /// 错误信息 + public ServiceException(string message) + : base(message) + { + Message = message; + } - /// - /// 使用指定错误消息和对作为此异常原因的内部异常的引用来初始化 System.Exception 类的新实例。 - /// - /// 解释异常原因的错误信息。 - /// 导致当前异常的异常;如果未指定内部异常,则是一个 null 引用 - public ServiceException(string message, Exception e) + /// + /// Initializes a new instance of the class. + /// + /// 解释异常原因的错误信息。 + /// 导致当前异常的异常;如果未指定内部异常,则是一个 null 引用 + public ServiceException(string message, Exception e) : base(message, e) - { - Message = string.IsNullOrEmpty(message) ? e.Message : message; - this.Source = e.Source; - } + { + Message = string.IsNullOrEmpty(message) ? e.Message : message; + this.Source = e.Source; + } - /// - /// 错误号 - /// - public int Code - { - get; - set; - } + #endregion 构造函数 - /// - /// 错误信息 - /// - public new string Message - { - get; - set; - } + #region 属性 - /// - /// 使用指定的枚举初始化 System.Exception 类的新实例 - /// - /// 错误号 - public ServiceException(Enum sysCode) - : base(sysCode.GetDisplay()) - { - this.Code = (int)Enum.Parse(sysCode.GetType(), sysCode.ToString()); - this.Message = sysCode.GetDisplay(); - - } + /// + /// Gets or sets the Code + /// 错误号 + /// + public int Code { get; set; } + + /// + /// Gets or sets the Message + /// 错误信息 + /// + public new string Message { get; set; } - public ServiceException(Enum sysCode, string message) - { - this.Code = (int)Enum.Parse(sysCode.GetType(), sysCode.ToString()); - this.Message = string.Format(message, sysCode.GetDisplay()); - - } + #endregion 属性 - /// - /// 通过自定义错误枚举对象获取ServiceException - /// - /// 自定义错误枚举 - /// 返回ServiceException - public ServiceException GetServiceException() - { - var code = Message.Substring(Message.LastIndexOf("错误号", System.StringComparison.Ordinal) + 3); - var sysCode = Enum.Parse(typeof(T), code); - this.Code = (int)Enum.Parse(sysCode.GetType(), sysCode.ToString()); - - return this; - } + #region 方法 + + /// + /// 通过自定义错误枚举对象获取ServiceException + /// + /// 自定义错误枚举 + /// 返回ServiceException + public ServiceException GetServiceException() + { + var code = Message.Substring(Message.LastIndexOf("错误号", System.StringComparison.Ordinal) + 3); + var sysCode = Enum.Parse(typeof(T), code); + this.Code = (int)Enum.Parse(sysCode.GetType(), sysCode.ToString()); + + return this; } -} + + #endregion 方法 + } +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/AppConfig.cs b/src/Surging.Core/Surging.Core.Consul/AppConfig.cs index b12f3d6c4..d5b588580 100644 --- a/src/Surging.Core/Surging.Core.Consul/AppConfig.cs +++ b/src/Surging.Core/Surging.Core.Consul/AppConfig.cs @@ -5,8 +5,18 @@ namespace Surging.Core.Consul { - public class AppConfig + /// + /// Defines the + /// + public class AppConfig { + #region 属性 + + /// + /// Gets or sets the Configuration + /// public static IConfigurationRoot Configuration { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/Configurations/ConfigInfo.cs b/src/Surging.Core/Surging.Core.Consul/Configurations/ConfigInfo.cs index 2bf308009..513a7ebde 100644 --- a/src/Surging.Core/Surging.Core.Consul/Configurations/ConfigInfo.cs +++ b/src/Surging.Core/Surging.Core.Consul/Configurations/ConfigInfo.cs @@ -5,43 +5,33 @@ namespace Surging.Core.Consul.Configurations { + /// + /// Defines the + /// public class ConfigInfo { - /// - /// 初始化会话超时为20秒的consul配置信息。 - /// - /// 连接字符串。 - /// 服务命令配置路径 - /// 路由路径配置路径 - /// 订阅者配置路径 - /// 缓存中心配置路径 - public ConfigInfo(string connectionString,string routePath = "services/serviceRoutes/", - string subscriberPath = "services/serviceSubscribers/", - string commandPath = "services/serviceCommands/", - string cachePath="services/serviceCaches/", - string mqttRoutePath = "services/mqttServiceRoutes/", - bool reloadOnChange=false, bool enableChildrenMonitor = false) : - this(connectionString, TimeSpan.FromSeconds(20), 0, routePath, subscriberPath,commandPath, cachePath, mqttRoutePath, reloadOnChange, enableChildrenMonitor) - { - } + #region 构造函数 /// - /// 初始化consul配置信息。 + /// 初始化会话超时为20秒的consul配置信息。 /// /// 连接字符串。 /// 会话超时时间。 - /// 服务命令配置命令。 - /// 订阅者配置命令。 + /// The lockDelay /// 路由路径配置路径 + /// 订阅者配置命令。 + /// 服务命令配置命令。 /// 缓存中心配置路径 /// Mqtt路由路径配置路径 + /// The reloadOnChange + /// The enableChildrenMonitor public ConfigInfo(string connectionString, TimeSpan sessionTimeout, int lockDelay, string routePath = "services/serviceRoutes/", string subscriberPath = "services/serviceSubscribers/", string commandPath = "services/serviceCommands/", - string cachePath= "services/serviceCaches/", - string mqttRoutePath= "services/mqttServiceRoutes/", - bool reloadOnChange=false, bool enableChildrenMonitor = false) + string cachePath = "services/serviceCaches/", + string mqttRoutePath = "services/mqttServiceRoutes/", + bool reloadOnChange = false, bool enableChildrenMonitor = false) { CachePath = cachePath; ReloadOnChange = reloadOnChange; @@ -62,9 +52,9 @@ public ConfigInfo(string connectionString, TimeSpan sessionTimeout, int lockDela else { var address = ConvertAddressModel(connectionString); - if (address !=null) - { - var ipAddress=address as IpAddressModel; + if (address != null) + { + var ipAddress = address as IpAddressModel; Host = ipAddress.Ip; Port = ipAddress.Port; } @@ -75,10 +65,42 @@ public ConfigInfo(string connectionString, TimeSpan sessionTimeout, int lockDela } } + /// + /// 初始化consul配置信息 + /// + /// 连接字符串。 + /// 路由路径配置路径 + /// 订阅者配置路径 + /// 服务命令配置路径 + /// 缓存中心配置路径 + /// The mqttRoutePath + /// The reloadOnChange + /// The enableChildrenMonitor + public ConfigInfo(string connectionString, string routePath = "services/serviceRoutes/", + string subscriberPath = "services/serviceSubscribers/", + string commandPath = "services/serviceCommands/", + string cachePath = "services/serviceCaches/", + string mqttRoutePath = "services/mqttServiceRoutes/", + bool reloadOnChange = false, bool enableChildrenMonitor = false) : + this(connectionString, TimeSpan.FromSeconds(20), 0, routePath, subscriberPath, commandPath, cachePath, mqttRoutePath, reloadOnChange, enableChildrenMonitor) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The host + /// The port public ConfigInfo(string host, int port) : this(host, port, TimeSpan.FromSeconds(20)) { } + /// + /// Initializes a new instance of the class. + /// + /// The host + /// The port + /// The sessionTimeout public ConfigInfo(string host, int port, TimeSpan sessionTimeout) { SessionTimeout = sessionTimeout; @@ -86,53 +108,91 @@ public ConfigInfo(string host, int port, TimeSpan sessionTimeout) Port = port; } - public bool ReloadOnChange { get; set; } + #endregion 构造函数 + + #region 属性 /// - /// watch 时间间隔 + ///地址 /// - public int WatchInterval { get; set; } = 60; + public IEnumerable Addresses { get; set; } - public int LockDelay { get; set; } + /// + /// Gets or sets the CachePath + /// 缓存中心配置中心 + /// + public string CachePath { get; set; } - public bool EnableChildrenMonitor { get; set; } /// + /// Gets or sets the CommandPath /// 命令配置路径 /// public string CommandPath { get; set; } /// - /// 订阅者配置路径 + /// Gets or sets a value indicating whether EnableChildrenMonitor /// - public string SubscriberPath { get; set; } + public bool EnableChildrenMonitor { get; set; } /// - /// 路由配置路径。 + /// Gets or sets the Host /// - public string RoutePath { get; set; } + public string Host { get; set; } + /// + /// Gets or sets the LockDelay + /// + public int LockDelay { get; set; } /// + /// Gets or sets the MqttRoutePath /// Mqtt路由配置路径。 /// public string MqttRoutePath { get; set; } - public IEnumerable Addresses { get; set; } - /// - /// 缓存中心配置中心 + /// Gets or sets the Port /// - public string CachePath { get; set; } + public int Port { get; set; } - public string Host { get; set; } + /// + /// Gets or sets a value indicating whether ReloadOnChange + /// + public bool ReloadOnChange { get; set; } - public int Port { get; set; } + /// + /// Gets or sets the RoutePath + /// 路由配置路径。 + /// + public string RoutePath { get; set; } /// + /// Gets or sets the SessionTimeout /// 会话超时时间。 /// public TimeSpan SessionTimeout { get; set; } + /// + /// Gets or sets the SubscriberPath + /// 订阅者配置路径 + /// + public string SubscriberPath { get; set; } + + /// + /// Gets or sets the WatchInterval + /// watch 时间间隔 + /// + public int WatchInterval { get; set; } = 60; + + #endregion 属性 + + #region 方法 + + /// + /// The ConvertAddressModel + /// + /// The connection + /// The public AddressModel ConvertAddressModel(string connection) { var address = connection.Split(":"); @@ -145,5 +205,6 @@ public AddressModel ConvertAddressModel(string connection) return null; } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/Configurations/ConsulConfigurationExtensions.cs b/src/Surging.Core/Surging.Core.Consul/Configurations/ConsulConfigurationExtensions.cs index 317fbec5f..f04a5a0e0 100644 --- a/src/Surging.Core/Surging.Core.Consul/Configurations/ConsulConfigurationExtensions.cs +++ b/src/Surging.Core/Surging.Core.Consul/Configurations/ConsulConfigurationExtensions.cs @@ -8,23 +8,23 @@ namespace Surging.Core.Consul.Configurations { + /// + /// Consul配置扩展 + /// public static class ConsulConfigurationExtensions { - public static IConfigurationBuilder AddConsulFile(this IConfigurationBuilder builder, string path) - { - return AddConsulFile(builder, provider: null, path: path, optional: false, reloadOnChange: false); - } - - public static IConfigurationBuilder AddConsulFile(this IConfigurationBuilder builder, string path, bool optional) - { - return AddConsulFile(builder, provider: null, path: path, optional: optional, reloadOnChange: false); - } - - public static IConfigurationBuilder AddConsulFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange) - { - return AddConsulFile(builder, provider: null, path: path, optional: optional, reloadOnChange: reloadOnChange); - } + #region 方法 + /// + /// 加Consul配置文件 + /// + /// 构建配置对象建造器 + /// The provider + /// 文件相对路径 + /// 是否可选 + /// 当改变时是不是重新加载 + /// 构建配置对象建造器 + /// 路径太长 public static IConfigurationBuilder AddConsulFile(this IConfigurationBuilder builder, IFileProvider provider, string path, bool optional, bool reloadOnChange) { Check.NotNull(builder, "builder"); @@ -46,5 +46,46 @@ public static IConfigurationBuilder AddConsulFile(this IConfigurationBuilder bui AppConfig.Configuration = builder.Build(); return builder; } + + /// + /// 加Consul配置文件 + /// + /// 构建配置对象建造器 + /// 文件相对路径 + /// 构建配置对象建造器 + /// 路径太长 + public static IConfigurationBuilder AddConsulFile(this IConfigurationBuilder builder, string path) + { + return AddConsulFile(builder, provider: null, path: path, optional: false, reloadOnChange: false); + } + + /// + /// 加Consul配置文件 + /// + /// 构建配置对象建造器 + /// 文件相对路径 + /// 是否可选 + /// 构建配置对象建造器 + /// 路径太长 + public static IConfigurationBuilder AddConsulFile(this IConfigurationBuilder builder, string path, bool optional) + { + return AddConsulFile(builder, provider: null, path: path, optional: optional, reloadOnChange: false); + } + + /// + /// 加Consul配置文件 + /// + /// 构建配置对象建造器 + /// 文件相对路径 + /// 是否可选 + /// 当改变时是不是重新加载 + /// 构建配置对象建造器 + /// 路径太长 + public static IConfigurationBuilder AddConsulFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange) + { + return AddConsulFile(builder, provider: null, path: path, optional: optional, reloadOnChange: reloadOnChange); + } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/Configurations/ConsulConfigurationProvider.cs b/src/Surging.Core/Surging.Core.Consul/Configurations/ConsulConfigurationProvider.cs index 94764663d..739466215 100644 --- a/src/Surging.Core/Surging.Core.Consul/Configurations/ConsulConfigurationProvider.cs +++ b/src/Surging.Core/Surging.Core.Consul/Configurations/ConsulConfigurationProvider.cs @@ -7,14 +7,35 @@ namespace Surging.Core.Consul.Configurations { - public class ConsulConfigurationProvider : FileConfigurationProvider + /// + /// Consul配置提供者 + /// + public class ConsulConfigurationProvider : FileConfigurationProvider { - public ConsulConfigurationProvider(ConsulConfigurationSource source) : base(source) { } + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The source + public ConsulConfigurationProvider(ConsulConfigurationSource source) : base(source) + { + } + + #endregion 构造函数 + + #region 方法 + + /// + /// The Load + /// + /// The stream public override void Load(Stream stream) { var parser = new JsonConfigurationParser(); this.Data = parser.Parse(stream, null); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/Configurations/ConsulConfigurationSource.cs b/src/Surging.Core/Surging.Core.Consul/Configurations/ConsulConfigurationSource.cs index bf2906e03..86f59f72c 100644 --- a/src/Surging.Core/Surging.Core.Consul/Configurations/ConsulConfigurationSource.cs +++ b/src/Surging.Core/Surging.Core.Consul/Configurations/ConsulConfigurationSource.cs @@ -1,16 +1,34 @@ using Microsoft.Extensions.Configuration; - namespace Surging.Core.Consul.Configurations { + /// + /// Defines the + /// public class ConsulConfigurationSource : FileConfigurationSource { + #region 属性 + + /// + /// Gets or sets the ConfigurationKeyPrefix + /// public string ConfigurationKeyPrefix { get; set; } + #endregion 属性 + + #region 方法 + + /// + /// The Build + /// + /// The builder + /// The public override IConfigurationProvider Build(IConfigurationBuilder builder) { FileProvider = FileProvider ?? builder.GetFileProvider(); return new ConsulConfigurationProvider(this); } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/Configurations/ConsulOption.cs b/src/Surging.Core/Surging.Core.Consul/Configurations/ConsulOption.cs index b49fab259..595af80cf 100644 --- a/src/Surging.Core/Surging.Core.Consul/Configurations/ConsulOption.cs +++ b/src/Surging.Core/Surging.Core.Consul/Configurations/ConsulOption.cs @@ -4,26 +4,73 @@ namespace Surging.Core.Consul.Configurations { - public class ConsulOption + /// + /// Consul选项 + /// + public class ConsulOption { - public string SessionTimeout { get; set; } + #region 属性 - public string ConnectionString { get; set; } + /// + /// Gets or sets the CachePath + /// 缓存路径 + /// + public string CachePath { get; set; } - public string RoutePath { get; set; } + /// + /// Gets or sets the CommandPath + /// 指令路径 + /// + public string CommandPath { get; set; } - public string SubscriberPath { get; set; } + /// + /// Gets or sets the ConnectionString + /// 连接字符串 + /// + public string ConnectionString { get; set; } - public string CommandPath { get; set; } + /// + /// Gets or sets the EnableChildrenMonitor + /// 是否启用子孙配置 + /// + public string EnableChildrenMonitor { get; set; } - public string CachePath { get; set; } + /// + /// Gets or sets the LockDelay + /// 锁定延时 + /// + public int? LockDelay { get; set; } + /// + /// Gets or sets the MqttRoutePath + /// mqtt路由路径 + /// public string MqttRoutePath { get; set; } + /// + /// Gets or sets the ReloadOnChange + /// 改变时重加载 + /// public string ReloadOnChange { get; set; } - public string EnableChildrenMonitor { get; set; } + /// + /// Gets or sets the RoutePath + /// 路由路径 + /// + public string RoutePath { get; set; } - public int? LockDelay { get; set; } + /// + /// Gets or sets the SessionTimeout + /// Session过期间隔 + /// + public string SessionTimeout { get; set; } + + /// + /// Gets or sets the SubscriberPath + /// 订阅路径 + /// + public string SubscriberPath { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/ConsulModule.cs b/src/Surging.Core/Surging.Core.Consul/ConsulModule.cs index d46898d33..bd3e9b9a4 100644 --- a/src/Surging.Core/Surging.Core.Consul/ConsulModule.cs +++ b/src/Surging.Core/Surging.Core.Consul/ConsulModule.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Configuration; +using Autofac; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Surging.Core.Consul.Configurations; @@ -20,51 +21,66 @@ using Surging.Core.CPlatform.Serialization; using Surging.Core.CPlatform.Support; using System; -using Autofac; namespace Surging.Core.Consul { + /// + /// Defines the + /// public class ConsulModule : EnginePartModule { + #region 方法 + + /// + /// The Initialize + /// + /// The context public override void Initialize(AppModuleContext context) { base.Initialize(context); } /// - /// Inject dependent third-party components + /// The UseCacheManager /// - /// - protected override void RegisterBuilder(ContainerBuilderWrapper builder) + /// The builder + /// The factory + /// The + public ContainerBuilderWrapper UseCacheManager(ContainerBuilderWrapper builder, Func factory) { - base.RegisterBuilder(builder); - var configInfo = new ConfigInfo(null); - UseConsulAddressSelector(builder) - .UseHealthCheck(builder) - .UseCounlClientProvider(builder, configInfo) - .UseConsulRouteManager(builder, configInfo) - .UseConsulServiceSubscribeManager(builder, configInfo) - .UseConsulCommandManager(builder, configInfo) - .UseConsulCacheManager(builder, configInfo) - .UseConsulWatch(builder, configInfo) - .UseConsulMqttRouteManager(builder, configInfo); + builder.RegisterAdapter(factory).InstancePerLifetimeScope(); + return builder; } - public ConsulModule UseConsulRouteManager(ContainerBuilderWrapper builder, ConfigInfo configInfo) + /// + /// The UseCommandManager + /// + /// The builder + /// The factory + /// The + public ContainerBuilderWrapper UseCommandManager(ContainerBuilderWrapper builder, Func factory) { - UseRouteManager(builder, provider => - new ConsulServiceRouteManager( - GetConfigInfo(configInfo), - provider.GetRequiredService>(), - provider.GetRequiredService>(), - provider.GetRequiredService(), - provider.GetRequiredService(), - provider.GetRequiredService>(), - provider.GetRequiredService(), - provider.GetRequiredService())); + builder.RegisterAdapter(factory).InstancePerLifetimeScope(); + return builder; + } + + /// + /// The UseConsulAddressSelector + /// + /// The builder + /// The + public ConsulModule UseConsulAddressSelector(ContainerBuilderWrapper builder) + { + builder.RegisterType().As().SingleInstance(); return this; } + /// + /// The UseConsulCacheManager + /// + /// The builder + /// The configInfo + /// The public ConsulModule UseConsulCacheManager(ContainerBuilderWrapper builder, ConfigInfo configInfo) { UseCacheManager(builder, provider => @@ -100,19 +116,12 @@ public ConsulModule UseConsulCommandManager(ContainerBuilderWrapper builder, Con return this; } - public ConsulModule UseConsulServiceSubscribeManager(ContainerBuilderWrapper builder, ConfigInfo configInfo) - { - UseSubscribeManager(builder, provider => new ConsulServiceSubscribeManager( - GetConfigInfo(configInfo), - provider.GetRequiredService>(), - provider.GetRequiredService>(), - provider.GetRequiredService(), - provider.GetRequiredService(), - provider.GetRequiredService>(), - provider.GetRequiredService())); - return this; - } - + /// + /// The UseConsulMqttRouteManager + /// + /// The builder + /// The configInfo + /// The public ConsulModule UseConsulMqttRouteManager(ContainerBuilderWrapper builder, ConfigInfo configInfo) { UseMqttRouteManager(builder, provider => @@ -123,37 +132,72 @@ public ConsulModule UseConsulMqttRouteManager(ContainerBuilderWrapper builder, C provider.GetRequiredService(), provider.GetRequiredService(), provider.GetRequiredService>(), - provider.GetRequiredService(), + provider.GetRequiredService(), provider.GetRequiredService())); return this; } /// - /// 设置使用基于Consul的Watch机制 + /// The UseConsulRouteManager /// - /// - /// - public ConsulModule UseConsulWatch(ContainerBuilderWrapper builder, ConfigInfo configInfo) + /// The builder + /// The configInfo + /// The + public ConsulModule UseConsulRouteManager(ContainerBuilderWrapper builder, ConfigInfo configInfo) { - builder.Register(provider => - { - return new ClientWatchManager(provider.Resolve>(),configInfo); - }).As().SingleInstance(); + UseRouteManager(builder, provider => + new ConsulServiceRouteManager( + GetConfigInfo(configInfo), + provider.GetRequiredService>(), + provider.GetRequiredService>(), + provider.GetRequiredService(), + provider.GetRequiredService(), + provider.GetRequiredService>(), + provider.GetRequiredService(), + provider.GetRequiredService())); return this; } - public ConsulModule UseConsulAddressSelector(ContainerBuilderWrapper builder) + /// + /// The UseConsulServiceSubscribeManager + /// + /// The builder + /// The configInfo + /// The + public ConsulModule UseConsulServiceSubscribeManager(ContainerBuilderWrapper builder, ConfigInfo configInfo) { - builder.RegisterType().As().SingleInstance(); + UseSubscribeManager(builder, provider => new ConsulServiceSubscribeManager( + GetConfigInfo(configInfo), + provider.GetRequiredService>(), + provider.GetRequiredService>(), + provider.GetRequiredService(), + provider.GetRequiredService(), + provider.GetRequiredService>(), + provider.GetRequiredService())); return this; } - public ConsulModule UseHealthCheck(ContainerBuilderWrapper builder) + /// + /// 设置使用基于Consul的Watch机制 + /// + /// + /// The configInfo + /// + public ConsulModule UseConsulWatch(ContainerBuilderWrapper builder, ConfigInfo configInfo) { - builder.RegisterType().As().SingleInstance(); + builder.Register(provider => + { + return new ClientWatchManager(provider.Resolve>(), configInfo); + }).As().SingleInstance(); return this; } + /// + /// The UseCounlClientProvider + /// + /// The builder + /// The configInfo + /// The public ConsulModule UseCounlClientProvider(ContainerBuilderWrapper builder, ConfigInfo configInfo) { UseCounlClientProvider(builder, provider => @@ -165,42 +209,89 @@ public ConsulModule UseCounlClientProvider(ContainerBuilderWrapper builder, Conf return this; } - public ContainerBuilderWrapper UseSubscribeManager(ContainerBuilderWrapper builder, Func factory) + /// + /// The UseCounlClientProvider + /// + /// The builder + /// The factory + /// The + public ContainerBuilderWrapper UseCounlClientProvider(ContainerBuilderWrapper builder, Func factory) { builder.RegisterAdapter(factory).InstancePerLifetimeScope(); return builder; } - public ContainerBuilderWrapper UseCommandManager(ContainerBuilderWrapper builder, Func factory) + /// + /// The UseHealthCheck + /// + /// The builder + /// The + public ConsulModule UseHealthCheck(ContainerBuilderWrapper builder) { - builder.RegisterAdapter(factory).InstancePerLifetimeScope(); - return builder; + builder.RegisterType().As().SingleInstance(); + return this; } - public ContainerBuilderWrapper UseCacheManager(ContainerBuilderWrapper builder, Func factory) + /// + /// The UseMqttRouteManager + /// + /// The builder + /// The factory + /// The + public ContainerBuilderWrapper UseMqttRouteManager(ContainerBuilderWrapper builder, Func factory) { builder.RegisterAdapter(factory).InstancePerLifetimeScope(); return builder; } + /// + /// The UseRouteManager + /// + /// The builder + /// The factory + /// The public ContainerBuilderWrapper UseRouteManager(ContainerBuilderWrapper builder, Func factory) { builder.RegisterAdapter(factory).InstancePerLifetimeScope(); return builder; } - public ContainerBuilderWrapper UseMqttRouteManager(ContainerBuilderWrapper builder, Func factory) + /// + /// The UseSubscribeManager + /// + /// The builder + /// The factory + /// The + public ContainerBuilderWrapper UseSubscribeManager(ContainerBuilderWrapper builder, Func factory) { builder.RegisterAdapter(factory).InstancePerLifetimeScope(); return builder; } - public ContainerBuilderWrapper UseCounlClientProvider(ContainerBuilderWrapper builder, Func factory) + /// + /// Inject dependent third-party components + /// + /// + protected override void RegisterBuilder(ContainerBuilderWrapper builder) { - builder.RegisterAdapter(factory).InstancePerLifetimeScope(); - return builder; + base.RegisterBuilder(builder); + var configInfo = new ConfigInfo(null); + UseConsulAddressSelector(builder) + .UseHealthCheck(builder) + .UseCounlClientProvider(builder, configInfo) + .UseConsulRouteManager(builder, configInfo) + .UseConsulServiceSubscribeManager(builder, configInfo) + .UseConsulCommandManager(builder, configInfo) + .UseConsulCacheManager(builder, configInfo) + .UseConsulWatch(builder, configInfo) + .UseConsulMqttRouteManager(builder, configInfo); } + /// + /// The GetConfigInfo + /// + /// The config + /// The private ConfigInfo GetConfigInfo(ConfigInfo config) { ConsulOption option = null; @@ -230,5 +321,7 @@ private ConfigInfo GetConfigInfo(ConfigInfo config) } return config; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/ConsulMqttServiceRouteManager.cs b/src/Surging.Core/Surging.Core.Consul/ConsulMqttServiceRouteManager.cs index 47b282763..855aee243 100644 --- a/src/Surging.Core/Surging.Core.Consul/ConsulMqttServiceRouteManager.cs +++ b/src/Surging.Core/Surging.Core.Consul/ConsulMqttServiceRouteManager.cs @@ -20,21 +20,76 @@ namespace Surging.Core.Consul { + /// + /// Defines the + /// public class ConsulMqttServiceRouteManager : MqttServiceRouteManagerBase, IDisposable - { + { + #region 字段 + + /// + /// Defines the _configInfo + /// private readonly ConfigInfo _configInfo; - private readonly ISerializer _serializer; - private readonly IMqttServiceFactory _mqttServiceFactory; + + /// + /// Defines the _consulClientFactory + /// + private readonly IConsulClientProvider _consulClientFactory; + + /// + /// Defines the _logger + /// private readonly ILogger _logger; - private readonly ISerializer _stringSerializer; + + /// + /// Defines the _manager + /// private readonly IClientWatchManager _manager; - private MqttServiceRoute[] _routes; - private readonly IConsulClientProvider _consulClientFactory; + + /// + /// Defines the _mqttServiceFactory + /// + private readonly IMqttServiceFactory _mqttServiceFactory; + + /// + /// Defines the _serializer + /// + private readonly ISerializer _serializer; + + /// + /// Defines the _serviceHeartbeatManager + /// private readonly IServiceHeartbeatManager _serviceHeartbeatManager; + /// + /// Defines the _stringSerializer + /// + private readonly ISerializer _stringSerializer; + + /// + /// Defines the _routes + /// + private MqttServiceRoute[] _routes; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The configInfo + /// The serializer + /// The stringSerializer + /// The manager + /// The mqttServiceFactory + /// The logger + /// The serviceHeartbeatManager + /// The consulClientFactory public ConsulMqttServiceRouteManager(ConfigInfo configInfo, ISerializer serializer, ISerializer stringSerializer, IClientWatchManager manager, IMqttServiceFactory mqttServiceFactory, - ILogger logger,IServiceHeartbeatManager serviceHeartbeatManager, + ILogger logger, IServiceHeartbeatManager serviceHeartbeatManager, IConsulClientProvider consulClientFactory) : base(stringSerializer) { _configInfo = configInfo; @@ -48,6 +103,14 @@ public ConsulMqttServiceRouteManager(ConfigInfo configInfo, ISerializer EnterRoutes().Wait(); } + #endregion 构造函数 + + #region 方法 + + /// + /// The ClearAsync + /// + /// The public override async Task ClearAsync() { var clients = await _consulClientFactory.GetClients(); @@ -67,6 +130,9 @@ public override async Task ClearAsync() } } + /// + /// The Dispose + /// public void Dispose() { } @@ -81,31 +147,35 @@ public override async Task> GetRoutesAsync() return _routes; } - public override async Task SetRoutesAsync(IEnumerable routes) + /// + /// The RemoveByTopicAsync + /// + /// The topic + /// The endpoint + /// The + public override async Task RemoveByTopicAsync(string topic, IEnumerable endpoint) { - var hostAddr = NetUtils.GetHostAddress(); - var mqttServiceRoutes = await GetRoutes(routes.Select(p => $"{ _configInfo.MqttRoutePath}{p.MqttDescriptor.Topic}")); - foreach (var route in routes) + var routes = await GetRoutesAsync(); + try { - var mqttServiceRoute = mqttServiceRoutes.Where(p => p.MqttDescriptor.Topic == route.MqttDescriptor.Topic).FirstOrDefault(); - - if (mqttServiceRoute != null) + var route = routes.Where(p => p.MqttDescriptor.Topic == topic).SingleOrDefault(); + if (route != null) { - var addresses = mqttServiceRoute.MqttEndpoint.Concat( - route.MqttEndpoint.Except(mqttServiceRoute.MqttEndpoint)).ToList(); - - foreach (var address in route.MqttEndpoint) - { - addresses.Remove(addresses.Where(p => p.ToString() == address.ToString()).FirstOrDefault()); - addresses.Add(address); - } - route.MqttEndpoint = addresses; + route.MqttEndpoint = route.MqttEndpoint.Except(endpoint); + await base.SetRoutesAsync(new MqttServiceRoute[] { route }); } } - - await base.SetRoutesAsync(routes); + catch (Exception ex) + { + throw ex; + } } + /// + /// The RemveAddressAsync + /// + /// The endpoint + /// The public override async Task RemveAddressAsync(IEnumerable endpoint) { var routes = await GetRoutesAsync(); @@ -123,25 +193,41 @@ public override async Task RemveAddressAsync(IEnumerable endpoint) await base.SetRoutesAsync(routes); } - public override async Task RemoveByTopicAsync(string topic, IEnumerable endpoint) + /// + /// The SetRoutesAsync + /// + /// The routes + /// The + public override async Task SetRoutesAsync(IEnumerable routes) { - var routes = await GetRoutesAsync(); - try + var hostAddr = NetUtils.GetHostAddress(); + var mqttServiceRoutes = await GetRoutes(routes.Select(p => $"{ _configInfo.MqttRoutePath}{p.MqttDescriptor.Topic}")); + foreach (var route in routes) { - var route = routes.Where(p => p.MqttDescriptor.Topic == topic).SingleOrDefault(); - if(route !=null) - { - route.MqttEndpoint = route.MqttEndpoint.Except(endpoint); - await base.SetRoutesAsync(new MqttServiceRoute[] { route }); + var mqttServiceRoute = mqttServiceRoutes.Where(p => p.MqttDescriptor.Topic == route.MqttDescriptor.Topic).FirstOrDefault(); + + if (mqttServiceRoute != null) + { + var addresses = mqttServiceRoute.MqttEndpoint.Concat( + route.MqttEndpoint.Except(mqttServiceRoute.MqttEndpoint)).ToList(); + + foreach (var address in route.MqttEndpoint) + { + addresses.Remove(addresses.Where(p => p.ToString() == address.ToString()).FirstOrDefault()); + addresses.Add(address); + } + route.MqttEndpoint = addresses; } } - catch (Exception ex) - { - throw ex; - } - + + await base.SetRoutesAsync(routes); } + /// + /// The SetRoutesAsync + /// + /// The routes + /// The protected override async Task SetRoutesAsync(IEnumerable routes) { var clients = await _consulClientFactory.GetClients(); @@ -156,109 +242,103 @@ protected override async Task SetRoutesAsync(IEnumerable } } - #region 私有方法 - - private async Task RemoveExceptRoutesAsync(IEnumerable routes, AddressModel hostAddr) + /// + /// The DataEquals + /// + /// The data1 + /// The data2 + /// The + private static bool DataEquals(IReadOnlyList data1, IReadOnlyList data2) { - routes = routes.ToArray(); - var clients = await _consulClientFactory.GetClients(); - foreach (var client in clients) + if (data1.Count != data2.Count) + return false; + for (var i = 0; i < data1.Count; i++) { - if (_routes != null) - { - var oldRouteTopics = _routes.Select(i => i.MqttDescriptor.Topic).ToArray(); - var newRouteTopics = routes.Select(i => i.MqttDescriptor.Topic).ToArray(); - var deletedRouteTopics = oldRouteTopics.Except(newRouteTopics).ToArray(); - foreach (var deletedRouteTopic in deletedRouteTopics) - { - var addresses = _routes.Where(p => p.MqttDescriptor.Topic == deletedRouteTopic).Select(p => p.MqttEndpoint).FirstOrDefault(); - if (addresses.Contains(hostAddr)) - await client.KV.Delete($"{_configInfo.MqttRoutePath}{deletedRouteTopic}"); - } - } + var b1 = data1[i]; + var b2 = data2[i]; + if (b1 != b2) + return false; } + return true; } - private async Task GetRoute(byte[] data) + /// + /// The ChildrenChange + /// + /// The oldChildrens + /// The newChildrens + /// The + private async Task ChildrenChange(string[] oldChildrens, string[] newChildrens) { if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) - _logger.LogDebug($"准备转换mqtt服务路由,配置内容:{Encoding.UTF8.GetString(data)}。"); - - if (data == null) - return null; - - var descriptor = _serializer.Deserialize(data); - return (await _mqttServiceFactory.CreateMqttServiceRoutesAsync(new[] { descriptor })).First(); - } - - private async Task GetRouteDatas(string[] routes) - { - List serviceRoutes = new List(); - foreach (var route in routes) - { - var serviceRoute = await GetRouteData(route); - serviceRoutes.Add(serviceRoute); - } - return serviceRoutes.ToArray(); - } + _logger.LogDebug($"最新的mqtt节点信息:{string.Join(",", newChildrens)}"); - private async Task GetRouteData(string data) - { - if (data == null) - return null; + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + _logger.LogDebug($"旧的mqtt节点信息:{string.Join(",", oldChildrens)}"); - var descriptor = _stringSerializer.Deserialize(data, typeof(MqttServiceDescriptor)) as MqttServiceDescriptor; - return (await _mqttServiceFactory.CreateMqttServiceRoutesAsync(new[] { descriptor })).First(); - } + //计算出已被删除的节点。 + var deletedChildrens = oldChildrens.Except(newChildrens).ToArray(); + //计算出新增的节点。 + var createdChildrens = newChildrens.Except(oldChildrens).ToArray(); - private async Task GetRoutes(IEnumerable childrens) - { + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + _logger.LogDebug($"需要被删除的mqtt路由节点:{string.Join(",", deletedChildrens)}"); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + _logger.LogDebug($"需要被添加的mqtt路由节点:{string.Join(",", createdChildrens)}"); - childrens = childrens.ToArray(); - var routes = new List(childrens.Count()); + //获取新增的路由信息。 + var newRoutes = (await GetRoutes(createdChildrens)).ToArray(); - foreach (var children in childrens) + var routes = _routes.ToArray(); + lock (_routes) { - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) - _logger.LogDebug($"准备从节点:{children}中获取mqtt路由信息。"); - - var route = await GetRoute(children); - if (route != null) - routes.Add(route); + _routes = _routes + //删除无效的节点路由。 + .Where(i => !deletedChildrens.Contains($"{_configInfo.MqttRoutePath}{i.MqttDescriptor.Topic}")) + //连接上新的路由。 + .Concat(newRoutes) + .ToArray(); } + //需要删除的路由集合。 + var deletedRoutes = routes.Where(i => deletedChildrens.Contains($"{_configInfo.MqttRoutePath}{i.MqttDescriptor.Topic}")).ToArray(); + //触发删除事件。 + OnRemoved(deletedRoutes.Select(route => new MqttServiceRouteEventArgs(route)).ToArray()); - return routes.ToArray(); + //触发路由被创建事件。 + OnCreated(newRoutes.Select(route => new MqttServiceRouteEventArgs(route)).ToArray()); + + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information)) + _logger.LogInformation("mqtt路由数据更新成功。"); } - private async Task GetRoute(string path) + /// + /// 转化topic集合 + /// + /// 信息数据集合 + /// 返回路径集合 + private async Task ConvertPaths(string[] datas) { - MqttServiceRoute result = null; - var client = await GetConsulClient(); - var watcher = new NodeMonitorWatcher(GetConsulClient, _manager, path, - async (oldData, newData) => await NodeChange(oldData, newData),tmpPath=> { - var index = tmpPath.LastIndexOf("/"); - return _serviceHeartbeatManager.ExistsWhitelist(tmpPath.Substring(index + 1)); - }); - - var queryResult = await client.KV.Keys(path); - if (queryResult.Response != null) + List topics = new List(); + foreach (var data in datas) { - var data = (await client.GetDataAsync(path)); - if (data != null) - { - watcher.SetCurrentData(data); - result = await GetRoute(data); - } + var result = await GetRouteData(data); + var topic = result?.MqttDescriptor.Topic; + if (!string.IsNullOrEmpty(topic)) + topics.Add(topic); } - return result; + return topics.ToArray(); } + /// + /// The EnterRoutes + /// + /// The private async Task EnterRoutes() { if (_routes != null && _routes.Length > 0) return; Action action = null; - var client =await GetConsulClient(); + var client = await GetConsulClient(); if (_configInfo.EnableChildrenMonitor) { var watcher = new ChildrenMonitorWatcher(GetConsulClient, _manager, _configInfo.MqttRoutePath, @@ -281,46 +361,122 @@ private async Task EnterRoutes() _routes = new MqttServiceRoute[0]; } } - - private static bool DataEquals(IReadOnlyList data1, IReadOnlyList data2) + /// + /// The GetConsulClient + /// + /// The + private async ValueTask GetConsulClient() { - if (data1.Count != data2.Count) - return false; - for (var i = 0; i < data1.Count; i++) + var client = await _consulClientFactory.GetClient(); + return client; + } + + /// + /// The GetRoute + /// + /// The data + /// The + private async Task GetRoute(byte[] data) + { + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + _logger.LogDebug($"准备转换mqtt服务路由,配置内容:{Encoding.UTF8.GetString(data)}。"); + + if (data == null) + return null; + + var descriptor = _serializer.Deserialize(data); + return (await _mqttServiceFactory.CreateMqttServiceRoutesAsync(new[] { descriptor })).First(); + } + + /// + /// The GetRoute + /// + /// The path + /// The + private async Task GetRoute(string path) + { + MqttServiceRoute result = null; + var client = await GetConsulClient(); + var watcher = new NodeMonitorWatcher(GetConsulClient, _manager, path, + async (oldData, newData) => await NodeChange(oldData, newData), tmpPath => + { + var index = tmpPath.LastIndexOf("/"); + return _serviceHeartbeatManager.ExistsWhitelist(tmpPath.Substring(index + 1)); + }); + + var queryResult = await client.KV.Keys(path); + if (queryResult.Response != null) { - var b1 = data1[i]; - var b2 = data2[i]; - if (b1 != b2) - return false; + var data = (await client.GetDataAsync(path)); + if (data != null) + { + watcher.SetCurrentData(data); + result = await GetRoute(data); + } } - return true; + return result; } - private async ValueTask GetConsulClient() + /// + /// The GetRouteData + /// + /// The data + /// The + private async Task GetRouteData(string data) { - var client = await _consulClientFactory.GetClient(); - return client; + if (data == null) + return null; + + var descriptor = _stringSerializer.Deserialize(data, typeof(MqttServiceDescriptor)) as MqttServiceDescriptor; + return (await _mqttServiceFactory.CreateMqttServiceRoutesAsync(new[] { descriptor })).First(); } /// - /// 转化topic集合 + /// The GetRouteDatas /// - /// 信息数据集合 - /// 返回路径集合 - private async Task ConvertPaths(string[] datas) + /// The routes + /// The + private async Task GetRouteDatas(string[] routes) { - List topics = new List(); - foreach (var data in datas) + List serviceRoutes = new List(); + foreach (var route in routes) { - var result = await GetRouteData(data); - var topic = result?.MqttDescriptor.Topic; - if (!string.IsNullOrEmpty(topic)) - topics.Add(topic); + var serviceRoute = await GetRouteData(route); + serviceRoutes.Add(serviceRoute); } - return topics.ToArray(); + return serviceRoutes.ToArray(); + } + + /// + /// The GetRoutes + /// + /// The childrens + /// The + private async Task GetRoutes(IEnumerable childrens) + { + childrens = childrens.ToArray(); + var routes = new List(childrens.Count()); + + foreach (var children in childrens) + { + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + _logger.LogDebug($"准备从节点:{children}中获取mqtt路由信息。"); + + var route = await GetRoute(children); + if (route != null) + routes.Add(route); + } + + return routes.ToArray(); } + /// + /// The NodeChange + /// + /// The oldData + /// The newData + /// The private async Task NodeChange(byte[] oldData, byte[] newData) { if (DataEquals(oldData, newData)) @@ -343,48 +499,33 @@ private async Task NodeChange(byte[] oldData, byte[] newData) OnChanged(new MqttServiceRouteChangedEventArgs(newRoute, oldRoute)); } - private async Task ChildrenChange(string[] oldChildrens, string[] newChildrens) + /// + /// The RemoveExceptRoutesAsync + /// + /// The routes + /// The hostAddr + /// The + private async Task RemoveExceptRoutesAsync(IEnumerable routes, AddressModel hostAddr) { - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) - _logger.LogDebug($"最新的mqtt节点信息:{string.Join(",", newChildrens)}"); - - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) - _logger.LogDebug($"旧的mqtt节点信息:{string.Join(",", oldChildrens)}"); - - //计算出已被删除的节点。 - var deletedChildrens = oldChildrens.Except(newChildrens).ToArray(); - //计算出新增的节点。 - var createdChildrens = newChildrens.Except(oldChildrens).ToArray(); - - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) - _logger.LogDebug($"需要被删除的mqtt路由节点:{string.Join(",", deletedChildrens)}"); - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) - _logger.LogDebug($"需要被添加的mqtt路由节点:{string.Join(",", createdChildrens)}"); - - //获取新增的路由信息。 - var newRoutes = (await GetRoutes(createdChildrens)).ToArray(); - - var routes = _routes.ToArray(); - lock (_routes) + routes = routes.ToArray(); + var clients = await _consulClientFactory.GetClients(); + foreach (var client in clients) { - _routes = _routes - //删除无效的节点路由。 - .Where(i => !deletedChildrens.Contains($"{_configInfo.MqttRoutePath}{i.MqttDescriptor.Topic}")) - //连接上新的路由。 - .Concat(newRoutes) - .ToArray(); + if (_routes != null) + { + var oldRouteTopics = _routes.Select(i => i.MqttDescriptor.Topic).ToArray(); + var newRouteTopics = routes.Select(i => i.MqttDescriptor.Topic).ToArray(); + var deletedRouteTopics = oldRouteTopics.Except(newRouteTopics).ToArray(); + foreach (var deletedRouteTopic in deletedRouteTopics) + { + var addresses = _routes.Where(p => p.MqttDescriptor.Topic == deletedRouteTopic).Select(p => p.MqttEndpoint).FirstOrDefault(); + if (addresses.Contains(hostAddr)) + await client.KV.Delete($"{_configInfo.MqttRoutePath}{deletedRouteTopic}"); + } + } } - //需要删除的路由集合。 - var deletedRoutes = routes.Where(i => deletedChildrens.Contains($"{_configInfo.MqttRoutePath}{i.MqttDescriptor.Topic}")).ToArray(); - //触发删除事件。 - OnRemoved(deletedRoutes.Select(route => new MqttServiceRouteEventArgs(route)).ToArray()); - - //触发路由被创建事件。 - OnCreated(newRoutes.Select(route => new MqttServiceRouteEventArgs(route)).ToArray()); - - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information)) - _logger.LogInformation("mqtt路由数据更新成功。"); } - #endregion + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/ConsulServiceCacheManager.cs b/src/Surging.Core/Surging.Core.Consul/ConsulServiceCacheManager.cs index 302fc15ab..ef83ce873 100644 --- a/src/Surging.Core/Surging.Core.Consul/ConsulServiceCacheManager.cs +++ b/src/Surging.Core/Surging.Core.Consul/ConsulServiceCacheManager.cs @@ -16,17 +16,67 @@ namespace Surging.Core.Consul { + /// + /// Defines the + /// public class ConsulServiceCacheManager : ServiceCacheManagerBase, IDisposable { + #region 字段 + + /// + /// Defines the _configInfo + /// private readonly ConfigInfo _configInfo; - private readonly ISerializer _serializer; + + /// + /// Defines the _consulClientFactory + /// + private readonly IConsulClientProvider _consulClientFactory; + + /// + /// Defines the _logger + /// private readonly ILogger _logger; + + /// + /// Defines the _manager + /// private readonly IClientWatchManager _manager; - private ServiceCache[] _serviceCaches; + + /// + /// Defines the _serializer + /// + private readonly ISerializer _serializer; + + /// + /// Defines the _serviceCacheFactory + /// private readonly IServiceCacheFactory _serviceCacheFactory; + + /// + /// Defines the _stringSerializer + /// private readonly ISerializer _stringSerializer; - private readonly IConsulClientProvider _consulClientFactory; + /// + /// Defines the _serviceCaches + /// + private ServiceCache[] _serviceCaches; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The configInfo + /// The serializer + /// The stringSerializer + /// The manager + /// The serviceCacheFactory + /// The logger + /// The consulClientFactory public ConsulServiceCacheManager(ConfigInfo configInfo, ISerializer serializer, ISerializer stringSerializer, IClientWatchManager manager, IServiceCacheFactory serviceCacheFactory, ILogger logger, IConsulClientProvider consulClientFactory) : base(stringSerializer) @@ -37,10 +87,18 @@ public ConsulServiceCacheManager(ConfigInfo configInfo, ISerializer seri _serviceCacheFactory = serviceCacheFactory; _consulClientFactory = consulClientFactory; _logger = logger; - _manager = manager; + _manager = manager; EnterCaches().Wait(); } + #endregion 构造函数 + + #region 方法 + + /// + /// The ClearAsync + /// + /// The public override async Task ClearAsync() { var clients = await _consulClientFactory.GetClients(); @@ -59,24 +117,28 @@ public override async Task ClearAsync() } } + /// + /// The Dispose + /// public void Dispose() { } - public override async Task SetCachesAsync(IEnumerable caches) - { - var serviceCaches = await GetCaches(caches.Select(p => $"{ _configInfo.CachePath}{p.CacheDescriptor.Id}")); - - await RemoveCachesAsync(caches); - await base.SetCachesAsync(caches); - } - + /// + /// The GetCachesAsync + /// + /// The public override async Task> GetCachesAsync() { await EnterCaches(); return _serviceCaches; } - + + /// + /// The RemveAddressAsync + /// + /// The endpoints + /// The public override async Task RemveAddressAsync(IEnumerable endpoints) { var caches = await GetCachesAsync(); @@ -94,6 +156,24 @@ public override async Task RemveAddressAsync(IEnumerable endpoint await base.SetCachesAsync(caches); } + /// + /// The SetCachesAsync + /// + /// The caches + /// The + public override async Task SetCachesAsync(IEnumerable caches) + { + var serviceCaches = await GetCaches(caches.Select(p => $"{ _configInfo.CachePath}{p.CacheDescriptor.Id}")); + + await RemoveCachesAsync(caches); + await base.SetCachesAsync(caches); + } + + /// + /// The SetCachesAsync + /// + /// The cacheDescriptors + /// The public override async Task SetCachesAsync(IEnumerable cacheDescriptors) { var clients = await _consulClientFactory.GetClients(); @@ -108,79 +188,110 @@ public override async Task SetCachesAsync(IEnumerable ca } } - private async Task GetCaches(IEnumerable childrens) + /// + /// The DataEquals + /// + /// The data1 + /// The data2 + /// The + private static bool DataEquals(IReadOnlyList data1, IReadOnlyList data2) { - - childrens = childrens.ToArray(); - var caches = new List(childrens.Count()); - - foreach (var children in childrens) + if (data1.Count != data2.Count) + return false; + for (var i = 0; i < data1.Count; i++) { - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) - _logger.LogDebug($"准备从节点:{children}中获取缓存信息。"); - - var cache = await GetCache(children); - if (cache != null) - caches.Add(cache); + var b1 = data1[i]; + var b2 = data2[i]; + if (b1 != b2) + return false; } - return caches.ToArray(); + return true; } - private async Task GetCache(string path) + /// + /// The ChildrenChange + /// + /// The oldChildrens + /// The newChildrens + /// The + private async Task ChildrenChange(string[] oldChildrens, string[] newChildrens) { - ServiceCache result = null; - var client = await GetConsulClient(); - var watcher = new NodeMonitorWatcher(GetConsulClient, _manager, path, - async (oldData, newData) => await NodeChange(oldData, newData),null); - var queryResult = await client.KV.Keys(path); - if (queryResult.Response != null) + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + _logger.LogDebug($"最新的节点信息:{string.Join(",", newChildrens)}"); + + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + _logger.LogDebug($"旧的节点信息:{string.Join(",", oldChildrens)}"); + + //计算出已被删除的节点。 + var deletedChildrens = oldChildrens.Except(newChildrens).ToArray(); + //计算出新增的节点。 + var createdChildrens = newChildrens.Except(oldChildrens).ToArray(); + + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + _logger.LogDebug($"需要被删除的服务缓存节点:{string.Join(",", deletedChildrens)}"); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information)) + _logger.LogDebug($"需要被添加的服务缓存节点:{string.Join(",", createdChildrens)}"); + + //获取新增的缓存信息。 + var newCaches = (await GetCaches(createdChildrens)).ToArray(); + + var caches = _serviceCaches.ToArray(); + lock (_serviceCaches) { - var data = (await client.GetDataAsync(path)); - if (data != null) - { - watcher.SetCurrentData(data); - result = await GetCache(data); - } + _serviceCaches = _serviceCaches + //删除无效的缓存节点。 + .Where(i => !deletedChildrens.Contains($"{_configInfo.CachePath}{i.CacheDescriptor.Id}")) + //连接上新的缓存。 + .Concat(newCaches) + .ToArray(); } - return result; - } + //需要删除的缓存集合。 + var deletedCaches = caches.Where(i => deletedChildrens.Contains($"{_configInfo.CachePath}{i.CacheDescriptor.Id}")).ToArray(); + //触发删除事件。 + OnRemoved(deletedCaches.Select(cache => new ServiceCacheEventArgs(cache)).ToArray()); - #region 私有方法 + //触发缓存被创建事件。 + OnCreated(newCaches.Select(cache => new ServiceCacheEventArgs(cache)).ToArray()); - private async Task RemoveCachesAsync(IEnumerable caches) - { - var path = _configInfo.CachePath; - caches = caches.ToArray(); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information)) + _logger.LogInformation("缓存数据更新成功。"); + } - if (_serviceCaches != null) + /// + /// The ConvertPaths + /// + /// The datas + /// The + private async Task ConvertPaths(string[] datas) + { + List paths = new List(); + foreach (var data in datas) { - var clients = await _consulClientFactory.GetClients(); - foreach (var client in clients) - { - - var deletedCacheIds = caches.Select(i => i.CacheDescriptor.Id).ToArray(); - foreach (var deletedCacheId in deletedCacheIds) - { - var nodePath = $"{path}{deletedCacheId}"; - await client.KV.Delete(nodePath); - } - } + var result = await GetCacheData(data); + var serviceId = result?.CacheDescriptor.Id; + if (!string.IsNullOrEmpty(serviceId)) + paths.Add(serviceId); } + return paths.ToArray(); } + /// + /// The EnterCaches + /// + /// The private async Task EnterCaches() { if (_serviceCaches != null && _serviceCaches.Length > 0) return; - var client =await GetConsulClient(); - var watcher = new ChildrenMonitorWatcher(GetConsulClient, _manager, _configInfo.CachePath, - async (oldChildrens, newChildrens) => await ChildrenChange(oldChildrens, newChildrens), - (result) => ConvertPaths(result).Result); + var client = await GetConsulClient(); + var watcher = new ChildrenMonitorWatcher(GetConsulClient, _manager, _configInfo.CachePath, + async (oldChildrens, newChildrens) => await ChildrenChange(oldChildrens, newChildrens), + (result) => ConvertPaths(result).Result); if (client.KV.Keys(_configInfo.CachePath).Result.Response?.Count() > 0) { var result = await client.GetChildrenAsync(_configInfo.CachePath); var keys = await client.KV.Keys(_configInfo.CachePath); - var childrens = result; + var childrens = result; watcher.SetCurrentData(ConvertPaths(childrens).Result.Select(key => $"{_configInfo.CachePath}{key}").ToArray()); _serviceCaches = await GetCaches(keys.Response); } @@ -192,20 +303,11 @@ private async Task EnterCaches() } } - private static bool DataEquals(IReadOnlyList data1, IReadOnlyList data2) - { - if (data1.Count != data2.Count) - return false; - for (var i = 0; i < data1.Count; i++) - { - var b1 = data1[i]; - var b2 = data2[i]; - if (b1 != b2) - return false; - } - return true; - } - + /// + /// The GetCache + /// + /// The data + /// The private async Task GetCache(byte[] data) { if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) @@ -218,6 +320,35 @@ private async Task GetCache(byte[] data) return (await _serviceCacheFactory.CreateServiceCachesAsync(new[] { descriptor })).First(); } + /// + /// The GetCache + /// + /// The path + /// The + private async Task GetCache(string path) + { + ServiceCache result = null; + var client = await GetConsulClient(); + var watcher = new NodeMonitorWatcher(GetConsulClient, _manager, path, + async (oldData, newData) => await NodeChange(oldData, newData), null); + var queryResult = await client.KV.Keys(path); + if (queryResult.Response != null) + { + var data = (await client.GetDataAsync(path)); + if (data != null) + { + watcher.SetCurrentData(data); + result = await GetCache(data); + } + } + return result; + } + + /// + /// The GetCacheData + /// + /// The data + /// The private async Task GetCacheData(string data) { if (data == null) @@ -227,6 +358,11 @@ private async Task GetCacheData(string data) return (await _serviceCacheFactory.CreateServiceCachesAsync(new[] { descriptor })).First(); } + /// + /// The GetCacheDatas + /// + /// The caches + /// The private async Task GetCacheDatas(string[] caches) { List serviceCaches = new List(); @@ -238,25 +374,44 @@ private async Task GetCacheDatas(string[] caches) return serviceCaches.ToArray(); } - private async Task ConvertPaths(string[] datas) + /// + /// The GetCaches + /// + /// The childrens + /// The + private async Task GetCaches(IEnumerable childrens) { - List paths = new List(); - foreach (var data in datas) + childrens = childrens.ToArray(); + var caches = new List(childrens.Count()); + + foreach (var children in childrens) { - var result = await GetCacheData(data); - var serviceId = result?.CacheDescriptor.Id; - if (!string.IsNullOrEmpty(serviceId)) - paths.Add(serviceId); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + _logger.LogDebug($"准备从节点:{children}中获取缓存信息。"); + + var cache = await GetCache(children); + if (cache != null) + caches.Add(cache); } - return paths.ToArray(); + return caches.ToArray(); } + /// + /// The GetConsulClient + /// + /// The private async ValueTask GetConsulClient() { var client = await _consulClientFactory.GetClient(); return client; } + /// + /// The NodeChange + /// + /// The oldData + /// The newData + /// The private async Task NodeChange(byte[] oldData, byte[] newData) { if (DataEquals(oldData, newData)) @@ -278,58 +433,38 @@ private async Task NodeChange(byte[] oldData, byte[] newData) if (newCache == null) //触发删除事件。 OnRemoved(new ServiceCacheEventArgs(oldCache)); - else if (oldCache == null) OnCreated(new ServiceCacheEventArgs(newCache)); - - else - //触发缓存变更事件。 - OnChanged(new ServiceCacheChangedEventArgs(newCache, oldCache)); + else + //触发缓存变更事件。 + OnChanged(new ServiceCacheChangedEventArgs(newCache, oldCache)); } - private async Task ChildrenChange(string[] oldChildrens, string[] newChildrens) + /// + /// The RemoveCachesAsync + /// + /// The caches + /// The + private async Task RemoveCachesAsync(IEnumerable caches) { - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) - _logger.LogDebug($"最新的节点信息:{string.Join(",", newChildrens)}"); - - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) - _logger.LogDebug($"旧的节点信息:{string.Join(",", oldChildrens)}"); - - //计算出已被删除的节点。 - var deletedChildrens = oldChildrens.Except(newChildrens).ToArray(); - //计算出新增的节点。 - var createdChildrens = newChildrens.Except(oldChildrens).ToArray(); - - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) - _logger.LogDebug($"需要被删除的服务缓存节点:{string.Join(",", deletedChildrens)}"); - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information)) - _logger.LogDebug($"需要被添加的服务缓存节点:{string.Join(",", createdChildrens)}"); - - //获取新增的缓存信息。 - var newCaches = (await GetCaches(createdChildrens)).ToArray(); + var path = _configInfo.CachePath; + caches = caches.ToArray(); - var caches = _serviceCaches.ToArray(); - lock (_serviceCaches) + if (_serviceCaches != null) { - _serviceCaches = _serviceCaches - //删除无效的缓存节点。 - .Where(i => !deletedChildrens.Contains($"{_configInfo.CachePath}{i.CacheDescriptor.Id}")) - //连接上新的缓存。 - .Concat(newCaches) - .ToArray(); + var clients = await _consulClientFactory.GetClients(); + foreach (var client in clients) + { + var deletedCacheIds = caches.Select(i => i.CacheDescriptor.Id).ToArray(); + foreach (var deletedCacheId in deletedCacheIds) + { + var nodePath = $"{path}{deletedCacheId}"; + await client.KV.Delete(nodePath); + } + } } - //需要删除的缓存集合。 - var deletedCaches = caches.Where(i => deletedChildrens.Contains($"{_configInfo.CachePath}{i.CacheDescriptor.Id}")).ToArray(); - //触发删除事件。 - OnRemoved(deletedCaches.Select(cache => new ServiceCacheEventArgs(cache)).ToArray()); - - //触发缓存被创建事件。 - OnCreated(newCaches.Select(cache => new ServiceCacheEventArgs(cache)).ToArray()); - - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information)) - _logger.LogInformation("缓存数据更新成功。"); } - #endregion + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/ConsulServiceCommandManager.cs b/src/Surging.Core/Surging.Core.Consul/ConsulServiceCommandManager.cs index cd0a089db..969de3262 100644 --- a/src/Surging.Core/Surging.Core.Consul/ConsulServiceCommandManager.cs +++ b/src/Surging.Core/Surging.Core.Consul/ConsulServiceCommandManager.cs @@ -1,38 +1,94 @@ -using Surging.Core.CPlatform; -using Surging.Core.CPlatform.Support.Implementation; -using System; -using System.Collections.Generic; -using System.Text; -using Surging.Core.CPlatform.Support; -using System.Threading.Tasks; -using Surging.Core.CPlatform.Serialization; +using Consul; using Microsoft.Extensions.Logging; -using Surging.Core.CPlatform.Runtime.Server; using Surging.Core.Consul.Configurations; -using Consul; -using System.Linq; -using Surging.Core.Consul.WatcherProvider; +using Surging.Core.Consul.Internal; using Surging.Core.Consul.Utilitys; +using Surging.Core.Consul.WatcherProvider; using Surging.Core.Consul.WatcherProvider.Implementation; +using Surging.Core.CPlatform; using Surging.Core.CPlatform.Routing; using Surging.Core.CPlatform.Routing.Implementation; using Surging.Core.CPlatform.Runtime.Client; -using Surging.Core.Consul.Internal; +using Surging.Core.CPlatform.Runtime.Server; +using Surging.Core.CPlatform.Serialization; +using Surging.Core.CPlatform.Support; +using Surging.Core.CPlatform.Support.Implementation; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; namespace Surging.Core.Consul { + /// + /// Defines the + /// public class ConsulServiceCommandManager : ServiceCommandManagerBase, IDisposable { + #region 字段 + + /// + /// Defines the _configInfo + /// private readonly ConfigInfo _configInfo; - private readonly ISerializer _serializer; + + /// + /// Defines the _consulClientFactory + /// + private readonly IConsulClientProvider _consulClientFactory; + + /// + /// Defines the _logger + /// private readonly ILogger _logger; + + /// + /// Defines the _manager + /// private readonly IClientWatchManager _manager; - private ServiceCommandDescriptor[] _serviceCommands; - private readonly ISerializer _stringSerializer; - private readonly IServiceRouteManager _serviceRouteManager; + + /// + /// Defines the _serializer + /// + private readonly ISerializer _serializer; + + /// + /// Defines the _serviceHeartbeatManager + /// private readonly IServiceHeartbeatManager _serviceHeartbeatManager; - private readonly IConsulClientProvider _consulClientFactory; + /// + /// Defines the _serviceRouteManager + /// + private readonly IServiceRouteManager _serviceRouteManager; + + /// + /// Defines the _stringSerializer + /// + private readonly ISerializer _stringSerializer; + + /// + /// Defines the _serviceCommands + /// + private ServiceCommandDescriptor[] _serviceCommands; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The configInfo + /// The serializer + /// The stringSerializer + /// The serviceRouteManager + /// The manager + /// The serviceEntryManager + /// The logger + /// The serviceHeartbeatManager + /// The consulClientFactory public ConsulServiceCommandManager(ConfigInfo configInfo, ISerializer serializer, ISerializer stringSerializer, IServiceRouteManager serviceRouteManager, IClientWatchManager manager, IServiceEntryManager serviceEntryManager, ILogger logger, @@ -50,6 +106,63 @@ public ConsulServiceCommandManager(ConfigInfo configInfo, ISerializer se _serviceRouteManager.Removed += ServiceRouteManager_Removed; } + #endregion 构造函数 + + #region 方法 + + /// + /// The ChildrenChange + /// + /// The oldChildrens + /// The newChildrens + /// The + public async Task ChildrenChange(string[] oldChildrens, string[] newChildrens) + { + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + _logger.LogDebug($"最新的节点信息:{string.Join(",", newChildrens)}"); + + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + _logger.LogDebug($"旧的节点信息:{string.Join(",", oldChildrens)}"); + + //计算出已被删除的节点。 + var deletedChildrens = oldChildrens.Except(newChildrens).ToArray(); + //计算出新增的节点。 + var createdChildrens = newChildrens.Except(oldChildrens).ToArray(); + + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + _logger.LogDebug($"需要被删除的服务命令节点:{string.Join(",", deletedChildrens)}"); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + _logger.LogDebug($"需要被添加的服务命令节点:{string.Join(",", createdChildrens)}"); + + //获取新增的服务命令信息。 + var newCommands = (await GetServiceCommands(createdChildrens)).ToArray(); + + var serviceCommands = _serviceCommands.ToArray(); + lock (_serviceCommands) + { + _serviceCommands = _serviceCommands + //删除无效的节点服务命令。 + .Where(i => !deletedChildrens.Contains($"{_configInfo.CommandPath}{i.ServiceId}")) + //连接上新的服务命令。 + .Concat(newCommands) + .ToArray(); + } + //需要删除的服务命令集合。 + var deletedRoutes = serviceCommands.Where(i => deletedChildrens.Contains($"{_configInfo.CommandPath}{i.ServiceId}")).ToArray(); + //触发删除事件。 + OnRemoved(deletedRoutes.Select(command => new ServiceCommandEventArgs(command)).ToArray()); + + //触发服务命令被创建事件。 + OnCreated(newCommands.Select(command => new ServiceCommandEventArgs(command)).ToArray()); + + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information)) + _logger.LogInformation("服务命令数据更新成功。"); + } + + /// + /// The ClearAsync + /// + /// The public override async Task ClearAsync() { var clients = await _consulClientFactory.GetClients(); @@ -69,6 +182,9 @@ public override async Task ClearAsync() } } + /// + /// The Dispose + /// public void Dispose() { } @@ -83,6 +199,11 @@ public override async Task> GetServiceComm return _serviceCommands; } + /// + /// The NodeChange + /// + /// The oldData + /// The newData public void NodeChange(byte[] oldData, byte[] newData) { if (DataEquals(oldData, newData)) @@ -104,7 +225,6 @@ public void NodeChange(byte[] oldData, byte[] newData) if (newCommand == null) //触发删除事件。 OnRemoved(new ServiceCommandEventArgs(oldCommand)); - else if (oldCommand == null) OnCreated(new ServiceCommandEventArgs(newCommand)); else @@ -112,6 +232,10 @@ public void NodeChange(byte[] oldData, byte[] newData) OnChanged(new ServiceCommandChangedEventArgs(newCommand, oldCommand)); } + /// + /// The NodeChange + /// + /// The newCommand public void NodeChange(ServiceCommandDescriptor newCommand) { //得到旧的服务命令。 @@ -129,6 +253,11 @@ public void NodeChange(ServiceCommandDescriptor newCommand) OnChanged(new ServiceCommandChangedEventArgs(newCommand, oldCommand)); } + /// + /// The SetServiceCommandsAsync + /// + /// The serviceCommands + /// The public override async Task SetServiceCommandsAsync(IEnumerable serviceCommands) { var clients = await _consulClientFactory.GetClients(); @@ -147,6 +276,11 @@ public override async Task SetServiceCommandsAsync(IEnumerable + /// The InitServiceCommandsAsync + /// + /// The serviceCommands + /// The protected override async Task InitServiceCommandsAsync(IEnumerable serviceCommands) { var commands = await GetServiceCommands(serviceCommands.Select(p => $"{ _configInfo.CommandPath}{ p.ServiceId}")); @@ -156,49 +290,109 @@ protected override async Task InitServiceCommandsAsync(IEnumerable + /// The DataEquals + /// + /// The data1 + /// The data2 + /// The + private static bool DataEquals(IReadOnlyList data1, IReadOnlyList data2) { - var clients = _consulClientFactory.GetClients().Result; - foreach (var client in clients) + if (data1.Count != data2.Count) + return false; + for (var i = 0; i < data1.Count; i++) { - client.KV.Delete($"{_configInfo.CommandPath}{e.Route.ServiceDescriptor.Id}").Wait(); + var b1 = data1[i]; + var b2 = data2[i]; + if (b1 != b2) + return false; } + return true; } - - - private ServiceCommandDescriptor GetServiceCommand(byte[] data) + /// + /// 转化路径集合 + /// + /// 信息数据集合 + /// 返回路径集合 + private string[] ConvertPaths(string[] datas) { - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) - _logger.LogDebug($"准备转换服务命令,配置内容:{Encoding.UTF8.GetString(data)}。"); - - if (data == null) - return null; - - var descriptor = _serializer.Deserialize(data); - return descriptor; + List paths = new List(); + foreach (var data in datas) + { + var result = GetServiceCommandData(data); + var serviceId = result?.ServiceId; + if (!string.IsNullOrEmpty(serviceId)) + paths.Add(serviceId); + } + return paths.ToArray(); } - private ServiceCommandDescriptor[] GetServiceCommandDatas(string[] commands) + /// + /// The EnterServiceCommands + /// + /// The + private async Task EnterServiceCommands() { - List serviceCommands = new List(); - foreach (var command in commands) + if (_serviceCommands != null) + return; + Action action = null; + var client = await GetConsulClient(); + if (_configInfo.EnableChildrenMonitor) { - var serviceCommand = GetServiceCommandData(command); - serviceCommands.Add(serviceCommand); + var watcher = new ChildrenMonitorWatcher(GetConsulClient, _manager, _configInfo.CommandPath, + async (oldChildrens, newChildrens) => await ChildrenChange(oldChildrens, newChildrens), + (result) => ConvertPaths(result)); + action = currentData => watcher.SetCurrentData(currentData); + } + if (client.KV.Keys(_configInfo.CommandPath).Result.Response?.Count() > 0) + { + var result = await client.GetChildrenAsync(_configInfo.CommandPath); + var keys = await client.KV.Keys(_configInfo.CommandPath); + var childrens = result; + action?.Invoke(ConvertPaths(childrens).Select(key => $"{_configInfo.CommandPath}{key}").ToArray()); + _serviceCommands = await GetServiceCommands(keys.Response); + } + else + { + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Warning)) + _logger.LogWarning($"无法获取服务命令信息,因为节点:{_configInfo.CommandPath},不存在。"); + _serviceCommands = new ServiceCommandDescriptor[0]; } - return serviceCommands.ToArray(); } - private ServiceCommandDescriptor GetServiceCommandData(string data) + /// + /// The GetConsulClient + /// + /// The + private async ValueTask GetConsulClient() + { + var client = await _consulClientFactory.GetClient(); + return client; + } + + /// + /// The GetServiceCommand + /// + /// The data + /// The + private ServiceCommandDescriptor GetServiceCommand(byte[] data) { + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + _logger.LogDebug($"准备转换服务命令,配置内容:{Encoding.UTF8.GetString(data)}。"); + if (data == null) return null; - var serviceCommand = _stringSerializer.Deserialize(data, typeof(ServiceCommandDescriptor)) as ServiceCommandDescriptor; - return serviceCommand; + var descriptor = _serializer.Deserialize(data); + return descriptor; } + /// + /// The GetServiceCommand + /// + /// The path + /// The private async Task GetServiceCommand(string path) { ServiceCommandDescriptor result = null; @@ -222,6 +416,41 @@ private async Task GetServiceCommand(string path) return result; } + /// + /// The GetServiceCommandData + /// + /// The data + /// The + private ServiceCommandDescriptor GetServiceCommandData(string data) + { + if (data == null) + return null; + + var serviceCommand = _stringSerializer.Deserialize(data, typeof(ServiceCommandDescriptor)) as ServiceCommandDescriptor; + return serviceCommand; + } + + /// + /// The GetServiceCommandDatas + /// + /// The commands + /// The + private ServiceCommandDescriptor[] GetServiceCommandDatas(string[] commands) + { + List serviceCommands = new List(); + foreach (var command in commands) + { + var serviceCommand = GetServiceCommandData(command); + serviceCommands.Add(serviceCommand); + } + return serviceCommands.ToArray(); + } + + /// + /// The GetServiceCommands + /// + /// The childrens + /// The private async Task GetServiceCommands(IEnumerable childrens) { var rootPath = _configInfo.CommandPath; @@ -242,114 +471,21 @@ private async Task GetServiceCommands(IEnumerable GetConsulClient() - { - var client = await _consulClientFactory.GetClient(); - return client; - } - - private async Task EnterServiceCommands() - { - if (_serviceCommands != null) - return; - Action action = null; - var client = await GetConsulClient(); - if (_configInfo.EnableChildrenMonitor) - { - var watcher = new ChildrenMonitorWatcher(GetConsulClient, _manager, _configInfo.CommandPath, - async (oldChildrens, newChildrens) => await ChildrenChange(oldChildrens, newChildrens), - (result) => ConvertPaths(result)); - action = currentData => watcher.SetCurrentData(currentData); - } - if (client.KV.Keys(_configInfo.CommandPath).Result.Response?.Count() > 0) - { - var result = await client.GetChildrenAsync(_configInfo.CommandPath); - var keys = await client.KV.Keys(_configInfo.CommandPath); - var childrens = result; - action?.Invoke(ConvertPaths(childrens).Select(key => $"{_configInfo.CommandPath}{key}").ToArray()); - _serviceCommands = await GetServiceCommands(keys.Response); - } - else - { - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Warning)) - _logger.LogWarning($"无法获取服务命令信息,因为节点:{_configInfo.CommandPath},不存在。"); - _serviceCommands = new ServiceCommandDescriptor[0]; - } - } /// - /// 转化路径集合 + /// The ServiceRouteManager_Removed /// - /// 信息数据集合 - /// 返回路径集合 - private string[] ConvertPaths(string[] datas) + /// The sender + /// The e + private void ServiceRouteManager_Removed(object sender, ServiceRouteEventArgs e) { - List paths = new List(); - foreach (var data in datas) + var clients = _consulClientFactory.GetClients().Result; + foreach (var client in clients) { - var result = GetServiceCommandData(data); - var serviceId = result?.ServiceId; - if (!string.IsNullOrEmpty(serviceId)) - paths.Add(serviceId); + client.KV.Delete($"{_configInfo.CommandPath}{e.Route.ServiceDescriptor.Id}").Wait(); } - return paths.ToArray(); } - private static bool DataEquals(IReadOnlyList data1, IReadOnlyList data2) - { - if (data1.Count != data2.Count) - return false; - for (var i = 0; i < data1.Count; i++) - { - var b1 = data1[i]; - var b2 = data2[i]; - if (b1 != b2) - return false; - } - return true; - } - - public async Task ChildrenChange(string[] oldChildrens, string[] newChildrens) - { - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) - _logger.LogDebug($"最新的节点信息:{string.Join(",", newChildrens)}"); - - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) - _logger.LogDebug($"旧的节点信息:{string.Join(",", oldChildrens)}"); - - //计算出已被删除的节点。 - var deletedChildrens = oldChildrens.Except(newChildrens).ToArray(); - //计算出新增的节点。 - var createdChildrens = newChildrens.Except(oldChildrens).ToArray(); - - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) - _logger.LogDebug($"需要被删除的服务命令节点:{string.Join(",", deletedChildrens)}"); - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) - _logger.LogDebug($"需要被添加的服务命令节点:{string.Join(",", createdChildrens)}"); - - //获取新增的服务命令信息。 - var newCommands = (await GetServiceCommands(createdChildrens)).ToArray(); - - var serviceCommands = _serviceCommands.ToArray(); - lock (_serviceCommands) - { - _serviceCommands = _serviceCommands - //删除无效的节点服务命令。 - .Where(i => !deletedChildrens.Contains($"{_configInfo.CommandPath}{i.ServiceId}")) - //连接上新的服务命令。 - .Concat(newCommands) - .ToArray(); - } - //需要删除的服务命令集合。 - var deletedRoutes = serviceCommands.Where(i => deletedChildrens.Contains($"{_configInfo.CommandPath}{i.ServiceId}")).ToArray(); - //触发删除事件。 - OnRemoved(deletedRoutes.Select(command => new ServiceCommandEventArgs(command)).ToArray()); - - //触发服务命令被创建事件。 - OnCreated(newCommands.Select(command => new ServiceCommandEventArgs(command)).ToArray()); - - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information)) - _logger.LogInformation("服务命令数据更新成功。"); - } + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/ConsulServiceRouteManager.cs b/src/Surging.Core/Surging.Core.Consul/ConsulServiceRouteManager.cs index f449f499b..87792856d 100644 --- a/src/Surging.Core/Surging.Core.Consul/ConsulServiceRouteManager.cs +++ b/src/Surging.Core/Surging.Core.Consul/ConsulServiceRouteManager.cs @@ -21,20 +21,72 @@ namespace Surging.Core.Consul { /// - /// consul服务路由管理器 + /// consul服务路由管理器 /// public class ConsulServiceRouteManager : ServiceRouteManagerBase, IDisposable { + #region 字段 + + /// + /// Defines the _configInfo + /// private readonly ConfigInfo _configInfo; + + /// + /// Defines the _consulClientProvider + /// + private readonly IConsulClientProvider _consulClientProvider; + + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the _manager + /// + private readonly IClientWatchManager _manager; + + /// + /// Defines the _serializer + /// private readonly ISerializer _serializer; + + /// + /// Defines the _serviceHeartbeatManager + /// + private readonly IServiceHeartbeatManager _serviceHeartbeatManager; + + /// + /// Defines the _serviceRouteFactory + /// private readonly IServiceRouteFactory _serviceRouteFactory; - private readonly ILogger _logger; + + /// + /// Defines the _stringSerializer + /// private readonly ISerializer _stringSerializer; - private readonly IClientWatchManager _manager; + + /// + /// Defines the _routes + /// private ServiceRoute[] _routes; - private readonly IConsulClientProvider _consulClientProvider; - private readonly IServiceHeartbeatManager _serviceHeartbeatManager; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The configInfo + /// The serializer + /// The stringSerializer + /// The manager + /// The serviceRouteFactory + /// The logger + /// The serviceHeartbeatManager + /// The consulClientProvider public ConsulServiceRouteManager(ConfigInfo configInfo, ISerializer serializer, ISerializer stringSerializer, IClientWatchManager manager, IServiceRouteFactory serviceRouteFactory, ILogger logger, @@ -51,6 +103,10 @@ public ConsulServiceRouteManager(ConfigInfo configInfo, ISerializer seri EnterRoutes().Wait(); } + #endregion 构造函数 + + #region 方法 + /// /// 清空服务路由 /// @@ -74,6 +130,9 @@ public override async Task ClearAsync() } } + /// + /// The Dispose + /// public void Dispose() { } @@ -88,6 +147,33 @@ public override async Task> GetRoutesAsync() return _routes; } + /// + /// The RemveAddressAsync + /// + /// The Address + /// The + public override async Task RemveAddressAsync(IEnumerable Address) + { + var routes = await GetRoutesAsync(); + try + { + foreach (var route in routes) + { + route.Address = route.Address.Except(Address); + } + } + catch (Exception ex) + { + throw ex; + } + await base.SetRoutesAsync(routes); + } + + /// + /// The SetRoutesAsync + /// + /// The routes + /// The public override async Task SetRoutesAsync(IEnumerable routes) { var locks = await CreateLock(); @@ -122,23 +208,11 @@ public override async Task SetRoutesAsync(IEnumerable routes) } } - public override async Task RemveAddressAsync(IEnumerable Address) - { - var routes = await GetRoutesAsync(); - try - { - foreach (var route in routes) - { - route.Address = route.Address.Except(Address); - } - } - catch (Exception ex) - { - throw ex; - } - await base.SetRoutesAsync(routes); - } - + /// + /// The SetRoutesAsync + /// + /// The routes + /// The protected override async Task SetRoutesAsync(IEnumerable routes) { var clients = await _consulClientProvider.GetClients(); @@ -153,124 +227,115 @@ protected override async Task SetRoutesAsync(IEnumerable } } - #region 私有方法 - - private async Task RemoveExceptRoutesAsync(IEnumerable routes, AddressModel hostAddr) + /// + /// The DataEquals + /// + /// The data1 + /// The data2 + /// The + private static bool DataEquals(IReadOnlyList data1, IReadOnlyList data2) { - routes = routes.ToArray(); - var clients = await _consulClientProvider.GetClients(); - foreach (var client in clients) + if (data1.Count != data2.Count) + return false; + for (var i = 0; i < data1.Count; i++) { - if (_routes != null) - { - var oldRouteIds = _routes.Select(i => i.ServiceDescriptor.Id).ToArray(); - var newRouteIds = routes.Select(i => i.ServiceDescriptor.Id).ToArray(); - var deletedRouteIds = oldRouteIds.Except(newRouteIds).ToArray(); - foreach (var deletedRouteId in deletedRouteIds) - { - var addresses = _routes.Where(p => p.ServiceDescriptor.Id == deletedRouteId).Select(p => p.Address).FirstOrDefault(); - if (addresses.Contains(hostAddr)) - await client.KV.Delete($"{_configInfo.RoutePath}{deletedRouteId}"); - } - } + var b1 = data1[i]; + var b2 = data2[i]; + if (b1 != b2) + return false; } + return true; } - private async Task> CreateLock() + /// + /// 数据更新 + /// + /// 旧的节点信息 + /// 最新的节点信息 + /// + private async Task ChildrenChange(string[] oldChildrens, string[] newChildrens) { - var result = new List(); - var clients = await _consulClientProvider.GetClients(); - foreach (var client in clients) - { - var distributedLock = await client.AcquireLock($"lock_{_configInfo.RoutePath}", _configInfo.LockDelay == 0 ? - default : - new CancellationTokenSource(TimeSpan.FromSeconds(_configInfo.LockDelay)).Token); - result.Add(distributedLock); - } - return result; - } + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + _logger.LogDebug($"最新的节点信息:{string.Join(",", newChildrens)}"); - private async Task GetRoute(byte[] data) - { if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) - _logger.LogDebug($"准备转换服务路由,配置内容:{Encoding.UTF8.GetString(data)}。"); + _logger.LogDebug($"旧的节点信息:{string.Join(",", oldChildrens)}"); - if (data == null) - return null; + //计算出已被删除的节点。 + var deletedChildrens = oldChildrens.Except(newChildrens).ToArray(); + //计算出新增的节点。 + var createdChildrens = newChildrens.Except(oldChildrens).ToArray(); - var descriptor = _serializer.Deserialize(data); - return (await _serviceRouteFactory.CreateServiceRoutesAsync(new[] { descriptor })).First(); - } + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + _logger.LogDebug($"需要被删除的路由节点:{string.Join(",", deletedChildrens)}"); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + _logger.LogDebug($"需要被添加的路由节点:{string.Join(",", createdChildrens)}"); - private async Task GetRouteDatas(string[] routes) - { - List serviceRoutes = new List(); - foreach (var route in routes) + //获取新增的路由信息。 + var newRoutes = (await GetRoutes(createdChildrens)).ToArray(); + + var routes = _routes.ToArray(); + lock (_routes) { - var serviceRoute = await GetRouteData(route); - serviceRoutes.Add(serviceRoute); + _routes = _routes + //删除无效的节点路由。 + .Where(i => !deletedChildrens.Contains($"{_configInfo.RoutePath}{i.ServiceDescriptor.Id}")) + //连接上新的路由。 + .Concat(newRoutes) + .ToArray(); } - return serviceRoutes.ToArray(); - } + //需要删除的路由集合。 + var deletedRoutes = routes.Where(i => deletedChildrens.Contains($"{_configInfo.RoutePath}{i.ServiceDescriptor.Id}")).ToArray(); + //触发删除事件。 + OnRemoved(deletedRoutes.Select(route => new ServiceRouteEventArgs(route)).ToArray()); - private async Task GetRouteData(string data) - { - if (data == null) - return null; + //触发路由被创建事件。 + OnCreated(newRoutes.Select(route => new ServiceRouteEventArgs(route)).ToArray()); - var descriptor = _stringSerializer.Deserialize(data, typeof(ServiceRouteDescriptor)) as ServiceRouteDescriptor; - return (await _serviceRouteFactory.CreateServiceRoutesAsync(new[] { descriptor })).First(); + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information)) + _logger.LogInformation("路由数据更新成功。"); } - private async Task GetRoutes(IEnumerable childrens) + /// + /// 转化路径集合 + /// + /// 信息数据集合 + /// 返回路径集合 + private async Task ConvertPaths(string[] datas) { - - childrens = childrens.ToArray(); - var routes = new List(childrens.Count()); - - foreach (var children in childrens) + List paths = new List(); + foreach (var data in datas) { - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) - _logger.LogDebug($"准备从节点:{children}中获取路由信息。"); - - var route = await GetRoute(children); - if (route != null) - routes.Add(route); + var result = await GetRouteData(data); + var serviceId = result?.ServiceDescriptor.Id; + if (!string.IsNullOrEmpty(serviceId)) + paths.Add(serviceId); } - - return routes.ToArray(); + return paths.ToArray(); } - private async Task GetRoute(string path) + /// + /// The CreateLock + /// + /// The + private async Task> CreateLock() { - ServiceRoute result = null; - var client = await GetConsulClient(); - var watcher = new NodeMonitorWatcher(GetConsulClient, _manager, path, - async (oldData, newData) => await NodeChange(oldData, newData), tmpPath => - { - var index = tmpPath.LastIndexOf("/"); - return _serviceHeartbeatManager.ExistsWhitelist(tmpPath.Substring(index + 1)); - }); - - var queryResult = await client.KV.Keys(path); - if (queryResult.Response != null) + var result = new List(); + var clients = await _consulClientProvider.GetClients(); + foreach (var client in clients) { - var data = (await client.GetDataAsync(path)); - if (data != null) - { - watcher.SetCurrentData(data); - result = await GetRoute(data); - } + var distributedLock = await client.AcquireLock($"lock_{_configInfo.RoutePath}", _configInfo.LockDelay == 0 ? + default : + new CancellationTokenSource(TimeSpan.FromSeconds(_configInfo.LockDelay)).Token); + result.Add(distributedLock); } return result; } - private async ValueTask GetConsulClient() - { - var client = await _consulClientProvider.GetClient(); - return client; - } - + /// + /// The EnterRoutes + /// + /// The private async Task EnterRoutes() { if (_routes != null && _routes.Length > 0) @@ -305,38 +370,121 @@ private async Task EnterRoutes() } } - private static bool DataEquals(IReadOnlyList data1, IReadOnlyList data2) + /// + /// The GetConsulClient + /// + /// The + private async ValueTask GetConsulClient() { - if (data1.Count != data2.Count) - return false; - for (var i = 0; i < data1.Count; i++) + var client = await _consulClientProvider.GetClient(); + return client; + } + + /// + /// The GetRoute + /// + /// The data + /// The + private async Task GetRoute(byte[] data) + { + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + _logger.LogDebug($"准备转换服务路由,配置内容:{Encoding.UTF8.GetString(data)}。"); + + if (data == null) + return null; + + var descriptor = _serializer.Deserialize(data); + return (await _serviceRouteFactory.CreateServiceRoutesAsync(new[] { descriptor })).First(); + } + + /// + /// The GetRoute + /// + /// The path + /// The + private async Task GetRoute(string path) + { + ServiceRoute result = null; + var client = await GetConsulClient(); + var watcher = new NodeMonitorWatcher(GetConsulClient, _manager, path, + async (oldData, newData) => await NodeChange(oldData, newData), tmpPath => + { + var index = tmpPath.LastIndexOf("/"); + return _serviceHeartbeatManager.ExistsWhitelist(tmpPath.Substring(index + 1)); + }); + + var queryResult = await client.KV.Keys(path); + if (queryResult.Response != null) { - var b1 = data1[i]; - var b2 = data2[i]; - if (b1 != b2) - return false; + var data = (await client.GetDataAsync(path)); + if (data != null) + { + watcher.SetCurrentData(data); + result = await GetRoute(data); + } } - return true; + return result; } /// - /// 转化路径集合 + /// The GetRouteData /// - /// 信息数据集合 - /// 返回路径集合 - private async Task ConvertPaths(string[] datas) + /// The data + /// The + private async Task GetRouteData(string data) { - List paths = new List(); - foreach (var data in datas) + if (data == null) + return null; + + var descriptor = _stringSerializer.Deserialize(data, typeof(ServiceRouteDescriptor)) as ServiceRouteDescriptor; + return (await _serviceRouteFactory.CreateServiceRoutesAsync(new[] { descriptor })).First(); + } + + /// + /// The GetRouteDatas + /// + /// The routes + /// The + private async Task GetRouteDatas(string[] routes) + { + List serviceRoutes = new List(); + foreach (var route in routes) { - var result = await GetRouteData(data); - var serviceId = result?.ServiceDescriptor.Id; - if (!string.IsNullOrEmpty(serviceId)) - paths.Add(serviceId); + var serviceRoute = await GetRouteData(route); + serviceRoutes.Add(serviceRoute); } - return paths.ToArray(); + return serviceRoutes.ToArray(); + } + + /// + /// The GetRoutes + /// + /// The childrens + /// The + private async Task GetRoutes(IEnumerable childrens) + { + childrens = childrens.ToArray(); + var routes = new List(childrens.Count()); + + foreach (var children in childrens) + { + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + _logger.LogDebug($"准备从节点:{children}中获取路由信息。"); + + var route = await GetRoute(children); + if (route != null) + routes.Add(route); + } + + return routes.ToArray(); } + /// + /// The NodeChange + /// + /// The oldData + /// The newData + /// The private async Task NodeChange(byte[] oldData, byte[] newData) { if (DataEquals(oldData, newData)) @@ -360,55 +508,32 @@ private async Task NodeChange(byte[] oldData, byte[] newData) } /// - /// 数据更新 + /// The RemoveExceptRoutesAsync /// - /// 旧的节点信息 - /// 最新的节点信息 - /// - private async Task ChildrenChange(string[] oldChildrens, string[] newChildrens) + /// The routes + /// The hostAddr + /// The + private async Task RemoveExceptRoutesAsync(IEnumerable routes, AddressModel hostAddr) { - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) - _logger.LogDebug($"最新的节点信息:{string.Join(",", newChildrens)}"); - - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) - _logger.LogDebug($"旧的节点信息:{string.Join(",", oldChildrens)}"); - - //计算出已被删除的节点。 - var deletedChildrens = oldChildrens.Except(newChildrens).ToArray(); - //计算出新增的节点。 - var createdChildrens = newChildrens.Except(oldChildrens).ToArray(); - - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) - _logger.LogDebug($"需要被删除的路由节点:{string.Join(",", deletedChildrens)}"); - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) - _logger.LogDebug($"需要被添加的路由节点:{string.Join(",", createdChildrens)}"); - - //获取新增的路由信息。 - var newRoutes = (await GetRoutes(createdChildrens)).ToArray(); - - var routes = _routes.ToArray(); - lock (_routes) + routes = routes.ToArray(); + var clients = await _consulClientProvider.GetClients(); + foreach (var client in clients) { - #region 节点变更操作 - _routes = _routes - //删除无效的节点路由。 - .Where(i => !deletedChildrens.Contains($"{_configInfo.RoutePath}{i.ServiceDescriptor.Id}")) - //连接上新的路由。 - .Concat(newRoutes) - .ToArray(); - #endregion + if (_routes != null) + { + var oldRouteIds = _routes.Select(i => i.ServiceDescriptor.Id).ToArray(); + var newRouteIds = routes.Select(i => i.ServiceDescriptor.Id).ToArray(); + var deletedRouteIds = oldRouteIds.Except(newRouteIds).ToArray(); + foreach (var deletedRouteId in deletedRouteIds) + { + var addresses = _routes.Where(p => p.ServiceDescriptor.Id == deletedRouteId).Select(p => p.Address).FirstOrDefault(); + if (addresses.Contains(hostAddr)) + await client.KV.Delete($"{_configInfo.RoutePath}{deletedRouteId}"); + } + } } - //需要删除的路由集合。 - var deletedRoutes = routes.Where(i => deletedChildrens.Contains($"{_configInfo.RoutePath}{i.ServiceDescriptor.Id}")).ToArray(); - //触发删除事件。 - OnRemoved(deletedRoutes.Select(route => new ServiceRouteEventArgs(route)).ToArray()); - - //触发路由被创建事件。 - OnCreated(newRoutes.Select(route => new ServiceRouteEventArgs(route)).ToArray()); - - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information)) - _logger.LogInformation("路由数据更新成功。"); } - #endregion + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/ConsulServiceSubscribeManager.cs b/src/Surging.Core/Surging.Core.Consul/ConsulServiceSubscribeManager.cs index 3640e4a5e..cf89752f5 100644 --- a/src/Surging.Core/Surging.Core.Consul/ConsulServiceSubscribeManager.cs +++ b/src/Surging.Core/Surging.Core.Consul/ConsulServiceSubscribeManager.cs @@ -15,17 +15,67 @@ namespace Surging.Core.Consul { + /// + /// Defines the + /// public class ConsulServiceSubscribeManager : ServiceSubscribeManagerBase, IDisposable { + #region 字段 + + /// + /// Defines the _configInfo + /// private readonly ConfigInfo _configInfo; + + /// + /// Defines the _consulClientFactory + /// + private readonly IConsulClientProvider _consulClientFactory; + + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the _manager + /// + private readonly IClientWatchManager _manager; + + /// + /// Defines the _serializer + /// private readonly ISerializer _serializer; + + /// + /// Defines the _serviceSubscriberFactory + /// private readonly IServiceSubscriberFactory _serviceSubscriberFactory; - private readonly ILogger _logger; + + /// + /// Defines the _stringSerializer + /// private readonly ISerializer _stringSerializer; - private readonly IClientWatchManager _manager; - private readonly IConsulClientProvider _consulClientFactory; + + /// + /// Defines the _subscribers + /// private ServiceSubscriber[] _subscribers; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The configInfo + /// The serializer + /// The stringSerializer + /// The manager + /// The serviceSubscriberFactory + /// The logger + /// The consulClientFactory public ConsulServiceSubscribeManager(ConfigInfo configInfo, ISerializer serializer, ISerializer stringSerializer, IClientWatchManager manager, IServiceSubscriberFactory serviceSubscriberFactory, ILogger logger, IConsulClientProvider consulClientFactory) : base(stringSerializer) @@ -40,16 +90,14 @@ public ConsulServiceSubscribeManager(ConfigInfo configInfo, ISerializer EnterSubscribers().Wait(); } + #endregion 构造函数 + + #region 方法 + /// - /// 获取所有可用的服务订阅者信息。 + /// The ClearAsync /// - /// 服务路由集合。 - public override async Task> GetSubscribersAsync() - { - await EnterSubscribers(); - return _subscribers; - } - + /// The public override async Task ClearAsync() { var clients = await _consulClientFactory.GetClients(); @@ -69,10 +117,51 @@ public override async Task ClearAsync() } } + /// + /// The Dispose + /// public void Dispose() { } + /// + /// 获取所有可用的服务订阅者信息。 + /// + /// 服务路由集合。 + public override async Task> GetSubscribersAsync() + { + await EnterSubscribers(); + return _subscribers; + } + + /// + /// The SetSubscribersAsync + /// + /// The subscribers + /// The + public override async Task SetSubscribersAsync(IEnumerable subscribers) + { + var serviceSubscribers = await GetSubscribers(subscribers.Select(p => $"{ _configInfo.SubscriberPath }{ p.ServiceDescriptor.Id}")); + if (serviceSubscribers.Count() > 0) + { + foreach (var subscriber in subscribers) + { + var serviceSubscriber = serviceSubscribers.Where(p => p.ServiceDescriptor.Id == subscriber.ServiceDescriptor.Id).FirstOrDefault(); + if (serviceSubscriber != null) + { + subscriber.Address = subscriber.Address.Concat( + subscriber.Address.Except(serviceSubscriber.Address)); + } + } + } + await base.SetSubscribersAsync(subscribers); + } + + /// + /// The SetSubscribersAsync + /// + /// The subscribers + /// The protected override async Task SetSubscribersAsync(IEnumerable subscribers) { subscribers = subscribers.ToArray(); @@ -98,36 +187,10 @@ protected override async Task SetSubscribersAsync(IEnumerable subscribers) - { - var serviceSubscribers = await GetSubscribers(subscribers.Select(p => $"{ _configInfo.SubscriberPath }{ p.ServiceDescriptor.Id}")); - if (serviceSubscribers.Count() > 0) - { - foreach (var subscriber in subscribers) - { - var serviceSubscriber = serviceSubscribers.Where(p => p.ServiceDescriptor.Id == subscriber.ServiceDescriptor.Id).FirstOrDefault(); - if (serviceSubscriber != null) - { - subscriber.Address = subscriber.Address.Concat( - subscriber.Address.Except(serviceSubscriber.Address)); - } - } - } - await base.SetSubscribersAsync(subscribers); - } - - private async Task GetSubscriber(byte[] data) - { - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) - _logger.LogDebug($"准备转换服务订阅者,配置内容:{Encoding.UTF8.GetString(data)}。"); - - if (data == null) - return null; - - var descriptor = _serializer.Deserialize(data); - return (await _serviceSubscriberFactory.CreateServiceSubscribersAsync(new[] { descriptor })).First(); - } - + /// + /// The EnterSubscribers + /// + /// The private async Task EnterSubscribers() { if (_subscribers != null) @@ -147,6 +210,28 @@ private async Task EnterSubscribers() } } + /// + /// The GetSubscriber + /// + /// The data + /// The + private async Task GetSubscriber(byte[] data) + { + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + _logger.LogDebug($"准备转换服务订阅者,配置内容:{Encoding.UTF8.GetString(data)}。"); + + if (data == null) + return null; + + var descriptor = _serializer.Deserialize(data); + return (await _serviceSubscriberFactory.CreateServiceSubscribersAsync(new[] { descriptor })).First(); + } + + /// + /// The GetSubscriber + /// + /// The path + /// The private async Task GetSubscriber(string path) { ServiceSubscriber result = null; @@ -163,6 +248,11 @@ private async Task GetSubscriber(string path) return result; } + /// + /// The GetSubscribers + /// + /// The childrens + /// The private async Task GetSubscribers(IEnumerable childrens) { childrens = childrens.ToArray(); @@ -178,5 +268,7 @@ private async Task GetSubscribers(IEnumerable child } return subscribers.ToArray(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/ContainerBuilderExtensions.cs b/src/Surging.Core/Surging.Core.Consul/ContainerBuilderExtensions.cs index 4de155127..52445836e 100644 --- a/src/Surging.Core/Surging.Core.Consul/ContainerBuilderExtensions.cs +++ b/src/Surging.Core/Surging.Core.Consul/ContainerBuilderExtensions.cs @@ -1,51 +1,55 @@ using Autofac; + +using Autofac; + +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Surging.Core.Consul.Configurations; +using Surging.Core.Consul.Internal; +using Surging.Core.Consul.Internal.Cluster.HealthChecks; +using Surging.Core.Consul.Internal.Cluster.HealthChecks.Implementation; +using Surging.Core.Consul.Internal.Cluster.Implementation.Selectors; +using Surging.Core.Consul.Internal.Cluster.Implementation.Selectors.Implementation; +using Surging.Core.Consul.Internal.Implementation; using Surging.Core.Consul.WatcherProvider; using Surging.Core.Consul.WatcherProvider.Implementation; using Surging.Core.CPlatform; using Surging.Core.CPlatform.Cache; +using Surging.Core.CPlatform.Module; +using Surging.Core.CPlatform.Mqtt; using Surging.Core.CPlatform.Routing; using Surging.Core.CPlatform.Runtime.Client; using Surging.Core.CPlatform.Runtime.Server; using Surging.Core.CPlatform.Serialization; using System; -using Microsoft.Extensions.Configuration; -using Surging.Core.CPlatform.Mqtt; -using Surging.Core.Consul.Internal; -using Surging.Core.Consul.Internal.Cluster.Implementation.Selectors.Implementation; -using Surging.Core.CPlatform.Module; -using Surging.Core.Consul.Internal.Implementation; -using Surging.Core.Consul.Internal.Cluster.HealthChecks; -using Surging.Core.Consul.Internal.Cluster.Implementation.Selectors; -using Autofac; -using Surging.Core.Consul.Internal.Cluster.HealthChecks.Implementation; namespace Surging.Core.Consul { + /// + /// Defines the + /// public static class ContainerBuilderExtensions { + #region 方法 + /// - /// 设置服务路由管理者。 + /// The UseConsulAddressSelector /// - /// Rpc服务构建者。 - /// Consul设置信息。 - /// 服务构建者。 - public static IServiceBuilder UseConsulRouteManager(this IServiceBuilder builder, ConfigInfo configInfo) + /// The builder + /// The + public static IServiceBuilder UseConsulAddressSelector(this IServiceBuilder builder) { - return builder.UseRouteManager(provider => - new ConsulServiceRouteManager( - GetConfigInfo(configInfo), - provider.GetRequiredService>(), - provider.GetRequiredService>(), - provider.GetRequiredService(), - provider.GetRequiredService(), - provider.GetRequiredService>(), - provider.GetRequiredService(), - provider.GetRequiredService())); + builder.Services.RegisterType().As().SingleInstance(); + return builder; } + /// + /// The UseConsulCacheManager + /// + /// The builder + /// The configInfo + /// The public static IServiceBuilder UseConsulCacheManager(this IServiceBuilder builder, ConfigInfo configInfo) { return builder.UseCacheManager(provider => @@ -83,6 +87,51 @@ public static IServiceBuilder UseConsulCommandManager(this IServiceBuilder build }); } + /// + /// The UseConsulManager + /// + /// The builder + /// The + [Obsolete] + public static IServiceBuilder UseConsulManager(this IServiceBuilder builder) + { + var configInfo = new ConfigInfo(null); + return builder.UseConsulRouteManager(configInfo) + .UseHealthCheck() + .UseConsulServiceSubscribeManager(configInfo) + .UseConsulCommandManager(configInfo) + .UseCounlClientProvider(configInfo) + .UseConsulAddressSelector() + .UseConsulCacheManager(configInfo).UseConsulWatch(configInfo) + .UseConsulMqttRouteManager(configInfo); + } + + /// + /// The UseConsulManager + /// + /// The builder + /// The configInfo + /// The + [Obsolete] + public static IServiceBuilder UseConsulManager(this IServiceBuilder builder, ConfigInfo configInfo) + { + return builder.UseConsulRouteManager(configInfo) + .UseHealthCheck() + .UseConsulServiceSubscribeManager(configInfo) + .UseConsulCommandManager(configInfo) + .UseConsulCacheManager(configInfo) + .UseCounlClientProvider(configInfo) + .UseConsulAddressSelector() + .UseConsulWatch(configInfo) + .UseConsulMqttRouteManager(configInfo); + } + + /// + /// The UseConsulMqttRouteManager + /// + /// The builder + /// The configInfo + /// The public static IServiceBuilder UseConsulMqttRouteManager(this IServiceBuilder builder, ConfigInfo configInfo) { return builder.UseMqttRouteManager(provider => @@ -97,6 +146,32 @@ public static IServiceBuilder UseConsulMqttRouteManager(this IServiceBuilder bui provider.GetRequiredService())); } + /// + /// 设置服务路由管理者。 + /// + /// Rpc服务构建者。 + /// Consul设置信息。 + /// 服务构建者。 + public static IServiceBuilder UseConsulRouteManager(this IServiceBuilder builder, ConfigInfo configInfo) + { + return builder.UseRouteManager(provider => + new ConsulServiceRouteManager( + GetConfigInfo(configInfo), + provider.GetRequiredService>(), + provider.GetRequiredService>(), + provider.GetRequiredService(), + provider.GetRequiredService(), + provider.GetRequiredService>(), + provider.GetRequiredService(), + provider.GetRequiredService())); + } + + /// + /// The UseConsulServiceSubscribeManager + /// + /// The builder + /// The configInfo + /// The public static IServiceBuilder UseConsulServiceSubscribeManager(this IServiceBuilder builder, ConfigInfo configInfo) { return builder.UseSubscribeManager(provider => @@ -117,6 +192,7 @@ public static IServiceBuilder UseConsulServiceSubscribeManager(this IServiceBuil /// 设置使用基于Consul的Watch机制 /// /// + /// The configInfo /// public static IServiceBuilder UseConsulWatch(this IServiceBuilder builder, ConfigInfo configInfo) { @@ -127,19 +203,12 @@ public static IServiceBuilder UseConsulWatch(this IServiceBuilder builder, Confi return builder; } - public static IServiceBuilder UseConsulAddressSelector(this IServiceBuilder builder) - { - builder.Services.RegisterType().As().SingleInstance(); - return builder; - } - - public static IServiceBuilder UseHealthCheck(this IServiceBuilder builder) - { - builder.Services.RegisterType().As().SingleInstance(); - return builder; - } - - + /// + /// The UseCounlClientProvider + /// + /// The builder + /// The configInfo + /// The public static IServiceBuilder UseCounlClientProvider(this IServiceBuilder builder, ConfigInfo configInfo) { builder.Services.Register(provider => @@ -151,35 +220,22 @@ public static IServiceBuilder UseCounlClientProvider(this IServiceBuilder builde return builder; } - [Obsolete] - public static IServiceBuilder UseConsulManager(this IServiceBuilder builder, ConfigInfo configInfo) - { - return builder.UseConsulRouteManager(configInfo) - .UseHealthCheck() - .UseConsulServiceSubscribeManager(configInfo) - .UseConsulCommandManager(configInfo) - .UseConsulCacheManager(configInfo) - .UseCounlClientProvider(configInfo) - .UseConsulAddressSelector() - .UseConsulWatch(configInfo) - .UseConsulMqttRouteManager(configInfo); - } - - [Obsolete] - public static IServiceBuilder UseConsulManager(this IServiceBuilder builder) + /// + /// The UseHealthCheck + /// + /// The builder + /// The + public static IServiceBuilder UseHealthCheck(this IServiceBuilder builder) { - var configInfo = new ConfigInfo(null); - return builder.UseConsulRouteManager(configInfo) - .UseHealthCheck() - .UseConsulServiceSubscribeManager(configInfo) - .UseConsulCommandManager(configInfo) - .UseCounlClientProvider(configInfo) - .UseConsulAddressSelector() - .UseConsulCacheManager(configInfo).UseConsulWatch(configInfo) - .UseConsulMqttRouteManager(configInfo); + builder.Services.RegisterType().As().SingleInstance(); + return builder; } - + /// + /// The GetConfigInfo + /// + /// The config + /// The private static ConfigInfo GetConfigInfo(ConfigInfo config) { ConsulOption option = null; @@ -210,5 +266,7 @@ private static ConfigInfo GetConfigInfo(ConfigInfo config) } return config; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/Internal/Cluster/HealthChecks/IHealthCheckService.cs b/src/Surging.Core/Surging.Core.Consul/Internal/Cluster/HealthChecks/IHealthCheckService.cs index 5c0afbb1d..4f2302800 100644 --- a/src/Surging.Core/Surging.Core.Consul/Internal/Cluster/HealthChecks/IHealthCheckService.cs +++ b/src/Surging.Core/Surging.Core.Consul/Internal/Cluster/HealthChecks/IHealthCheckService.cs @@ -6,10 +6,30 @@ namespace Surging.Core.Consul.Internal.Cluster.HealthChecks { + #region 接口 + + /// + /// Defines the + /// public interface IHealthCheckService { - void Monitor(AddressModel address); - + #region 方法 + + /// + /// The IsHealth + /// + /// The address + /// The ValueTask IsHealth(AddressModel address); + + /// + /// The Monitor + /// + /// The address + void Monitor(AddressModel address); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/Internal/Cluster/HealthChecks/Implementation/DefaultHealthCheckService.cs b/src/Surging.Core/Surging.Core.Consul/Internal/Cluster/HealthChecks/Implementation/DefaultHealthCheckService.cs index dec1fa714..459e54a5a 100644 --- a/src/Surging.Core/Surging.Core.Consul/Internal/Cluster/HealthChecks/Implementation/DefaultHealthCheckService.cs +++ b/src/Surging.Core/Surging.Core.Consul/Internal/Cluster/HealthChecks/Implementation/DefaultHealthCheckService.cs @@ -1,4 +1,5 @@ -using System; +using Surging.Core.CPlatform.Address; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -7,18 +8,39 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using Surging.Core.CPlatform.Address; namespace Surging.Core.Consul.Internal.Cluster.HealthChecks.Implementation { - public class DefaultHealthCheckService : IHealthCheckService,IDisposable + /// + /// Defines the + /// + public class DefaultHealthCheckService : IHealthCheckService, IDisposable { - private readonly int _timeout = 30000; - private readonly Timer _timer; + #region 字段 + + /// + /// Defines the _dictionary + /// private readonly ConcurrentDictionary, MonitorEntry> _dictionary = new ConcurrentDictionary, MonitorEntry>(); - #region Implementation of IHealthCheckService + /// + /// Defines the _timeout + /// + private readonly int _timeout = 30000; + + /// + /// Defines the _timer + /// + private readonly Timer _timer; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public DefaultHealthCheckService() { var timeSpan = TimeSpan.FromSeconds(60); @@ -29,6 +51,23 @@ public DefaultHealthCheckService() }, null, timeSpan, timeSpan); } + #endregion 构造函数 + + #region 方法 + + /// + /// The Dispose + /// + public void Dispose() + { + _timer.Dispose(); + } + + /// + /// The IsHealth + /// + /// The address + /// The public async ValueTask IsHealth(AddressModel address) { var ipAddress = address as IpAddressModel; @@ -37,24 +76,22 @@ public async ValueTask IsHealth(AddressModel address) return isHealth; } + /// + /// The Monitor + /// + /// The address public void Monitor(AddressModel address) { var ipAddress = address as IpAddressModel; _dictionary.GetOrAdd(new ValueTuple(ipAddress.Ip, ipAddress.Port), k => new MonitorEntry(address)); } - #region Implementation of IDisposable - - public void Dispose() - { - _timer.Dispose(); - } - #endregion - - #endregion Implementation of IDisposable - - #region Private Method - + /// + /// The Check + /// + /// The address + /// The timeout + /// The private static async Task Check(AddressModel address, int timeout) { bool isHealth = false; @@ -67,12 +104,17 @@ private static async Task Check(AddressModel address, int timeout) } catch { - } return isHealth; } } + /// + /// The Check + /// + /// The entrys + /// The timeout + /// The private static async Task Check(IEnumerable entrys, int timeout) { foreach (var entry in entrys) @@ -94,25 +136,46 @@ private static async Task Check(IEnumerable entrys, int timeout) } } - #endregion Private Method - - #region Help Class + #endregion 方法 + /// + /// Defines the + /// protected class MonitorEntry { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The addressModel + /// The health public MonitorEntry(AddressModel addressModel, bool health = true) { EndPoint = addressModel.CreateEndPoint(); Health = health; - } - public int UnhealthyTimes { get; set; } + #endregion 构造函数 + #region 属性 + + /// + /// Gets or sets the EndPoint + /// public EndPoint EndPoint { get; set; } + + /// + /// Gets or sets a value indicating whether Health + /// public bool Health { get; set; } - } - #endregion Help Class + /// + /// Gets or sets the UnhealthyTimes + /// + public int UnhealthyTimes { get; set; } + + #endregion 属性 + } } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/Internal/Cluster/Implementation/Selectors/IConsulAddressSelector.cs b/src/Surging.Core/Surging.Core.Consul/Internal/Cluster/Implementation/Selectors/IConsulAddressSelector.cs index ec3e06f1f..e96816bee 100644 --- a/src/Surging.Core/Surging.Core.Consul/Internal/Cluster/Implementation/Selectors/IConsulAddressSelector.cs +++ b/src/Surging.Core/Surging.Core.Consul/Internal/Cluster/Implementation/Selectors/IConsulAddressSelector.cs @@ -7,7 +7,14 @@ namespace Surging.Core.Consul.Internal.Cluster.Implementation.Selectors { + #region 接口 + + /// + /// Defines the + /// public interface IConsulAddressSelector : IAddressSelector - { + { } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/Internal/Cluster/Implementation/Selectors/Implementation/AddressSelectorMode.cs b/src/Surging.Core/Surging.Core.Consul/Internal/Cluster/Implementation/Selectors/Implementation/AddressSelectorMode.cs index c80c81605..3d56a0f8f 100644 --- a/src/Surging.Core/Surging.Core.Consul/Internal/Cluster/Implementation/Selectors/Implementation/AddressSelectorMode.cs +++ b/src/Surging.Core/Surging.Core.Consul/Internal/Cluster/Implementation/Selectors/Implementation/AddressSelectorMode.cs @@ -4,9 +4,23 @@ namespace Surging.Core.Consul.Internal.Cluster.Implementation.Selectors.Implementation { + #region 枚举 + + /// + /// Defines the AddressSelectorMode + /// public enum AddressSelectorMode - { + { + /// + /// Defines the Polling + /// Polling, + + /// + /// Defines the Random + /// Random } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/Internal/Cluster/Implementation/Selectors/Implementation/ConsulAddressSelectorBase.cs b/src/Surging.Core/Surging.Core.Consul/Internal/Cluster/Implementation/Selectors/Implementation/ConsulAddressSelectorBase.cs index 9a28e2e1a..6e0ab3bc3 100644 --- a/src/Surging.Core/Surging.Core.Consul/Internal/Cluster/Implementation/Selectors/Implementation/ConsulAddressSelectorBase.cs +++ b/src/Surging.Core/Surging.Core.Consul/Internal/Cluster/Implementation/Selectors/Implementation/ConsulAddressSelectorBase.cs @@ -8,9 +8,19 @@ namespace Surging.Core.Consul.Internal.Cluster.Implementation.Selectors.Implementation { + /// + /// Defines the + /// public abstract class ConsulAddressSelectorBase : IConsulAddressSelector { - #region Implementation of IAddressSelector + #region 方法 + + /// + /// 选择一个地址。 + /// + /// 地址选择上下文。 + /// 地址模型。 + protected abstract ValueTask SelectAsync(AddressSelectContext context); /// /// 选择一个地址。 @@ -28,7 +38,7 @@ async ValueTask IAddressSelector.SelectAsync(AddressSelectContext // var address = context.Address.ToArray(); if (context.Address.Count() == 0) - throw new ArgumentException("没有任何地址信息。", nameof(context.Address)); + throw new ArgumentException("没有任何地址信息。", nameof(context.Address)); if (context.Address.Count() == 1) { @@ -41,13 +51,6 @@ async ValueTask IAddressSelector.SelectAsync(AddressSelectContext } } - #endregion Implementation of IAddressSelector - - /// - /// 选择一个地址。 - /// - /// 地址选择上下文。 - /// 地址模型。 - protected abstract ValueTask SelectAsync(AddressSelectContext context); + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/Internal/Cluster/Implementation/Selectors/Implementation/ConsulRandomAddressSelector.cs b/src/Surging.Core/Surging.Core.Consul/Internal/Cluster/Implementation/Selectors/Implementation/ConsulRandomAddressSelector.cs index 889d36760..23e88e1a5 100644 --- a/src/Surging.Core/Surging.Core.Consul/Internal/Cluster/Implementation/Selectors/Implementation/ConsulRandomAddressSelector.cs +++ b/src/Surging.Core/Surging.Core.Consul/Internal/Cluster/Implementation/Selectors/Implementation/ConsulRandomAddressSelector.cs @@ -8,19 +8,29 @@ namespace Surging.Core.Consul.Internal.Cluster.Implementation.Selectors.Implementation { + /// + /// Defines the + /// public class ConsulRandomAddressSelector : ConsulAddressSelectorBase { - #region Field + #region 字段 + /// + /// Defines the _generate + /// private readonly Func _generate; + + /// + /// Defines the _random + /// private readonly Random _random; - #endregion Field + #endregion 字段 - #region Constructor + #region 构造函数 /// - /// 初始化一个以Random生成随机数的随机地址选择器。 + /// Initializes a new instance of the class. /// public ConsulRandomAddressSelector() { @@ -29,7 +39,7 @@ public ConsulRandomAddressSelector() } /// - /// 初始化一个自定义的随机地址选择器。 + /// Initializes a new instance of the class. /// /// 随机数生成委托,第一个参数为最小值,第二个参数为最大值(不可以超过该值)。 public ConsulRandomAddressSelector(Func generate) @@ -39,9 +49,9 @@ public ConsulRandomAddressSelector(Func generate) _generate = generate; } - #endregion Constructor + #endregion 构造函数 - #region Overrides of AddressSelectorBase + #region 方法 /// /// 选择一个地址。 @@ -57,6 +67,6 @@ protected override ValueTask SelectAsync(AddressSelectContext cont return new ValueTask(address[index]); } - #endregion Overrides of AddressSelectorBase + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/Internal/IConsulClientProvider.cs b/src/Surging.Core/Surging.Core.Consul/Internal/IConsulClientProvider.cs index 6318ee88a..044061747 100644 --- a/src/Surging.Core/Surging.Core.Consul/Internal/IConsulClientProvider.cs +++ b/src/Surging.Core/Surging.Core.Consul/Internal/IConsulClientProvider.cs @@ -7,12 +7,35 @@ namespace Surging.Core.Consul.Internal { - public interface IConsulClientProvider + #region 接口 + + /// + /// Defines the + /// + public interface IConsulClientProvider { + #region 方法 + + /// + /// The Check + /// + /// The + ValueTask Check(); + + /// + /// The GetClient + /// + /// The ValueTask GetClient(); + /// + /// The GetClients + /// + /// The ValueTask> GetClients(); - ValueTask Check(); + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/Internal/Implementation/DefaultConsulClientProvider.cs b/src/Surging.Core/Surging.Core.Consul/Internal/Implementation/DefaultConsulClientProvider.cs index d2bd7eb71..ff77f45df 100644 --- a/src/Surging.Core/Surging.Core.Consul/Internal/Implementation/DefaultConsulClientProvider.cs +++ b/src/Surging.Core/Surging.Core.Consul/Internal/Implementation/DefaultConsulClientProvider.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Consul; +using Consul; using Microsoft.Extensions.Logging; using Surging.Core.Consul.Configurations; using Surging.Core.Consul.Internal.Cluster.HealthChecks; @@ -14,21 +8,66 @@ using Surging.Core.CPlatform.Address; using Surging.Core.CPlatform.Exceptions; using Surging.Core.CPlatform.Runtime.Client.Address.Resolvers.Implementation.Selectors; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; using Level = Microsoft.Extensions.Logging.LogLevel; namespace Surging.Core.Consul.Internal.Implementation { + /// + /// Defines the + /// public class DefaultConsulClientProvider : IConsulClientProvider { - private ConfigInfo _config; - private readonly IHealthCheckService _healthCheckService; - private readonly IConsulAddressSelector _consulAddressSelector; - private readonly ILogger _logger; + #region 字段 + + /// + /// Defines the _addressSelectors + /// private readonly ConcurrentDictionary _addressSelectors = new ConcurrentDictionary(); + + /// + /// Defines the _consulAddressSelector + /// + private readonly IConsulAddressSelector _consulAddressSelector; + + /// + /// Defines the _consulClients + /// private readonly ConcurrentDictionary _consulClients = new ConcurrentDictionary(); + /// + /// Defines the _healthCheckService + /// + private readonly IHealthCheckService _healthCheckService; + + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the _config + /// + private ConfigInfo _config; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The config + /// The healthCheckService + /// The consulAddressSelector + /// The logger public DefaultConsulClientProvider(ConfigInfo config, IHealthCheckService healthCheckService, IConsulAddressSelector consulAddressSelector, ILogger logger) { @@ -38,6 +77,29 @@ public DefaultConsulClientProvider(ConfigInfo config, IHealthCheckService health _logger = logger; } + #endregion 构造函数 + + #region 方法 + + /// + /// The Check + /// + /// The + public async ValueTask Check() + { + foreach (var address in _config.Addresses) + { + if (!await _healthCheckService.IsHealth(address)) + { + throw new RegisterConnectionException(string.Format("注册中心{0}连接异常,请联系管理员", address.ToString())); + } + } + } + + /// + /// The GetClient + /// + /// The public async ValueTask GetClient() { ConsulClient result = null; @@ -76,6 +138,10 @@ public async ValueTask GetClient() return result; } + /// + /// The GetClients + /// + /// The public async ValueTask> GetClients() { var result = new List(); @@ -88,21 +154,11 @@ public async ValueTask> GetClients() { config.Address = new Uri($"http://{ipAddress.Ip}:{ipAddress.Port}"); }, null, h => { h.UseProxy = false; h.Proxy = null; }))); - } } return result; } - public async ValueTask Check() - { - foreach (var address in _config.Addresses) - { - if (!await _healthCheckService.IsHealth(address)) - { - throw new RegisterConnectionException(string.Format("注册中心{0}连接异常,请联系管理员", address.ToString())); - } - } - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/Utilitys/ConsulClientExtensions.cs b/src/Surging.Core/Surging.Core.Consul/Utilitys/ConsulClientExtensions.cs index 3ecdf27ad..9eeca0d1b 100644 --- a/src/Surging.Core/Surging.Core.Consul/Utilitys/ConsulClientExtensions.cs +++ b/src/Surging.Core/Surging.Core.Consul/Utilitys/ConsulClientExtensions.cs @@ -1,15 +1,26 @@ using Consul; using System; -using System.Threading.Tasks; using System.Linq; -using System.Text; using System.Net.Http; using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; namespace Surging.Core.Consul.Utilitys { + /// + /// Defines the + /// public static class ConsulClientExtensions { + #region 方法 + + /// + /// The GetChildrenAsync + /// + /// The client + /// The path + /// The public static async Task GetChildrenAsync(this ConsulClient client, string path) { try @@ -23,6 +34,12 @@ public static async Task GetChildrenAsync(this ConsulClient client, st } } + /// + /// The GetDataAsync + /// + /// The client + /// The path + /// The public static async Task GetDataAsync(this ConsulClient client, string path) { try @@ -35,6 +52,7 @@ public static async Task GetDataAsync(this ConsulClient client, string p return null; } } - } -} + #endregion 方法 + } +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/WatcherProvider/IClientWatchManager.cs b/src/Surging.Core/Surging.Core.Consul/WatcherProvider/IClientWatchManager.cs index 4e96861ff..524711296 100644 --- a/src/Surging.Core/Surging.Core.Consul/WatcherProvider/IClientWatchManager.cs +++ b/src/Surging.Core/Surging.Core.Consul/WatcherProvider/IClientWatchManager.cs @@ -4,8 +4,22 @@ namespace Surging.Core.Consul.WatcherProvider { + #region 接口 + + /// + /// Defines the + /// public interface IClientWatchManager { + #region 属性 + + /// + /// Gets or sets the DataWatches + /// Dictionary> DataWatches { get; set; } + + #endregion 属性 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/ChildWatchRegistration.cs b/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/ChildWatchRegistration.cs index e4eb6fa1f..7cdf685d2 100644 --- a/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/ChildWatchRegistration.cs +++ b/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/ChildWatchRegistration.cs @@ -4,19 +4,47 @@ namespace Surging.Core.Consul.WatcherProvider.Implementation { - public class ChildWatchRegistration : WatchRegistration + /// + /// Defines the + /// + public class ChildWatchRegistration : WatchRegistration { + #region 字段 + + /// + /// Defines the watchManager + /// private readonly IClientWatchManager watchManager; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The watchManager + /// The watcher + /// The clientPath public ChildWatchRegistration(IClientWatchManager watchManager, Watcher watcher, string clientPath) : base(watcher, clientPath) { this.watchManager = watchManager; } + #endregion 构造函数 + + #region 方法 + + /// + /// The GetWatches + /// + /// The protected override Dictionary> GetWatches() { return watchManager.DataWatches; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/ChildrenMonitorWatcher.cs b/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/ChildrenMonitorWatcher.cs index b723bd5df..70ada7ca2 100644 --- a/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/ChildrenMonitorWatcher.cs +++ b/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/ChildrenMonitorWatcher.cs @@ -1,20 +1,61 @@ using Consul; -using System; -using System.Threading.Tasks; using Surging.Core.Consul.Utilitys; -using System.Linq; using Surging.Core.Consul.WatcherProvider.Implementation; +using System; +using System.Linq; +using System.Threading.Tasks; namespace Surging.Core.Consul.WatcherProvider { + /// + /// Defines the + /// public class ChildrenMonitorWatcher : WatcherBase { + #region 字段 + + /// + /// Defines the _action + /// private readonly Action _action; - private readonly IClientWatchManager _manager; - private readonly string _path; + + /// + /// Defines the _clientCall + /// + private readonly Func> _clientCall; + + /// + /// Defines the _func + /// private readonly Func _func; + + /// + /// Defines the _manager + /// + private readonly IClientWatchManager _manager; + + /// + /// Defines the _path + /// + private readonly string _path; + + /// + /// Defines the _currentData + /// private string[] _currentData = new string[0]; - private readonly Func> _clientCall; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The clientCall + /// The manager + /// The path + /// The action + /// The func public ChildrenMonitorWatcher(Func> clientCall, IClientWatchManager manager, string path, Action action, Func func) { @@ -26,16 +67,29 @@ public ChildrenMonitorWatcher(Func> clientCall, IClientW RegisterWatch(); } + #endregion 构造函数 + + #region 方法 + + /// + /// The SetCurrentData + /// + /// The currentData + /// The public ChildrenMonitorWatcher SetCurrentData(string[] currentData) { _currentData = currentData ?? new string[0]; return this; } + /// + /// The ProcessImpl + /// + /// The protected override async Task ProcessImpl() { RegisterWatch(this); - var client =await _clientCall(); + var client = await _clientCall(); var result = await client.GetChildrenAsync(_path); if (result != null) { @@ -45,6 +99,10 @@ protected override async Task ProcessImpl() } } + /// + /// The RegisterWatch + /// + /// The watcher private void RegisterWatch(Watcher watcher = null) { ChildWatchRegistration wcb = null; @@ -58,5 +116,7 @@ private void RegisterWatch(Watcher watcher = null) } wcb.Register(); } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/ClientWatchManager.cs b/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/ClientWatchManager.cs index 6558af233..4e17ddcc1 100644 --- a/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/ClientWatchManager.cs +++ b/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/ClientWatchManager.cs @@ -12,24 +12,58 @@ namespace Surging.Core.Consul.WatcherProvider.Implementation { + /// + /// Defines the + /// public class ClientWatchManager : IClientWatchManager { - internal Dictionary> dataWatches = - new Dictionary>(); - private readonly Timer _timer; + #region 字段 + + /// + /// Defines the _logger + /// private readonly ILogger _logger; - public ClientWatchManager(ILogger logger,ConfigInfo config) + /// + /// Defines the _timer + /// + private readonly Timer _timer; + + /// + /// Defines the dataWatches + /// + internal Dictionary> dataWatches = + new Dictionary>(); + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The logger + /// The config + public ClientWatchManager(ILogger logger, ConfigInfo config) { var timeSpan = TimeSpan.FromSeconds(config.WatchInterval); _logger = logger; _timer = new Timer(async s => { - await Watching(); + await Watching(); }, null, timeSpan, timeSpan); } - public Dictionary> DataWatches { get + #endregion 构造函数 + + #region 属性 + + /// + /// Gets or sets the DataWatches + /// + public Dictionary> DataWatches + { + get { return dataWatches; } @@ -39,6 +73,14 @@ public Dictionary> DataWatches { get } } + #endregion 属性 + + #region 方法 + + /// + /// The Materialize + /// + /// The private HashSet Materialize() { HashSet result = new HashSet(); @@ -52,6 +94,10 @@ private HashSet Materialize() return result; } + /// + /// The Watching + /// + /// The private async Task Watching() { try @@ -68,6 +114,7 @@ private async Task Watching() _logger.LogError($"message:{ex.Message},Source:{ex.Source},Trace:{ex.StackTrace}"); } } - } -} + #endregion 方法 + } +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/NodeMonitorWatcher.cs b/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/NodeMonitorWatcher.cs index a4d835eb2..a94093317 100644 --- a/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/NodeMonitorWatcher.cs +++ b/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/NodeMonitorWatcher.cs @@ -7,16 +7,57 @@ namespace Surging.Core.Consul.WatcherProvider.Implementation { - class NodeMonitorWatcher : WatcherBase + /// + /// Defines the + /// + internal class NodeMonitorWatcher : WatcherBase { + #region 字段 + + /// + /// Defines the _action + /// private readonly Action _action; - private readonly IClientWatchManager _manager; + + /// + /// Defines the _clientCall + /// private readonly Func> _clientCall; + + /// + /// Defines the _manager + /// + private readonly IClientWatchManager _manager; + + /// + /// Defines the _path + /// private readonly string _path; + + /// + /// Defines the _allowChange + /// + internal Func _allowChange; + + /// + /// Defines the _currentData + /// private byte[] _currentData = new byte[0]; - Func _allowChange; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The clientCall + /// The manager + /// The path + /// The action + /// The allowChange public NodeMonitorWatcher(Func> clientCall, IClientWatchManager manager, string path, - Action action,Func allowChange) + Action action, Func allowChange) { this._action = action; _manager = manager; @@ -26,18 +67,31 @@ public NodeMonitorWatcher(Func> clientCall, IClientWatch RegisterWatch(); } + #endregion 构造函数 + + #region 方法 + + /// + /// The SetCurrentData + /// + /// The currentData + /// The public NodeMonitorWatcher SetCurrentData(byte[] currentData) { _currentData = currentData; return this; } + /// + /// The ProcessImpl + /// + /// The protected override async Task ProcessImpl() { RegisterWatch(this); - if (_allowChange!=null&&! _allowChange(_path)) return; + if (_allowChange != null && !_allowChange(_path)) return; var client = await _clientCall(); - var result =await client.GetDataAsync(_path); + var result = await client.GetDataAsync(_path); if (result != null) { _action(_currentData, result); @@ -45,6 +99,10 @@ protected override async Task ProcessImpl() } } + /// + /// The RegisterWatch + /// + /// The watcher private void RegisterWatch(Watcher watcher = null) { ChildWatchRegistration wcb = null; @@ -58,5 +116,7 @@ private void RegisterWatch(Watcher watcher = null) } wcb.Register(); } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/ReconnectionWatcher.cs b/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/ReconnectionWatcher.cs index 3bb42bfdf..a70ef2891 100644 --- a/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/ReconnectionWatcher.cs +++ b/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/ReconnectionWatcher.cs @@ -4,7 +4,10 @@ namespace Surging.Core.Consul.WatcherProvider { - public class ReconnectionWatcher + /// + /// Defines the + /// + public class ReconnectionWatcher { } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/WatchRegistration.cs b/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/WatchRegistration.cs index 0f21aa744..25b7ae267 100644 --- a/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/WatchRegistration.cs +++ b/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/WatchRegistration.cs @@ -1,23 +1,49 @@ using System; using System.Collections.Generic; -using System.Text; using System.Linq; +using System.Text; namespace Surging.Core.Consul.WatcherProvider.Implementation { - public abstract class WatchRegistration + /// + /// Defines the + /// + public abstract class WatchRegistration { - private readonly Watcher watcher; + #region 字段 + + /// + /// Defines the clientPath + /// private readonly string clientPath; + /// + /// Defines the watcher + /// + private readonly Watcher watcher; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The watcher + /// The clientPath protected WatchRegistration(Watcher watcher, string clientPath) { this.watcher = watcher; this.clientPath = clientPath; } - protected abstract Dictionary> GetWatches(); + #endregion 构造函数 + + #region 方法 + /// + /// The Register + /// public void Register() { var watches = GetWatches(); @@ -30,9 +56,17 @@ public void Register() watchers = new HashSet(); watches[clientPath] = watchers; } - if (!watchers.Any(p => p.GetType() == watcher.GetType())) - watchers.Add(watcher); + if (!watchers.Any(p => p.GetType() == watcher.GetType())) + watchers.Add(watcher); } } + + /// + /// The GetWatches + /// + /// The + protected abstract Dictionary> GetWatches(); + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/Watcher.cs b/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/Watcher.cs index f33c8e7b8..d2a9668c3 100644 --- a/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/Watcher.cs +++ b/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/Watcher.cs @@ -6,21 +6,56 @@ namespace Surging.Core.Consul.WatcherProvider { + /// + /// Defines the + /// public abstract class Watcher { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// protected Watcher() { } - + + #endregion 构造函数 + + #region 方法 + + /// + /// The Process + /// + /// The public abstract Task Process(); + #endregion 方法 + + /// + /// Defines the + /// public static class Event { + #region 枚举 + + /// + /// Defines the KeeperState + /// public enum KeeperState { + /// + /// Defines the Disconnected + /// Disconnected = 0, + + /// + /// Defines the SyncConnected + /// SyncConnected = 3, } + + #endregion 枚举 } } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/WatcherBase.cs b/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/WatcherBase.cs index ddae60631..3d7101c52 100644 --- a/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/WatcherBase.cs +++ b/src/Surging.Core/Surging.Core.Consul/WatcherProvider/Implementation/WatcherBase.cs @@ -6,19 +6,39 @@ namespace Surging.Core.Consul.WatcherProvider { + /// + /// Defines the + /// public abstract class WatcherBase : Watcher { + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// protected WatcherBase() { - } - + + #endregion 构造函数 + + #region 方法 + + /// + /// The Process + /// + /// The public override async Task Process() { - await ProcessImpl(); + await ProcessImpl(); } + /// + /// The ProcessImpl + /// + /// The protected abstract Task ProcessImpl(); + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.DNS/AppConfig.cs b/src/Surging.Core/Surging.Core.DNS/AppConfig.cs index 58dc00600..90665da50 100644 --- a/src/Surging.Core/Surging.Core.DNS/AppConfig.cs +++ b/src/Surging.Core/Surging.Core.DNS/AppConfig.cs @@ -6,10 +6,23 @@ namespace Surging.Core.DNS { - public static class AppConfig + /// + /// Defines the + /// + public static class AppConfig { + #region 属性 + + /// + /// Gets or sets the Configuration + /// public static IConfigurationRoot Configuration { get; set; } + /// + /// Gets or sets the DnsOption + /// public static DnsOption DnsOption { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.DNS/Configurations/DnsOption.cs b/src/Surging.Core/Surging.Core.DNS/Configurations/DnsOption.cs index aeebb9eb9..1368cdfdf 100644 --- a/src/Surging.Core/Surging.Core.DNS/Configurations/DnsOption.cs +++ b/src/Surging.Core/Surging.Core.DNS/Configurations/DnsOption.cs @@ -4,10 +4,23 @@ namespace Surging.Core.DNS.Configurations { + /// + /// Defines the + /// public class DnsOption { - public string RootDnsAddress { get; set; } + #region 属性 + /// + /// Gets or sets the QueryTimeout + /// public int QueryTimeout { get; set; } = 1000; + + /// + /// Gets or sets the RootDnsAddress + /// + public string RootDnsAddress { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.DNS/Configurations/EventBusConfigurationExtensions.cs b/src/Surging.Core/Surging.Core.DNS/Configurations/EventBusConfigurationExtensions.cs index fe4a4dd94..2ade7486f 100644 --- a/src/Surging.Core/Surging.Core.DNS/Configurations/EventBusConfigurationExtensions.cs +++ b/src/Surging.Core/Surging.Core.DNS/Configurations/EventBusConfigurationExtensions.cs @@ -8,28 +8,23 @@ namespace Surging.Core.DNS.Configurations { + /// + /// Defines the + /// public static class EventBusConfigurationExtensions { - public static IConfigurationBuilder AddDnsFile(this IConfigurationBuilder builder, string path) - { - return AddDnsFile(builder, provider: null, path: path, basePath: null, optional: false, reloadOnChange: false); - } - - public static IConfigurationBuilder AddDnsFile(this IConfigurationBuilder builder, string path, bool optional) - { - return AddDnsFile(builder, provider: null, path: path, basePath: null, optional: optional, reloadOnChange: false); - } - - public static IConfigurationBuilder AddDnsFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange) - { - return AddDnsFile(builder, provider: null, path: path, basePath: null, optional: optional, reloadOnChange: reloadOnChange); - } - - public static IConfigurationBuilder AddDnsFile(this IConfigurationBuilder builder, string path, string basePath, bool optional, bool reloadOnChange) - { - return AddDnsFile(builder, provider: null, path: path, basePath: basePath, optional: optional, reloadOnChange: reloadOnChange); - } + #region 方法 + /// + /// The AddDnsFile + /// + /// The builder + /// The provider + /// The path + /// The basePath + /// The optional + /// The reloadOnChange + /// The public static IConfigurationBuilder AddDnsFile(this IConfigurationBuilder builder, IFileProvider provider, string path, string basePath, bool optional, bool reloadOnChange) { Check.NotNull(builder, "builder"); @@ -54,5 +49,57 @@ public static IConfigurationBuilder AddDnsFile(this IConfigurationBuilder builde AppConfig.DnsOption = AppConfig.Configuration.Get(); return builder; } + + /// + /// The AddDnsFile + /// + /// The builder + /// The path + /// The + public static IConfigurationBuilder AddDnsFile(this IConfigurationBuilder builder, string path) + { + return AddDnsFile(builder, provider: null, path: path, basePath: null, optional: false, reloadOnChange: false); + } + + /// + /// The AddDnsFile + /// + /// The builder + /// The path + /// The optional + /// The + public static IConfigurationBuilder AddDnsFile(this IConfigurationBuilder builder, string path, bool optional) + { + return AddDnsFile(builder, provider: null, path: path, basePath: null, optional: optional, reloadOnChange: false); + } + + /// + /// The AddDnsFile + /// + /// The builder + /// The path + /// The optional + /// The reloadOnChange + /// The + public static IConfigurationBuilder AddDnsFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange) + { + return AddDnsFile(builder, provider: null, path: path, basePath: null, optional: optional, reloadOnChange: reloadOnChange); + } + + /// + /// The AddDnsFile + /// + /// The builder + /// The path + /// The basePath + /// The optional + /// The reloadOnChange + /// The + public static IConfigurationBuilder AddDnsFile(this IConfigurationBuilder builder, string path, string basePath, bool optional, bool reloadOnChange) + { + return AddDnsFile(builder, provider: null, path: path, basePath: basePath, optional: optional, reloadOnChange: reloadOnChange); + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.DNS/Configurations/EventBusConfigurationProvider.cs b/src/Surging.Core/Surging.Core.DNS/Configurations/EventBusConfigurationProvider.cs index 40dfdf54e..eb1852e87 100644 --- a/src/Surging.Core/Surging.Core.DNS/Configurations/EventBusConfigurationProvider.cs +++ b/src/Surging.Core/Surging.Core.DNS/Configurations/EventBusConfigurationProvider.cs @@ -7,14 +7,35 @@ namespace Surging.Core.DNS.Configurations { - public class EventBusConfigurationProvider : FileConfigurationProvider + /// + /// Defines the + /// + public class EventBusConfigurationProvider : FileConfigurationProvider { - public EventBusConfigurationProvider(EventBusConfigurationSource source) : base(source) { } + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The source + public EventBusConfigurationProvider(EventBusConfigurationSource source) : base(source) + { + } + + #endregion 构造函数 + + #region 方法 + + /// + /// The Load + /// + /// The stream public override void Load(Stream stream) { var parser = new JsonConfigurationParser(); this.Data = parser.Parse(stream, null); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.DNS/Configurations/EventBusConfigurationSource.cs b/src/Surging.Core/Surging.Core.DNS/Configurations/EventBusConfigurationSource.cs index 3a09f88e7..8383fb462 100644 --- a/src/Surging.Core/Surging.Core.DNS/Configurations/EventBusConfigurationSource.cs +++ b/src/Surging.Core/Surging.Core.DNS/Configurations/EventBusConfigurationSource.cs @@ -5,14 +5,33 @@ namespace Surging.Core.DNS.Configurations { - public class EventBusConfigurationSource : FileConfigurationSource + /// + /// Defines the + /// + public class EventBusConfigurationSource : FileConfigurationSource { + #region 属性 + + /// + /// Gets or sets the ConfigurationKeyPrefix + /// public string ConfigurationKeyPrefix { get; set; } + #endregion 属性 + + #region 方法 + + /// + /// The Build + /// + /// The builder + /// The public override IConfigurationProvider Build(IConfigurationBuilder builder) { FileProvider = FileProvider ?? builder.GetFileProvider(); return new EventBusConfigurationProvider(this); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.DNS/DnsProtocolModule.cs b/src/Surging.Core/Surging.Core.DNS/DnsProtocolModule.cs index a5b540854..c4adc0d58 100644 --- a/src/Surging.Core/Surging.Core.DNS/DnsProtocolModule.cs +++ b/src/Surging.Core/Surging.Core.DNS/DnsProtocolModule.cs @@ -12,8 +12,17 @@ namespace Surging.Core.DNS { - public class DnsProtocolModule : EnginePartModule + /// + /// Defines the + /// + public class DnsProtocolModule : EnginePartModule { + #region 方法 + + /// + /// The Initialize + /// + /// The context public override void Initialize(AppModuleContext context) { base.Initialize(context); @@ -49,6 +58,10 @@ protected override void RegisterBuilder(ContainerBuilderWrapper builder) } } + /// + /// The RegisterDefaultProtocol + /// + /// The builder private static void RegisterDefaultProtocol(ContainerBuilderWrapper builder) { builder.Register(provider => @@ -66,13 +79,15 @@ private static void RegisterDefaultProtocol(ContainerBuilderWrapper builder) await messageListener.StartAsync(endPoint); return messageListener; }, serviceExecutor); - }).As(); } + /// + /// The RegisterDnsProtocol + /// + /// The builder private static void RegisterDnsProtocol(ContainerBuilderWrapper builder) { - builder.Register(provider => { return new DotNettyDnsServerMessageListener(provider.Resolve>(), @@ -88,8 +103,9 @@ private static void RegisterDnsProtocol(ContainerBuilderWrapper builder) await messageListener.StartAsync(endPoint); return messageListener; }, serviceExecutor); - }).As(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.DNS/DnsServiceExecutor.cs b/src/Surging.Core/Surging.Core.DNS/DnsServiceExecutor.cs index 7ab391152..39b2563ee 100644 --- a/src/Surging.Core/Surging.Core.DNS/DnsServiceExecutor.cs +++ b/src/Surging.Core/Surging.Core.DNS/DnsServiceExecutor.cs @@ -13,33 +13,49 @@ namespace Surging.Core.DNS { - public class DnsServiceExecutor : IServiceExecutor + /// + /// Defines the + /// + public class DnsServiceExecutor : IServiceExecutor { - #region Field + #region 字段 + /// + /// Defines the _dnsServiceEntryProvider + /// private readonly IDnsServiceEntryProvider _dnsServiceEntryProvider; + + /// + /// Defines the _logger + /// private readonly ILogger _logger; - #endregion Field + #endregion 字段 - #region Constructor + #region 构造函数 - public DnsServiceExecutor(IDnsServiceEntryProvider dnsServiceEntryProvider, + /// + /// Initializes a new instance of the class. + /// + /// The dnsServiceEntryProvider + /// The logger + public DnsServiceExecutor(IDnsServiceEntryProvider dnsServiceEntryProvider, ILogger logger) { _dnsServiceEntryProvider = dnsServiceEntryProvider; _logger = logger; } - #endregion Constructor + #endregion 构造函数 - #region Implementation of IServiceExecutor + #region 方法 /// /// 执行。 /// /// 消息发送者。 /// 调用消息。 + /// The public async Task ExecuteAsync(IMessageSender sender, TransportMessage message) { if (_logger.IsEnabled(LogLevel.Trace)) @@ -69,18 +85,19 @@ public async Task ExecuteAsync(IMessageSender sender, TransportMessage message) await SendRemoteInvokeResult(sender, dnsTransportMessage); } - #endregion Implementation of IServiceExecutor - - #region Private Method - - + /// + /// The LocalExecuteAsync + /// + /// The entry + /// The message + /// The private async Task LocalExecuteAsync(DnsServiceEntry entry, DnsTransportMessage message) { HttpResultMessage resultMessage = new HttpResultMessage(); try { var dnsQuestion = message.DnsQuestion; - message.Address= await entry.Behavior.DomainResolve(dnsQuestion.Name); + message.Address = await entry.Behavior.DomainResolve(dnsQuestion.Name); } catch (Exception exception) { @@ -90,6 +107,12 @@ private async Task LocalExecuteAsync(DnsServiceEntry entry, return message; } + /// + /// The SendRemoteInvokeResult + /// + /// The sender + /// The resultMessage + /// The private async Task SendRemoteInvokeResult(IMessageSender sender, DnsTransportMessage resultMessage) { try @@ -108,6 +131,6 @@ private async Task SendRemoteInvokeResult(IMessageSender sender, DnsTransportMes } } - #endregion Private Method + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.DNS/DnsServiceHost.cs b/src/Surging.Core/Surging.Core.DNS/DnsServiceHost.cs index e93753a57..e109d7fb6 100644 --- a/src/Surging.Core/Surging.Core.DNS/DnsServiceHost.cs +++ b/src/Surging.Core/Surging.Core.DNS/DnsServiceHost.cs @@ -10,23 +10,44 @@ namespace Surging.Core.DNS { - public class DnsServiceHost : ServiceHostAbstract + /// + /// Defines the + /// + public class DnsServiceHost : ServiceHostAbstract { - #region Field + #region 字段 + /// + /// Defines the _messageListenerFactory + /// private readonly Func> _messageListenerFactory; + + /// + /// Defines the _serverMessageListener + /// private IMessageListener _serverMessageListener; - #endregion Field + #endregion 字段 + + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The messageListenerFactory + /// The serviceExecutor public DnsServiceHost(Func> messageListenerFactory, IServiceExecutor serviceExecutor) : base(serviceExecutor) { _messageListenerFactory = messageListenerFactory; } - #region Overrides of ServiceHostAbstract + #endregion 构造函数 + + #region 方法 - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + /// The Dispose + /// public override void Dispose() { (_serverMessageListener as IDisposable)?.Dispose(); @@ -51,11 +72,17 @@ await Task.Run(() => }; } + /// + /// The StartAsync + /// + /// The ip + /// The port + /// The public override async Task StartAsync(string ip, int port) { if (_serverMessageListener != null) return; - _serverMessageListener = await _messageListenerFactory(new IPEndPoint(IPAddress.Parse(ip),53)); + _serverMessageListener = await _messageListenerFactory(new IPEndPoint(IPAddress.Parse(ip), 53)); _serverMessageListener.Received += async (sender, message) => { await Task.Run(() => @@ -65,6 +92,6 @@ await Task.Run(() => }; } - #endregion Overrides of ServiceHostAbstract + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.DNS/DnsTransportMessage.cs b/src/Surging.Core/Surging.Core.DNS/DnsTransportMessage.cs index 0a95cab7c..268fe2200 100644 --- a/src/Surging.Core/Surging.Core.DNS/DnsTransportMessage.cs +++ b/src/Surging.Core/Surging.Core.DNS/DnsTransportMessage.cs @@ -7,12 +7,28 @@ namespace Surging.Core.DNS { - public class DnsTransportMessage + /// + /// Defines the + /// + public class DnsTransportMessage { - public IDnsResponse DnsResponse { get; set; } + #region 属性 + + /// + /// Gets or sets the Address + /// + public IPAddress Address { get; set; } + /// + /// Gets or sets the DnsQuestion + /// public IDnsQuestion DnsQuestion { get; set; } - public IPAddress Address { get; set; } + /// + /// Gets or sets the DnsResponse + /// + public IDnsResponse DnsResponse { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.DNS/DotNettyDnsServerMessageListener.cs b/src/Surging.Core/Surging.Core.DNS/DotNettyDnsServerMessageListener.cs index a84355300..7a1459e44 100644 --- a/src/Surging.Core/Surging.Core.DNS/DotNettyDnsServerMessageListener.cs +++ b/src/Surging.Core/Surging.Core.DNS/DotNettyDnsServerMessageListener.cs @@ -21,21 +21,42 @@ namespace Surging.Core.DNS { - class DotNettyDnsServerMessageListener : IMessageListener, IDisposable + /// + /// Defines the + /// + internal class DotNettyDnsServerMessageListener : IMessageListener, IDisposable { - #region Field + #region 字段 + /// + /// Defines the _logger + /// private readonly ILogger _logger; + + /// + /// Defines the _transportMessageDecoder + /// private readonly ITransportMessageDecoder _transportMessageDecoder; + + /// + /// Defines the _transportMessageEncoder + /// private readonly ITransportMessageEncoder _transportMessageEncoder; - private IChannel _channel; - public event ReceivedDelegate Received; + /// + /// Defines the _channel + /// + private IChannel _channel; - #endregion Field + #endregion 字段 - #region Constructor + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The logger + /// The codecFactory public DotNettyDnsServerMessageListener(ILogger logger, ITransportMessageCodecFactory codecFactory) { _logger = logger; @@ -43,8 +64,60 @@ public DotNettyDnsServerMessageListener(ILogger + /// Defines the Received + /// + public event ReceivedDelegate Received; + + #endregion 事件 + + #region 方法 + + /// + /// The CloseAsync + /// + public void CloseAsync() + { + Task.Run(async () => + { + await _channel.EventLoop.ShutdownGracefullyAsync(); + await _channel.CloseAsync(); + }).Wait(); + } + + /// + /// The Dispose + /// + public void Dispose() + { + Task.Run(async () => + { + await _channel.DisconnectAsync(); + }).Wait(); + } + + /// + /// The OnReceived + /// + /// The sender + /// The message + /// The + public async Task OnReceived(IMessageSender sender, TransportMessage message) + { + if (Received == null) + return; + await Received(sender, message); + } + + /// + /// The StartAsync + /// + /// The endPoint + /// The public async Task StartAsync(EndPoint endPoint) { if (_logger.IsEnabled(LogLevel.Debug)) @@ -68,7 +141,6 @@ public async Task StartAsync(EndPoint endPoint) })).Option(ChannelOption.SoBroadcast, true); try { - _channel = await bootstrap.BindAsync(endPoint); if (_logger.IsEnabled(LogLevel.Debug)) _logger.LogDebug($"DNS服务主机启动成功,监听地址:{endPoint}。"); @@ -77,54 +149,70 @@ public async Task StartAsync(EndPoint endPoint) { _logger.LogError($"DNS服务主机启动失败,监听地址:{endPoint}。 "); } - - } - - public void CloseAsync() - { - Task.Run(async () => - { - await _channel.EventLoop.ShutdownGracefullyAsync(); - await _channel.CloseAsync(); - }).Wait(); - } - - #region Implementation of IDisposable - - - public void Dispose() - { - Task.Run(async () => - { - await _channel.DisconnectAsync(); - }).Wait(); - } - - public async Task OnReceived(IMessageSender sender, TransportMessage message) - { - if (Received == null) - return; - await Received(sender, message); } - #endregion Implementation of IDisposable + #endregion 方法 + /// + /// Defines the + /// private class ServerHandler : SimpleChannelInboundHandler { + #region 字段 - private readonly Action _readAction; + /// + /// Defines the _logger + /// private readonly ILogger _logger; + + /// + /// Defines the _readAction + /// + private readonly Action _readAction; + + /// + /// Defines the _serializer + /// private readonly ISerializer _serializer; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The readAction + /// The logger public ServerHandler(Action readAction, ILogger logger) { _readAction = readAction; - _logger = logger; + _logger = logger; } - protected override void ChannelRead0(IChannelHandlerContext ctx, DatagramDnsQuery query) + #endregion 构造函数 + + #region 方法 + + /// + /// The ExceptionCaught + /// + /// The context + /// The exception + public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) { + context.CloseAsync(); + if (_logger.IsEnabled(LogLevel.Error)) + _logger.LogError(exception, $"与服务器:{context.Channel.RemoteAddress}通信时发送了错误。"); + } + /// + /// The ChannelRead0 + /// + /// The ctx + /// The query + protected override void ChannelRead0(IChannelHandlerContext ctx, DatagramDnsQuery query) + { DatagramDnsResponse response = new DatagramDnsResponse(query.Recipient, query.Sender, query.Id); DefaultDnsQuestion dnsQuestion = query.GetRecord(DnsSection.QUESTION); response.AddRecord(DnsSection.QUESTION, dnsQuestion); @@ -135,12 +223,7 @@ protected override void ChannelRead0(IChannelHandlerContext ctx, DatagramDnsQuer })); } - public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) - { - context.CloseAsync(); - if (_logger.IsEnabled(LogLevel.Error)) - _logger.LogError(exception, $"与服务器:{context.Channel.RemoteAddress}通信时发送了错误。"); - } + #endregion 方法 } } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.DNS/DotNettyDnsServerMessageSender.cs b/src/Surging.Core/Surging.Core.DNS/DotNettyDnsServerMessageSender.cs index 243c0dcb9..7051bdc8d 100644 --- a/src/Surging.Core/Surging.Core.DNS/DotNettyDnsServerMessageSender.cs +++ b/src/Surging.Core/Surging.Core.DNS/DotNettyDnsServerMessageSender.cs @@ -1,6 +1,4 @@ -using System.Net; -using System.Threading.Tasks; -using ARSoft.Tools.Net.Dns; +using ARSoft.Tools.Net.Dns; using DotNetty.Buffers; using DotNetty.Codecs.DNS; using DotNetty.Codecs.DNS.Messages; @@ -13,48 +11,107 @@ using Surging.Core.DNS.Extensions; using Surging.Core.DNS.Utilities; using Surging.Core.DotNetty; +using System.Net; +using System.Threading.Tasks; namespace Surging.Core.DNS { - class DotNettyDnsServerMessageSender : DotNettyMessageSender, IMessageSender + /// + /// Defines the + /// + internal class DotNettyDnsServerMessageSender : DotNettyMessageSender, IMessageSender { + #region 字段 + + /// + /// Defines the _context + /// private readonly IChannelHandlerContext _context; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The transportMessageEncoder + /// The context public DotNettyDnsServerMessageSender(ITransportMessageEncoder transportMessageEncoder, IChannelHandlerContext context) : base(transportMessageEncoder) { _context = context; } + #endregion 构造函数 + + #region 方法 + + /// + /// The GetDnsMessage + /// + /// The name + /// The recordType + /// The + public async Task GetDnsMessage(string name, DnsRecordType recordType) + { + return await DnsClientProvider.Instance().Resolve(name, recordType); + } + + /// + /// The SendAndFlushAsync + /// + /// The message + /// The public async Task SendAndFlushAsync(TransportMessage message) { - var response=await WriteResponse(message); + var response = await WriteResponse(message); await _context.WriteAndFlushAsync(response); } + /// + /// The SendAsync + /// + /// The message + /// The public async Task SendAsync(TransportMessage message) { var response = await WriteResponse(message); await _context.WriteAsync(response); } - private IDnsResponse GetDnsResponse(TransportMessage message) - { - if (message.Content !=null && !message.IsDnsResultMessage()) + /// + /// The GetDnsQuestion + /// + /// The message + /// The + private IDnsQuestion GetDnsQuestion(TransportMessage message) + { + if (message.Content != null && !message.IsDnsResultMessage()) return null; - var transportMessage = message.GetContent(); - return transportMessage.DnsResponse; + var transportMessage = message.GetContent(); + return transportMessage.DnsQuestion; } - private IDnsQuestion GetDnsQuestion(TransportMessage message) + /// + /// The GetDnsResponse + /// + /// The message + /// The + private IDnsResponse GetDnsResponse(TransportMessage message) { if (message.Content != null && !message.IsDnsResultMessage()) return null; var transportMessage = message.GetContent(); - return transportMessage.DnsQuestion; + return transportMessage.DnsResponse; } + /// + /// The GetIpAddr + /// + /// The message + /// The private IPAddress GetIpAddr(TransportMessage message) { if (message.Content != null && !message.IsDnsResultMessage()) @@ -64,8 +121,13 @@ private IPAddress GetIpAddr(TransportMessage message) return transportMessage.Address; } + /// + /// The WriteResponse + /// + /// The message + /// The private async Task WriteResponse(TransportMessage message) - { + { var response = GetDnsResponse(message); var dnsQuestion = GetDnsQuestion(message); var ipAddr = GetIpAddr(message); @@ -78,7 +140,7 @@ private async Task WriteResponse(TransportMessage message) } else { - var dnsMessage =await GetDnsMessage(dnsQuestion.Name, dnsQuestion.Type); + var dnsMessage = await GetDnsMessage(dnsQuestion.Name, dnsQuestion.Type); if (dnsMessage != null) { foreach (DnsRecordBase dnsRecord in dnsMessage.AnswerRecords) @@ -102,9 +164,6 @@ private async Task WriteResponse(TransportMessage message) return response; } - public async Task GetDnsMessage(string name, DnsRecordType recordType) - { - return await DnsClientProvider.Instance().Resolve(name, recordType); - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.DNS/Extensions/TransportMessageExtensions.cs b/src/Surging.Core/Surging.Core.DNS/Extensions/TransportMessageExtensions.cs index 6d07cdbab..673b62153 100644 --- a/src/Surging.Core/Surging.Core.DNS/Extensions/TransportMessageExtensions.cs +++ b/src/Surging.Core/Surging.Core.DNS/Extensions/TransportMessageExtensions.cs @@ -1,16 +1,26 @@ using Surging.Core.CPlatform.Messages; using System.Runtime.CompilerServices; - namespace Surging.Core.DNS.Extensions { + /// + /// Defines the + /// public static class TransportMessageExtensions { + #region 方法 + /// + /// The IsDnsResultMessage + /// + /// The message + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsDnsResultMessage(this TransportMessage message) { return message.ContentType == typeof(DnsTransportMessage).FullName; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.DNS/Runtime/DnsBehavior.cs b/src/Surging.Core/Surging.Core.DNS/Runtime/DnsBehavior.cs index 8f10a0e3f..f832feecd 100644 --- a/src/Surging.Core/Surging.Core.DNS/Runtime/DnsBehavior.cs +++ b/src/Surging.Core/Surging.Core.DNS/Runtime/DnsBehavior.cs @@ -1,92 +1,152 @@ -using Org.BouncyCastle.Asn1.Ocsp; +using Autofac; +using Org.BouncyCastle.Asn1.Ocsp; +using Surging.Core.CPlatform.EventBus.Events; +using Surging.Core.CPlatform.EventBus.Implementation; using Surging.Core.CPlatform.Ioc; +using Surging.Core.CPlatform.Module; +using Surging.Core.CPlatform.Utilities; using Surging.Core.ProxyGenerator; using System; using System.Collections.Generic; using System.Net; -using Surging.Core.CPlatform.Utilities; -using ServiceLocator = Surging.Core.CPlatform.Utilities.ServiceLocator; -using Autofac; -using Surging.Core.CPlatform.EventBus.Implementation; -using Surging.Core.CPlatform.EventBus.Events; -using Surging.Core.CPlatform.Module; using System.Threading.Tasks; +using ServiceLocator = Surging.Core.CPlatform.Utilities.ServiceLocator; namespace Surging.Core.DNS.Runtime { + /// + /// Defines the + /// public abstract class DnsBehavior : IServiceBehavior { - public T CreateProxy(string key) where T : class + #region 方法 + + /// + /// The CreateProxy + /// + /// + /// The + public T CreateProxy() where T : class { - return ServiceLocator.GetService().CreateProxy(key); + return ServiceLocator.GetService().CreateProxy(); } - public object CreateProxy(Type type) + /// + /// The CreateProxy + /// + /// + /// The key + /// The + public T CreateProxy(string key) where T : class { - return ServiceLocator.GetService().CreateProxy(type); + return ServiceLocator.GetService().CreateProxy(key); } + /// + /// The CreateProxy + /// + /// The key + /// The type + /// The public object CreateProxy(string key, Type type) { return ServiceLocator.GetService().CreateProxy(key, type); } - public T CreateProxy() where T : class - { - return ServiceLocator.GetService().CreateProxy(); - } - - public T GetService(string key) where T : class + /// + /// The CreateProxy + /// + /// The type + /// The + public object CreateProxy(Type type) { - if (ServiceLocator.Current.IsRegisteredWithKey(key)) - return ServiceLocator.GetService(key); - else - return ServiceLocator.GetService().CreateProxy(key); + return ServiceLocator.GetService().CreateProxy(type); } + /// + /// The GetService + /// + /// + /// The public T GetService() where T : class { if (ServiceLocator.Current.IsRegistered()) return ServiceLocator.GetService(); else return ServiceLocator.GetService().CreateProxy(); - } - public object GetService(Type type) + /// + /// The GetService + /// + /// + /// The key + /// The + public T GetService(string key) where T : class { - if (ServiceLocator.Current.IsRegistered(type)) - return ServiceLocator.GetService(type); + if (ServiceLocator.Current.IsRegisteredWithKey(key)) + return ServiceLocator.GetService(key); else - return ServiceLocator.GetService().CreateProxy(type); + return ServiceLocator.GetService().CreateProxy(key); } + /// + /// The GetService + /// + /// The key + /// The type + /// The public object GetService(string key, Type type) { if (ServiceLocator.Current.IsRegisteredWithKey(key, type)) return ServiceLocator.GetService(key, type); else return ServiceLocator.GetService().CreateProxy(key, type); + } + /// + /// The GetService + /// + /// The type + /// The + public object GetService(Type type) + { + if (ServiceLocator.Current.IsRegistered(type)) + return ServiceLocator.GetService(type); + else + return ServiceLocator.GetService().CreateProxy(type); } + /// + /// The Publish + /// + /// The event public void Publish(IntegrationEvent @event) { GetService().Publish(@event); } + /// + /// The Resolve + /// + /// The domainName + /// The public abstract Task Resolve(string domainName); - + /// + /// The DomainResolve + /// + /// The domainName + /// The internal async Task DomainResolve(string domainName) { domainName = domainName.TrimEnd('.'); var prefixLen = domainName.IndexOf("."); IPAddress result = null; if (prefixLen > 0) - { + { var prefixName = domainName.Substring(0, prefixLen).ToString(); - var pathLen= domainName.LastIndexOf(".") - prefixLen - 1; + var pathLen = domainName.LastIndexOf(".") - prefixLen - 1; if (pathLen > 0) { var routePath = domainName.Substring(prefixLen + 1, pathLen).Replace(".", "/").ToString(); @@ -100,5 +160,7 @@ internal async Task DomainResolve(string domainName) result = await Resolve(domainName) ?? result; return result; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.DNS/Runtime/DnsServiceEntry.cs b/src/Surging.Core/Surging.Core.DNS/Runtime/DnsServiceEntry.cs index 32ef644c3..476cc6c80 100644 --- a/src/Surging.Core/Surging.Core.DNS/Runtime/DnsServiceEntry.cs +++ b/src/Surging.Core/Surging.Core.DNS/Runtime/DnsServiceEntry.cs @@ -4,13 +4,28 @@ namespace Surging.Core.DNS.Runtime { - public class DnsServiceEntry + /// + /// Defines the + /// + public class DnsServiceEntry { + #region 属性 + + /// + /// Gets or sets the Behavior + /// + public DnsBehavior Behavior { get; set; } + + /// + /// Gets or sets the Path + /// public string Path { get; set; } + /// + /// Gets or sets the Type + /// public Type Type { get; set; } - public DnsBehavior Behavior { get; set; } - + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.DNS/Runtime/IDnsServiceEntryProvider.cs b/src/Surging.Core/Surging.Core.DNS/Runtime/IDnsServiceEntryProvider.cs index 8220b4ce2..cd328a8c9 100644 --- a/src/Surging.Core/Surging.Core.DNS/Runtime/IDnsServiceEntryProvider.cs +++ b/src/Surging.Core/Surging.Core.DNS/Runtime/IDnsServiceEntryProvider.cs @@ -4,8 +4,23 @@ namespace Surging.Core.DNS.Runtime { + #region 接口 + + /// + /// Defines the + /// public interface IDnsServiceEntryProvider { + #region 方法 + + /// + /// The GetEntry + /// + /// The DnsServiceEntry GetEntry(); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.DNS/Runtime/Implementation/DefaultDnsServiceEntryProvider.cs b/src/Surging.Core/Surging.Core.DNS/Runtime/Implementation/DefaultDnsServiceEntryProvider.cs index e9352fb5a..ec602c4a1 100644 --- a/src/Surging.Core/Surging.Core.DNS/Runtime/Implementation/DefaultDnsServiceEntryProvider.cs +++ b/src/Surging.Core/Surging.Core.DNS/Runtime/Implementation/DefaultDnsServiceEntryProvider.cs @@ -11,31 +11,79 @@ namespace Surging.Core.DNS.Runtime.Implementation { - public class DefaultDnsServiceEntryProvider:IDnsServiceEntryProvider + /// + /// Defines the + /// + public class DefaultDnsServiceEntryProvider : IDnsServiceEntryProvider { - #region Field + #region 字段 - private readonly IEnumerable _types; + /// + /// Defines the _logger + /// private readonly ILogger _logger; + + /// + /// Defines the _serviceProvider + /// private readonly CPlatformContainer _serviceProvider; - private DnsServiceEntry _dnsServiceEntry; - #endregion Field + /// + /// Defines the _types + /// + private readonly IEnumerable _types; + + /// + /// Defines the _dnsServiceEntry + /// + private DnsServiceEntry _dnsServiceEntry; + + #endregion 字段 - #region Constructor + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The serviceEntryProvider + /// The logger + /// The serviceProvider public DefaultDnsServiceEntryProvider(IServiceEntryProvider serviceEntryProvider, ILogger logger, CPlatformContainer serviceProvider) { _types = serviceEntryProvider.GetTypes(); _logger = logger; - _serviceProvider = serviceProvider; + _serviceProvider = serviceProvider; } - #endregion Constructor + #endregion 构造函数 + + #region 方法 - #region Implementation of IServiceEntryProvider + /// + /// The CreateServiceEntry + /// + /// The service + /// The + public DnsServiceEntry CreateServiceEntry(Type service) + { + DnsServiceEntry result = null; + var routeTemplate = service.GetCustomAttribute(); + var objInstance = _serviceProvider.GetInstances(service); + var behavior = objInstance as DnsBehavior; + var path = RoutePatternParser.Parse(routeTemplate.RouteTemplate, service.Name); + if (path.Length > 0 && path[0] != '/') + path = $"/{path}"; + if (behavior != null) + result = new DnsServiceEntry + { + Behavior = behavior, + Type = behavior.GetType(), + Path = path, + }; + return result; + } /// /// 获取服务条目集合。 @@ -46,7 +94,7 @@ public DnsServiceEntry GetEntry() var services = _types.ToArray(); if (_dnsServiceEntry == null) { - _dnsServiceEntry =new DnsServiceEntry(); + _dnsServiceEntry = new DnsServiceEntry(); foreach (var service in services) { var entry = CreateServiceEntry(service); @@ -63,25 +111,7 @@ public DnsServiceEntry GetEntry() } return _dnsServiceEntry; } - #endregion - public DnsServiceEntry CreateServiceEntry(Type service) - { - DnsServiceEntry result = null; - var routeTemplate = service.GetCustomAttribute(); - var objInstance = _serviceProvider.GetInstances(service); - var behavior = objInstance as DnsBehavior; - var path = RoutePatternParser.Parse(routeTemplate.RouteTemplate, service.Name); - if (path.Length > 0 && path[0] != '/') - path = $"/{path}"; - if (behavior != null) - result = new DnsServiceEntry - { - Behavior = behavior, - Type = behavior.GetType(), - Path = path, - }; - return result; - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.DNS/Utilities/DnsClientProvider.cs b/src/Surging.Core/Surging.Core.DNS/Utilities/DnsClientProvider.cs index 953ef2156..596de46ab 100644 --- a/src/Surging.Core/Surging.Core.DNS/Utilities/DnsClientProvider.cs +++ b/src/Surging.Core/Surging.Core.DNS/Utilities/DnsClientProvider.cs @@ -6,24 +6,46 @@ namespace Surging.Core.DNS.Utilities { + /// + /// Defines the + /// public class DnsClientProvider { - public async Task Resolve(string name, DnsRecordType recordType , DnsRecordClass recordClass = DnsRecordClass.IN) + #region 方法 + + /// + /// The Instance + /// + /// The + public static DnsClientProvider Instance() { - var dnsMessage =await GetDnsClient().ResolveAsync(DomainName.Parse(name), (RecordType)recordType.IntValue,(RecordClass)(int)recordClass); - return dnsMessage; + return new DnsClientProvider(); } - public DnsClient GetDnsClient() + /// + /// The GetDnsClient + /// + /// The + public DnsClient GetDnsClient() { var dnsOption = AppConfig.DnsOption; - DnsClient dnsClient = new DnsClient(IPAddress.Parse(dnsOption.RootDnsAddress), dnsOption.QueryTimeout); + DnsClient dnsClient = new DnsClient(IPAddress.Parse(dnsOption.RootDnsAddress), dnsOption.QueryTimeout); return dnsClient; } - public static DnsClientProvider Instance() + /// + /// The Resolve + /// + /// The name + /// The recordType + /// The recordClass + /// The + public async Task Resolve(string name, DnsRecordType recordType, DnsRecordClass recordClass = DnsRecordClass.IN) { - return new DnsClientProvider(); + var dnsMessage = await GetDnsClient().ResolveAsync(DomainName.Parse(name), (RecordType)recordType.IntValue, (RecordClass)(int)recordClass); + return dnsMessage; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.DotNetty/Adapter/TransportMessageChannelHandlerAdapter.cs b/src/Surging.Core/Surging.Core.DotNetty/Adapter/TransportMessageChannelHandlerAdapter.cs index ae6b76c9c..b195e9f10 100644 --- a/src/Surging.Core/Surging.Core.DotNetty/Adapter/TransportMessageChannelHandlerAdapter.cs +++ b/src/Surging.Core/Surging.Core.DotNetty/Adapter/TransportMessageChannelHandlerAdapter.cs @@ -8,27 +8,50 @@ namespace Surging.Core.DotNetty.Adapter { - class TransportMessageChannelHandlerAdapter : ChannelHandlerAdapter + /// + /// Defines the + /// + internal class TransportMessageChannelHandlerAdapter : ChannelHandlerAdapter { + #region 字段 + + /// + /// Defines the _transportMessageDecoder + /// private readonly ITransportMessageDecoder _transportMessageDecoder; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The transportMessageDecoder public TransportMessageChannelHandlerAdapter(ITransportMessageDecoder transportMessageDecoder) { _transportMessageDecoder = transportMessageDecoder; } - #region Overrides of ChannelHandlerAdapter + #endregion 构造函数 + + #region 方法 + /// + /// The ChannelRead + /// + /// The context + /// The message public override void ChannelRead(IChannelHandlerContext context, object message) { var buffer = (IByteBuffer)message; - var data = new byte[buffer.ReadableBytes]; - buffer.ReadBytes(data); - var transportMessage = _transportMessageDecoder.Decode(data); - context.FireChannelRead(transportMessage); - ReferenceCountUtil.Release(buffer); + var data = new byte[buffer.ReadableBytes]; + buffer.ReadBytes(data); + var transportMessage = _transportMessageDecoder.Decode(data); + context.FireChannelRead(transportMessage); + ReferenceCountUtil.Release(buffer); } - #endregion Overrides of ChannelHandlerAdapter + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.DotNetty/ContainerBuilderExtensions.cs b/src/Surging.Core/Surging.Core.DotNetty/ContainerBuilderExtensions.cs index 8f03a5574..1235175ff 100644 --- a/src/Surging.Core/Surging.Core.DotNetty/ContainerBuilderExtensions.cs +++ b/src/Surging.Core/Surging.Core.DotNetty/ContainerBuilderExtensions.cs @@ -10,14 +10,18 @@ namespace Surging.Core.DotNetty { + /// + /// Defines the + /// public static class ContainerBuilderExtensions { + #region 方法 + /// /// 使用DotNetty进行传输。 /// /// 服务构建者。 /// 服务构建者。 - [Obsolete] public static IServiceBuilder UseDotNettyTransport(this IServiceBuilder builder) { @@ -41,6 +45,10 @@ public static IServiceBuilder UseDotNettyTransport(this IServiceBuilder builder) return builder; } + /// + /// The RegisterDefaultProtocol + /// + /// The builder private static void RegisterDefaultProtocol(ContainerBuilder builder) { builder.Register(provider => @@ -50,7 +58,6 @@ private static void RegisterDefaultProtocol(ContainerBuilder builder) }).SingleInstance(); builder.Register(provider => { - var serviceExecutor = provider.ResolveKeyed(CommunicationProtocol.Tcp.ToString()); var messageListener = provider.Resolve(); return new DefaultServiceHost(async endPoint => @@ -60,5 +67,7 @@ private static void RegisterDefaultProtocol(ContainerBuilder builder) }, serviceExecutor); }).As(); } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.DotNetty/DotNettyMessageSender.cs b/src/Surging.Core/Surging.Core.DotNetty/DotNettyMessageSender.cs index 731e8c7b5..c988381c7 100644 --- a/src/Surging.Core/Surging.Core.DotNetty/DotNettyMessageSender.cs +++ b/src/Surging.Core/Surging.Core.DotNetty/DotNettyMessageSender.cs @@ -8,41 +8,39 @@ namespace Surging.Core.DotNetty { - /// - /// 基于DotNetty的消息发送者基类。 - /// - public abstract class DotNettyMessageSender - { - private readonly ITransportMessageEncoder _transportMessageEncoder; - - protected DotNettyMessageSender(ITransportMessageEncoder transportMessageEncoder) - { - _transportMessageEncoder = transportMessageEncoder; - } - - protected IByteBuffer GetByteBuffer(TransportMessage message) - { - var data = _transportMessageEncoder.Encode(message); - //var buffer = PooledByteBufferAllocator.Default.Buffer(); - return Unpooled.WrappedBuffer(data); - } - } - /// /// 基于DotNetty客户端的消息发送者。 /// public class DotNettyMessageClientSender : DotNettyMessageSender, IMessageSender, IDisposable { + #region 字段 + + /// + /// Defines the _channel + /// private readonly IChannel _channel; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The transportMessageEncoder + /// The channel public DotNettyMessageClientSender(ITransportMessageEncoder transportMessageEncoder, IChannel channel) : base(transportMessageEncoder) { _channel = channel; } - #region Implementation of IDisposable + #endregion 构造函数 + + #region 方法 - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + /// The Dispose + /// public void Dispose() { Task.Run(async () => @@ -51,33 +49,73 @@ public void Dispose() }).Wait(); } - #endregion Implementation of IDisposable - - #region Implementation of IMessageSender - /// - /// 发送消息。 + /// 发送消息并清空缓冲区。 /// /// 消息内容。 /// 一个任务。 - public async Task SendAsync(TransportMessage message) + public async Task SendAndFlushAsync(TransportMessage message) { var buffer = GetByteBuffer(message); await _channel.WriteAndFlushAsync(buffer); } /// - /// 发送消息并清空缓冲区。 + /// 发送消息。 /// /// 消息内容。 /// 一个任务。 - public async Task SendAndFlushAsync(TransportMessage message) + public async Task SendAsync(TransportMessage message) { var buffer = GetByteBuffer(message); await _channel.WriteAndFlushAsync(buffer); } - #endregion Implementation of IMessageSender + #endregion 方法 + } + + /// + /// 基于DotNetty的消息发送者基类。 + /// + public abstract class DotNettyMessageSender + { + #region 字段 + + /// + /// Defines the _transportMessageEncoder + /// + private readonly ITransportMessageEncoder _transportMessageEncoder; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The transportMessageEncoder + protected DotNettyMessageSender(ITransportMessageEncoder transportMessageEncoder) + { + _transportMessageEncoder = transportMessageEncoder; + } + + #endregion 构造函数 + + #region 方法 + + /// + /// The GetByteBuffer + /// + /// The message + /// The + protected IByteBuffer GetByteBuffer(TransportMessage message) + { + var data = _transportMessageEncoder.Encode(message); + //var buffer = PooledByteBufferAllocator.Default.Buffer(); + return Unpooled.WrappedBuffer(data); + } + + #endregion 方法 } /// @@ -85,37 +123,53 @@ public async Task SendAndFlushAsync(TransportMessage message) /// public class DotNettyServerMessageSender : DotNettyMessageSender, IMessageSender { + #region 字段 + + /// + /// Defines the _context + /// private readonly IChannelHandlerContext _context; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The transportMessageEncoder + /// The context public DotNettyServerMessageSender(ITransportMessageEncoder transportMessageEncoder, IChannelHandlerContext context) : base(transportMessageEncoder) { _context = context; } - #region Implementation of IMessageSender + #endregion 构造函数 + + #region 方法 /// - /// 发送消息。 + /// 发送消息并清空缓冲区。 /// /// 消息内容。 /// 一个任务。 - public async Task SendAsync(TransportMessage message) + public async Task SendAndFlushAsync(TransportMessage message) { var buffer = GetByteBuffer(message); - await _context.WriteAsync(buffer); + await _context.WriteAndFlushAsync(buffer); } /// - /// 发送消息并清空缓冲区。 + /// 发送消息。 /// /// 消息内容。 /// 一个任务。 - public async Task SendAndFlushAsync(TransportMessage message) + public async Task SendAsync(TransportMessage message) { var buffer = GetByteBuffer(message); - await _context.WriteAndFlushAsync(buffer); + await _context.WriteAsync(buffer); } - #endregion Implementation of IMessageSender + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.DotNetty/DotNettyModule.cs b/src/Surging.Core/Surging.Core.DotNetty/DotNettyModule.cs index dd9b00d5f..79de056c5 100644 --- a/src/Surging.Core/Surging.Core.DotNetty/DotNettyModule.cs +++ b/src/Surging.Core/Surging.Core.DotNetty/DotNettyModule.cs @@ -10,8 +10,17 @@ namespace Surging.Core.DotNetty { + /// + /// Defines the + /// public class DotNettyModule : EnginePartModule { + #region 方法 + + /// + /// The Initialize + /// + /// The context public override void Initialize(AppModuleContext context) { base.Initialize(context); @@ -41,6 +50,10 @@ protected override void RegisterBuilder(ContainerBuilderWrapper builder) } } + /// + /// The RegisterDefaultProtocol + /// + /// The builder private void RegisterDefaultProtocol(ContainerBuilderWrapper builder) { builder.Register(provider => @@ -59,5 +72,7 @@ private void RegisterDefaultProtocol(ContainerBuilderWrapper builder) }, serviceExecutor); }).As(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.DotNetty/DotNettyServerMessageListener.cs b/src/Surging.Core/Surging.Core.DotNetty/DotNettyServerMessageListener.cs index 6e3d94ef4..cde3d8a60 100644 --- a/src/Surging.Core/Surging.Core.DotNetty/DotNettyServerMessageListener.cs +++ b/src/Surging.Core/Surging.Core.DotNetty/DotNettyServerMessageListener.cs @@ -16,19 +16,42 @@ namespace Surging.Core.DotNetty { + /// + /// Defines the + /// public class DotNettyServerMessageListener : IMessageListener, IDisposable { - #region Field + #region 字段 + /// + /// Defines the _logger + /// private readonly ILogger _logger; + + /// + /// Defines the _transportMessageDecoder + /// private readonly ITransportMessageDecoder _transportMessageDecoder; + + /// + /// Defines the _transportMessageEncoder + /// private readonly ITransportMessageEncoder _transportMessageEncoder; + + /// + /// Defines the _channel + /// private IChannel _channel; - #endregion Field + #endregion 字段 - #region Constructor + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The logger + /// The codecFactory public DotNettyServerMessageListener(ILogger logger, ITransportMessageCodecFactory codecFactory) { _logger = logger; @@ -36,12 +59,42 @@ public DotNettyServerMessageListener(ILogger logg _transportMessageDecoder = codecFactory.GetDecoder(); } - #endregion Constructor + #endregion 构造函数 - #region Implementation of IMessageListener + #region 事件 + /// + /// Defines the Received + /// public event ReceivedDelegate Received; + #endregion 事件 + + #region 方法 + + /// + /// The CloseAsync + /// + public void CloseAsync() + { + Task.Run(async () => + { + await _channel.EventLoop.ShutdownGracefullyAsync(); + await _channel.CloseAsync(); + }).Wait(); + } + + /// + /// The Dispose + /// + public void Dispose() + { + Task.Run(async () => + { + await _channel.DisconnectAsync(); + }).Wait(); + } + /// /// 触发接收到消息事件。 /// @@ -55,8 +108,11 @@ public async Task OnReceived(IMessageSender sender, TransportMessage message) await Received(sender, message); } - #endregion Implementation of IMessageListener - + /// + /// The StartAsync + /// + /// The endPoint + /// The public async Task StartAsync(EndPoint endPoint) { if (_logger.IsEnabled(LogLevel.Debug)) @@ -65,7 +121,7 @@ public async Task StartAsync(EndPoint endPoint) IEventLoopGroup bossGroup = new MultithreadEventLoopGroup(1); IEventLoopGroup workerGroup = new MultithreadEventLoopGroup();//Default eventLoopCount is Environment.ProcessorCount * 2 var bootstrap = new ServerBootstrap(); - + if (AppConfig.ServerOptions.Libuv) { var dispatcher = new DispatcherEventLoopGroup(); @@ -78,7 +134,7 @@ public async Task StartAsync(EndPoint endPoint) bossGroup = new MultithreadEventLoopGroup(1); workerGroup = new MultithreadEventLoopGroup(); bootstrap.Channel(); - } + } bootstrap .Option(ChannelOption.SoBacklog, AppConfig.ServerOptions.SoBacklog) .ChildOption(ChannelOption.Allocator, PooledByteBufferAllocator.Default) @@ -107,43 +163,49 @@ public async Task StartAsync(EndPoint endPoint) } } - public void CloseAsync() - { - Task.Run(async () => - { - await _channel.EventLoop.ShutdownGracefullyAsync(); - await _channel.CloseAsync(); - }).Wait(); - } - - #region Implementation of IDisposable + #endregion 方法 - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - public void Dispose() + /// + /// Defines the + /// + private class ServerHandler : ChannelHandlerAdapter { - Task.Run(async () => - { - await _channel.DisconnectAsync(); - }).Wait(); - } - - #endregion Implementation of IDisposable + #region 字段 - #region Help Class + /// + /// Defines the _logger + /// + private readonly ILogger _logger; - private class ServerHandler : ChannelHandlerAdapter - { + /// + /// Defines the _readAction + /// private readonly Action _readAction; - private readonly ILogger _logger; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The readAction + /// The logger public ServerHandler(Action readAction, ILogger logger) { _readAction = readAction; _logger = logger; } - #region Overrides of ChannelHandlerAdapter + #endregion 构造函数 + #region 方法 + + /// + /// The ChannelRead + /// + /// The context + /// The message public override void ChannelRead(IChannelHandlerContext context, object message) { Task.Run(() => @@ -153,21 +215,28 @@ public override void ChannelRead(IChannelHandlerContext context, object message) }); } + /// + /// The ChannelReadComplete + /// + /// The context public override void ChannelReadComplete(IChannelHandlerContext context) { context.Flush(); } + /// + /// The ExceptionCaught + /// + /// The context + /// The exception public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) { context.CloseAsync();//客户端主动断开需要应答,否则socket变成CLOSE_WAIT状态导致socket资源耗尽 if (_logger.IsEnabled(LogLevel.Error)) - _logger.LogError(exception,$"与服务器:{context.Channel.RemoteAddress}通信时发送了错误。"); + _logger.LogError(exception, $"与服务器:{context.Channel.RemoteAddress}通信时发送了错误。"); } - #endregion Overrides of ChannelHandlerAdapter + #endregion 方法 } - - #endregion Help Class } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.DotNetty/DotNettyTransportClientFactory.cs b/src/Surging.Core/Surging.Core.DotNetty/DotNettyTransportClientFactory.cs index 7af6e9aee..61c9db97b 100644 --- a/src/Surging.Core/Surging.Core.DotNetty/DotNettyTransportClientFactory.cs +++ b/src/Surging.Core/Surging.Core.DotNetty/DotNettyTransportClientFactory.cs @@ -30,29 +30,80 @@ namespace Surging.Core.DotNetty /// public class DotNettyTransportClientFactory : ITransportClientFactory, IDisposable { - #region Field + #region 字段 - private readonly ITransportMessageEncoder _transportMessageEncoder; - private readonly ITransportMessageDecoder _transportMessageDecoder; - private readonly ILogger _logger; - private readonly IServiceExecutor _serviceExecutor; - private readonly IHealthCheckService _healthCheckService; - private readonly ConcurrentDictionary>> _clients = new ConcurrentDictionary>>(); - private readonly Bootstrap _bootstrap; + /// + /// Defines the messageListenerKey + /// + private static readonly AttributeKey messageListenerKey = AttributeKey.ValueOf(typeof(DotNettyTransportClientFactory), nameof(IMessageListener)); + /// + /// Defines the messageSenderKey + /// private static readonly AttributeKey messageSenderKey = AttributeKey.ValueOf(typeof(DotNettyTransportClientFactory), nameof(IMessageSender)); - private static readonly AttributeKey messageListenerKey = AttributeKey.ValueOf(typeof(DotNettyTransportClientFactory), nameof(IMessageListener)); + + /// + /// Defines the origEndPointKey + /// private static readonly AttributeKey origEndPointKey = AttributeKey.ValueOf(typeof(DotNettyTransportClientFactory), nameof(EndPoint)); - #endregion Field + /// + /// Defines the _bootstrap + /// + private readonly Bootstrap _bootstrap; + + /// + /// Defines the _clients + /// + private readonly ConcurrentDictionary>> _clients = new ConcurrentDictionary>>(); + + /// + /// Defines the _healthCheckService + /// + private readonly IHealthCheckService _healthCheckService; - #region Constructor + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + /// + /// Defines the _serviceExecutor + /// + private readonly IServiceExecutor _serviceExecutor; + + /// + /// Defines the _transportMessageDecoder + /// + private readonly ITransportMessageDecoder _transportMessageDecoder; + + /// + /// Defines the _transportMessageEncoder + /// + private readonly ITransportMessageEncoder _transportMessageEncoder; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The codecFactory + /// The healthCheckService + /// The logger public DotNettyTransportClientFactory(ITransportMessageCodecFactory codecFactory, IHealthCheckService healthCheckService, ILogger logger) : this(codecFactory, healthCheckService, logger, null) { } + /// + /// Initializes a new instance of the class. + /// + /// The codecFactory + /// The healthCheckService + /// The logger + /// The serviceExecutor public DotNettyTransportClientFactory(ITransportMessageCodecFactory codecFactory, IHealthCheckService healthCheckService, ILogger logger, IServiceExecutor serviceExecutor) { _transportMessageEncoder = codecFactory.GetEncoder(); @@ -71,9 +122,9 @@ public DotNettyTransportClientFactory(ITransportMessageCodecFactory codecFactory })); } - #endregion Constructor + #endregion 构造函数 - #region Implementation of ITransportClientFactory + #region 方法 /// /// 创建客户端。 @@ -120,11 +171,9 @@ public async Task CreateClientAsync(EndPoint endPoint) } } - #endregion Implementation of ITransportClientFactory - - #region Implementation of IDisposable - - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + /// The Dispose + /// public void Dispose() { foreach (var client in _clients.Values.Where(i => i.IsValueCreated)) @@ -133,12 +182,14 @@ public void Dispose() } } - #endregion Implementation of IDisposable - + /// + /// The GetBootstrap + /// + /// The private static Bootstrap GetBootstrap() { IEventLoopGroup group; - + var bootstrap = new Bootstrap(); if (AppConfig.ServerOptions.Libuv) { @@ -159,22 +210,51 @@ private static Bootstrap GetBootstrap() return bootstrap; } + #endregion 方法 + + /// + /// Defines the + /// protected class DefaultChannelHandler : ChannelHandlerAdapter { + #region 字段 + + /// + /// Defines the _factory + /// private readonly DotNettyTransportClientFactory _factory; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The factory public DefaultChannelHandler(DotNettyTransportClientFactory factory) { this._factory = factory; } - #region Overrides of ChannelHandlerAdapter + #endregion 构造函数 + + #region 方法 + /// + /// The ChannelInactive + /// + /// The context public override void ChannelInactive(IChannelHandlerContext context) { _factory._clients.TryRemove(context.Channel.GetAttribute(origEndPointKey).Get(), out var value); } + /// + /// The ChannelRead + /// + /// The context + /// The message public override void ChannelRead(IChannelHandlerContext context, object message) { var transportMessage = message as TransportMessage; @@ -184,7 +264,7 @@ public override void ChannelRead(IChannelHandlerContext context, object message) messageListener.OnReceived(messageSender, transportMessage); } - #endregion Overrides of ChannelHandlerAdapter + #endregion 方法 } } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusKafka/Appconfig.cs b/src/Surging.Core/Surging.Core.EventBusKafka/Appconfig.cs index 8b6a6fcee..7c3a20109 100644 --- a/src/Surging.Core/Surging.Core.EventBusKafka/Appconfig.cs +++ b/src/Surging.Core/Surging.Core.EventBusKafka/Appconfig.cs @@ -7,18 +7,35 @@ namespace Surging.Core.EventBusKafka { - public class AppConfig + /// + /// Defines the + /// + public class AppConfig { + #region 字段 - public static IConfigurationRoot Configuration { get; set; } + /// + /// Defines the _kafkaConsumerConfig + /// + private static IEnumerable> _kafkaConsumerConfig; - public static KafkaOptions Options { get; internal set; } + /// + /// Defines the _kafkaProducerConfig + /// + private static IEnumerable> _kafkaProducerConfig; - private static IEnumerable> _kafkaConsumerConfig; + #endregion 字段 + #region 属性 - private static IEnumerable> _kafkaProducerConfig; + /// + /// Gets or sets the Configuration + /// + public static IConfigurationRoot Configuration { get; set; } + /// + /// Gets or sets the KafkaConsumerConfig + /// public static IEnumerable> KafkaConsumerConfig { get @@ -31,6 +48,9 @@ internal set } } + /// + /// Gets or sets the KafkaProducerConfig + /// public static IEnumerable> KafkaProducerConfig { get @@ -42,5 +62,12 @@ internal set _kafkaProducerConfig = value; } } + + /// + /// Gets or sets the Options + /// + public static KafkaOptions Options { get; internal set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusKafka/Configurations/EventBusConfigurationExtensions.cs b/src/Surging.Core/Surging.Core.EventBusKafka/Configurations/EventBusConfigurationExtensions.cs index 93dda75d2..77962cd3f 100644 --- a/src/Surging.Core/Surging.Core.EventBusKafka/Configurations/EventBusConfigurationExtensions.cs +++ b/src/Surging.Core/Surging.Core.EventBusKafka/Configurations/EventBusConfigurationExtensions.cs @@ -8,29 +8,24 @@ namespace Surging.Core.EventBusKafka.Configurations { + /// + /// Defines the + /// public static class EventBusConfigurationExtensions { - public static IConfigurationBuilder AddEventBusFile(this IConfigurationBuilder builder, string path) - { - return AddEventBusFile(builder, provider: null, path: path, basePath: null, optional: false, reloadOnChange: false); - } + #region 方法 - public static IConfigurationBuilder AddEventBusFile(this IConfigurationBuilder builder, string path, bool optional) - { - return AddEventBusFile(builder, provider: null, path: path, basePath: null, optional: optional, reloadOnChange: false); - } - - public static IConfigurationBuilder AddEventBusFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange) - { - return AddEventBusFile(builder, provider: null, path: path, basePath:null, optional: optional, reloadOnChange: reloadOnChange); - } - - public static IConfigurationBuilder AddEventBusFile(this IConfigurationBuilder builder, string path,string basePath, bool optional, bool reloadOnChange) - { - return AddEventBusFile(builder, provider: null, path: path, basePath:basePath, optional: optional, reloadOnChange: reloadOnChange); - } - - public static IConfigurationBuilder AddEventBusFile(this IConfigurationBuilder builder, IFileProvider provider, string path,string basePath, bool optional, bool reloadOnChange) + /// + /// The AddEventBusFile + /// + /// The builder + /// The provider + /// The path + /// The basePath + /// The optional + /// The reloadOnChange + /// The + public static IConfigurationBuilder AddEventBusFile(this IConfigurationBuilder builder, IFileProvider provider, string path, string basePath, bool optional, bool reloadOnChange) { Check.NotNull(builder, "builder"); Check.CheckCondition(() => string.IsNullOrEmpty(path), "path"); @@ -39,7 +34,7 @@ public static IConfigurationBuilder AddEventBusFile(this IConfigurationBuilder b { provider = new PhysicalFileProvider(Path.GetDirectoryName(path)); path = Path.GetFileName(path); - } + } var source = new EventBusConfigurationSource { FileProvider = provider, @@ -53,5 +48,57 @@ public static IConfigurationBuilder AddEventBusFile(this IConfigurationBuilder b AppConfig.Configuration = builder.Build(); return builder; } + + /// + /// The AddEventBusFile + /// + /// The builder + /// The path + /// The + public static IConfigurationBuilder AddEventBusFile(this IConfigurationBuilder builder, string path) + { + return AddEventBusFile(builder, provider: null, path: path, basePath: null, optional: false, reloadOnChange: false); + } + + /// + /// The AddEventBusFile + /// + /// The builder + /// The path + /// The optional + /// The + public static IConfigurationBuilder AddEventBusFile(this IConfigurationBuilder builder, string path, bool optional) + { + return AddEventBusFile(builder, provider: null, path: path, basePath: null, optional: optional, reloadOnChange: false); + } + + /// + /// The AddEventBusFile + /// + /// The builder + /// The path + /// The optional + /// The reloadOnChange + /// The + public static IConfigurationBuilder AddEventBusFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange) + { + return AddEventBusFile(builder, provider: null, path: path, basePath: null, optional: optional, reloadOnChange: reloadOnChange); + } + + /// + /// The AddEventBusFile + /// + /// The builder + /// The path + /// The basePath + /// The optional + /// The reloadOnChange + /// The + public static IConfigurationBuilder AddEventBusFile(this IConfigurationBuilder builder, string path, string basePath, bool optional, bool reloadOnChange) + { + return AddEventBusFile(builder, provider: null, path: path, basePath: basePath, optional: optional, reloadOnChange: reloadOnChange); + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusKafka/Configurations/EventBusConfigurationProvider.cs b/src/Surging.Core/Surging.Core.EventBusKafka/Configurations/EventBusConfigurationProvider.cs index 13f69d4dd..d2e70f1f9 100644 --- a/src/Surging.Core/Surging.Core.EventBusKafka/Configurations/EventBusConfigurationProvider.cs +++ b/src/Surging.Core/Surging.Core.EventBusKafka/Configurations/EventBusConfigurationProvider.cs @@ -7,14 +7,35 @@ namespace Surging.Core.EventBusKafka.Configurations { - public class EventBusConfigurationProvider : FileConfigurationProvider + /// + /// Defines the + /// + public class EventBusConfigurationProvider : FileConfigurationProvider { - public EventBusConfigurationProvider(EventBusConfigurationSource source) : base(source) { } + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The source + public EventBusConfigurationProvider(EventBusConfigurationSource source) : base(source) + { + } + + #endregion 构造函数 + + #region 方法 + + /// + /// The Load + /// + /// The stream public override void Load(Stream stream) { var parser = new JsonConfigurationParser(); this.Data = parser.Parse(stream, null); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusKafka/Configurations/EventBusConfigurationSource.cs b/src/Surging.Core/Surging.Core.EventBusKafka/Configurations/EventBusConfigurationSource.cs index a9e2279af..ccf78da53 100644 --- a/src/Surging.Core/Surging.Core.EventBusKafka/Configurations/EventBusConfigurationSource.cs +++ b/src/Surging.Core/Surging.Core.EventBusKafka/Configurations/EventBusConfigurationSource.cs @@ -5,14 +5,33 @@ namespace Surging.Core.EventBusKafka.Configurations { + /// + /// Defines the + /// public class EventBusConfigurationSource : FileConfigurationSource { + #region 属性 + + /// + /// Gets or sets the ConfigurationKeyPrefix + /// public string ConfigurationKeyPrefix { get; set; } + #endregion 属性 + + #region 方法 + + /// + /// The Build + /// + /// The builder + /// The public override IConfigurationProvider Build(IConfigurationBuilder builder) { FileProvider = FileProvider ?? builder.GetFileProvider(); return new EventBusConfigurationProvider(this); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusKafka/Configurations/KafkaOptions.cs b/src/Surging.Core/Surging.Core.EventBusKafka/Configurations/KafkaOptions.cs index 5f1e0034b..476f8e0ca 100644 --- a/src/Surging.Core/Surging.Core.EventBusKafka/Configurations/KafkaOptions.cs +++ b/src/Surging.Core/Surging.Core.EventBusKafka/Configurations/KafkaOptions.cs @@ -5,36 +5,91 @@ namespace Surging.Core.EventBusKafka.Configurations { + /// + /// Defines the + /// public class KafkaOptions { - public string Servers { get; set; } = "localhost:9092"; + #region 属性 - public int MaxQueueBuffering { get; set; } = 10; + /// + /// Gets or sets the Acks + /// + public string Acks { get; set; } = "all"; - public int MaxSocketBlocking { get; set; } = 10; + /// + /// Gets or sets the CommitInterval + /// + public int CommitInterval { get; set; } = 1000; + /// + /// Gets or sets a value indicating whether EnableAutoCommit + /// public bool EnableAutoCommit { get; set; } = false; + /// + /// Gets or sets the GroupID + /// + public string GroupID { get; set; } = "suringdemo"; + + /// + /// Gets or sets the Linger + /// + public int Linger { get; set; } = 1; + + /// + /// Gets or sets a value indicating whether LogConnectionClose + /// public bool LogConnectionClose { get; set; } = false; - public int Timeout { get; set; } = 100; + /// + /// Gets or sets the MaxQueueBuffering + /// + public int MaxQueueBuffering { get; set; } = 10; - public int CommitInterval { get; set; } = 1000; + /// + /// Gets or sets the MaxSocketBlocking + /// + public int MaxSocketBlocking { get; set; } = 10; + /// + /// Gets or sets the OffsetReset + /// public OffsetResetMode OffsetReset { get; set; } = OffsetResetMode.Earliest; - public int SessionTimeout { get; set; } = 36000; - - public string Acks { get; set; } = "all"; + /// + /// Gets or sets the Retries + /// + public int Retries { get; set; } - public int Retries { get; set; } + /// + /// Gets or sets the Servers + /// + public string Servers { get; set; } = "localhost:9092"; - public int Linger { get; set; } = 1; + /// + /// Gets or sets the SessionTimeout + /// + public int SessionTimeout { get; set; } = 36000; - public string GroupID { get; set; } = "suringdemo"; + /// + /// Gets or sets the Timeout + /// + public int Timeout { get; set; } = 100; + /// + /// Gets the Value + /// public KafkaOptions Value => this; + #endregion 属性 + + #region 方法 + + /// + /// The GetConsumerConfig + /// + /// The public IEnumerable> GetConsumerConfig() { var configs = new List>(); @@ -50,14 +105,20 @@ public IEnumerable> GetConsumerConfig() return configs; } + /// + /// The GetProducerConfig + /// + /// The public IEnumerable> GetProducerConfig() { var configs = new List>(); configs.Add(new KeyValuePair("bootstrap.servers", Servers)); configs.Add(new KeyValuePair("acks", Acks)); - configs.Add(new KeyValuePair("retries", Retries)); - configs.Add(new KeyValuePair("linger.ms", Linger)); + configs.Add(new KeyValuePair("retries", Retries)); + configs.Add(new KeyValuePair("linger.ms", Linger)); return configs; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusKafka/Configurations/OffsetResetMode.cs b/src/Surging.Core/Surging.Core.EventBusKafka/Configurations/OffsetResetMode.cs index e6297042d..f54f7b2dc 100644 --- a/src/Surging.Core/Surging.Core.EventBusKafka/Configurations/OffsetResetMode.cs +++ b/src/Surging.Core/Surging.Core.EventBusKafka/Configurations/OffsetResetMode.cs @@ -4,10 +4,28 @@ namespace Surging.Core.EventBusKafka.Configurations { - public enum OffsetResetMode + #region 枚举 + + /// + /// Defines the OffsetResetMode + /// + public enum OffsetResetMode { + /// + /// Defines the Earliest + /// Earliest, + + /// + /// Defines the Latest + /// Latest, + + /// + /// Defines the None + /// None, } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusKafka/ContainerBuilderExtensions.cs b/src/Surging.Core/Surging.Core.EventBusKafka/ContainerBuilderExtensions.cs index 831c4c1ce..d93da0aef 100644 --- a/src/Surging.Core/Surging.Core.EventBusKafka/ContainerBuilderExtensions.cs +++ b/src/Surging.Core/Surging.Core.EventBusKafka/ContainerBuilderExtensions.cs @@ -1,29 +1,62 @@ using Autofac; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Surging.Core.CPlatform; using Surging.Core.CPlatform.EventBus; +using Surging.Core.CPlatform.EventBus.Events; using Surging.Core.CPlatform.EventBus.Implementation; +using Surging.Core.EventBusKafka.Configurations; using Surging.Core.EventBusKafka.Implementation; using System; using System.Collections.Generic; using System.Text; -using Microsoft.Extensions.DependencyInjection; -using Surging.Core.CPlatform.EventBus.Events; -using Microsoft.Extensions.Options; -using Surging.Core.EventBusKafka.Configurations; -using Microsoft.Extensions.Configuration; namespace Surging.Core.EventBusKafka { + /// + /// Defines the + /// public static class ContainerBuilderExtensions { + #region 方法 + + /// + /// The AddKafkaMQAdapt + /// + /// The builder + /// The + public static IServiceBuilder AddKafkaMQAdapt(this IServiceBuilder builder) + { + return builder.UseKafkaMQEventAdapt(provider => + new KafkaSubscriptionAdapt( + provider.GetService(), + provider.GetService>() + ) + ); + } + + /// + /// The UseKafkaMQEventAdapt + /// + /// The builder + /// The adapt + /// The + public static IServiceBuilder UseKafkaMQEventAdapt(this IServiceBuilder builder, Func adapt) + { + var services = builder.Services; + services.RegisterAdapter(adapt); + return builder; + } /// /// 使用KafkaMQ进行传输。 /// /// 服务构建者。 + /// The options /// 服务构建者。 - public static IServiceBuilder UseKafkaMQTransport(this IServiceBuilder builder,Action options) + public static IServiceBuilder UseKafkaMQTransport(this IServiceBuilder builder, Action options) { AppConfig.Options = new KafkaOptions(); var section = CPlatform.AppConfig.GetSection("Kafka"); @@ -45,22 +78,6 @@ public static IServiceBuilder UseKafkaMQTransport(this IServiceBuilder builder,A return builder; } - public static IServiceBuilder UseKafkaMQEventAdapt(this IServiceBuilder builder, Func adapt) - { - var services = builder.Services; - services.RegisterAdapter(adapt); - return builder; - } - - public static IServiceBuilder AddKafkaMQAdapt(this IServiceBuilder builder) - { - return builder.UseKafkaMQEventAdapt(provider => - new KafkaSubscriptionAdapt( - provider.GetService(), - provider.GetService>() - ) - ); - } + #endregion 方法 } -} - +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusKafka/EventBusKafkaModule.cs b/src/Surging.Core/Surging.Core.EventBusKafka/EventBusKafkaModule.cs index 6ab2d7d5c..6dcc9f719 100644 --- a/src/Surging.Core/Surging.Core.EventBusKafka/EventBusKafkaModule.cs +++ b/src/Surging.Core/Surging.Core.EventBusKafka/EventBusKafkaModule.cs @@ -1,21 +1,46 @@ using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Surging.Core.CPlatform; +using Surging.Core.CPlatform.Engines; using Surging.Core.CPlatform.EventBus; using Surging.Core.CPlatform.EventBus.Events; +using Surging.Core.CPlatform.EventBus.Implementation; using Surging.Core.CPlatform.Module; using Surging.Core.EventBusKafka.Configurations; using Surging.Core.EventBusKafka.Implementation; using System; -using System.Collections.Generic; -using Microsoft.Extensions.DependencyInjection; -using Surging.Core.CPlatform.EventBus.Implementation; -using Surging.Core.CPlatform.Engines; +using System.Collections.Generic; using System.Threading.Tasks; namespace Surging.Core.EventBusKafka { + /// + /// Defines the + /// public class EventBusKafkaModule : EnginePartModule { + #region 方法 + + /// + /// The AddKafkaMQAdapt + /// + /// The builder + /// The + public EventBusKafkaModule AddKafkaMQAdapt(ContainerBuilderWrapper builder) + { + UseKafkaMQEventAdapt(builder, provider => + new KafkaSubscriptionAdapt( + provider.GetService(), + provider.GetService>() + ) + ); + return this; + } + + /// + /// The Initialize + /// + /// The context public override void Initialize(AppModuleContext context) { var serviceProvider = context.ServiceProvoider; @@ -25,20 +50,27 @@ public override void Initialize(AppModuleContext context) { KafkaConsumerPersistentConnection connection = serviceProvider.GetInstances(KafkaConnectionType.Consumer.ToString()) as KafkaConsumerPersistentConnection; connection.Listening(TimeSpan.FromMilliseconds(AppConfig.Options.Timeout)); - }); + }); } /// - /// Inject dependent third-party components + /// The UseKafkaMQEventAdapt /// - /// - protected override void RegisterBuilder(ContainerBuilderWrapper builder) + /// The builder + /// The adapt + /// The + public ContainerBuilderWrapper UseKafkaMQEventAdapt(ContainerBuilderWrapper builder, Func adapt) { - base.RegisterBuilder(builder); - UseKafkaMQTransport(builder).AddKafkaMQAdapt(builder); + builder.RegisterAdapter(adapt); + return builder; } - public EventBusKafkaModule UseKafkaMQTransport(ContainerBuilderWrapper builder) + /// + /// The UseKafkaMQTransport + /// + /// The builder + /// The + public EventBusKafkaModule UseKafkaMQTransport(ContainerBuilderWrapper builder) { AppConfig.Options = new KafkaOptions(); var section = CPlatform.AppConfig.GetSection("EventBus_Kafka"); @@ -58,21 +90,16 @@ public EventBusKafkaModule UseKafkaMQTransport(ContainerBuilderWrapper builder) return this; } - public ContainerBuilderWrapper UseKafkaMQEventAdapt(ContainerBuilderWrapper builder, Func adapt) + /// + /// Inject dependent third-party components + /// + /// + protected override void RegisterBuilder(ContainerBuilderWrapper builder) { - builder.RegisterAdapter(adapt); - return builder; + base.RegisterBuilder(builder); + UseKafkaMQTransport(builder).AddKafkaMQAdapt(builder); } - public EventBusKafkaModule AddKafkaMQAdapt(ContainerBuilderWrapper builder) - { - UseKafkaMQEventAdapt(builder,provider => - new KafkaSubscriptionAdapt( - provider.GetService(), - provider.GetService>() - ) - ); - return this; - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusKafka/IConsumeConfigurator.cs b/src/Surging.Core/Surging.Core.EventBusKafka/IConsumeConfigurator.cs index 1bda6e9dc..3cd172f3a 100644 --- a/src/Surging.Core/Surging.Core.EventBusKafka/IConsumeConfigurator.cs +++ b/src/Surging.Core/Surging.Core.EventBusKafka/IConsumeConfigurator.cs @@ -4,10 +4,29 @@ namespace Surging.Core.EventBusKafka { - public interface IConsumeConfigurator + #region 接口 + + /// + /// Defines the + /// + public interface IConsumeConfigurator { + #region 方法 + + /// + /// The Configure + /// + /// The consumers void Configure(List consumers); + /// + /// The Unconfigure + /// + /// The consumers void Unconfigure(List consumers); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusKafka/IKafkaPersisterConnection.cs b/src/Surging.Core/Surging.Core.EventBusKafka/IKafkaPersisterConnection.cs index bfa5ea2ab..b14d14b73 100644 --- a/src/Surging.Core/Surging.Core.EventBusKafka/IKafkaPersisterConnection.cs +++ b/src/Surging.Core/Surging.Core.EventBusKafka/IKafkaPersisterConnection.cs @@ -5,12 +5,38 @@ namespace Surging.Core.EventBusKafka { - public interface IKafkaPersisterConnection : IDisposable + #region 接口 + + /// + /// Defines the + /// + public interface IKafkaPersisterConnection : IDisposable { + #region 属性 + + /// + /// Gets a value indicating whether IsConnected + /// bool IsConnected { get; } - bool TryConnect(); + #endregion 属性 + + #region 方法 + /// + /// The CreateConnect + /// + /// The Object CreateConnect(); + + /// + /// The TryConnect + /// + /// The + bool TryConnect(); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/DefaultConsumeConfigurator.cs b/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/DefaultConsumeConfigurator.cs index 93e5e1b8f..26f27380b 100644 --- a/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/DefaultConsumeConfigurator.cs +++ b/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/DefaultConsumeConfigurator.cs @@ -10,16 +10,46 @@ namespace Surging.Core.EventBusKafka.Implementation { - public class DefaultConsumeConfigurator : IConsumeConfigurator + /// + /// Defines the + /// + public class DefaultConsumeConfigurator : IConsumeConfigurator { - private readonly IEventBus _eventBus; + #region 字段 + + /// + /// Defines the _container + /// private readonly CPlatformContainer _container; + + /// + /// Defines the _eventBus + /// + private readonly IEventBus _eventBus; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The eventBus + /// The container public DefaultConsumeConfigurator(IEventBus eventBus, CPlatformContainer container) { _eventBus = eventBus; _container = container; } + #endregion 构造函数 + + #region 方法 + + /// + /// The Configure + /// + /// The consumers public void Configure(List consumers) { foreach (var consumer in consumers) @@ -48,6 +78,10 @@ public void Configure(List consumers) } } + /// + /// The Unconfigure + /// + /// The consumers public void Unconfigure(List consumers) { foreach (var consumer in consumers) @@ -76,6 +110,11 @@ public void Unconfigure(List consumers) } } + /// + /// The ConsumerTo + /// + /// + /// protected void ConsumerTo() where TConsumer : IIntegrationEventHandler where TEvent : class @@ -84,12 +123,18 @@ protected void ConsumerTo() (() => (TConsumer)_container.GetInstances(typeof(TConsumer))); } + /// + /// The RemoveConsumer + /// + /// + /// protected void RemoveConsumer() where TConsumer : IIntegrationEventHandler where TEvent : class { _eventBus.Unsubscribe(); } + + #endregion 方法 } -} - +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/EventBusKafka.cs b/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/EventBusKafka.cs index cd8f9e9ed..ca9474033 100644 --- a/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/EventBusKafka.cs +++ b/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/EventBusKafka.cs @@ -15,19 +15,47 @@ namespace Surging.Core.EventBusKafka.Implementation { + /// + /// Defines the + /// public class EventBusKafka : IEventBus, IDisposable { + #region 字段 + + /// + /// Defines the _consumerConnection + /// + private readonly IKafkaPersisterConnection _consumerConnection; + + /// + /// Defines the _logger + /// private readonly ILogger _logger; - private readonly IEventBusSubscriptionsManager _subsManager; + + /// + /// Defines the _producerConnection + /// private readonly IKafkaPersisterConnection _producerConnection; - private readonly IKafkaPersisterConnection _consumerConnection; - public event EventHandler OnShutdown; + /// + /// Defines the _subsManager + /// + private readonly IEventBusSubscriptionsManager _subsManager; + + #endregion 字段 - public EventBusKafka( ILogger logger, + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The logger + /// The subsManager + /// The serviceProvider + public EventBusKafka(ILogger logger, IEventBusSubscriptionsManager subsManager, CPlatformContainer serviceProvider) - { + { this._logger = logger; this._producerConnection = serviceProvider.GetInstances(KafkaConnectionType.Producer.ToString()); this._consumerConnection = serviceProvider.GetInstances(KafkaConnectionType.Consumer.ToString()); @@ -35,29 +63,32 @@ public EventBusKafka( ILogger logger, _subsManager.OnEventRemoved += SubsManager_OnEventRemoved; } - private void SubsManager_OnEventRemoved(object sender, ValueTuple tuple) - { - if (!_consumerConnection.IsConnected) - { - _consumerConnection.TryConnect(); - } + #endregion 构造函数 - using (var channel = _consumerConnection.CreateConnect() as Consumer) - { - channel.Unsubscribe(); - if (_subsManager.IsEmpty) - { - _consumerConnection.Dispose(); - } - } - } + #region 事件 + /// + /// Defines the OnShutdown + /// + public event EventHandler OnShutdown; + + #endregion 事件 + + #region 方法 + + /// + /// The Dispose + /// public void Dispose() { _producerConnection.Dispose(); _consumerConnection.Dispose(); } + /// + /// The Publish + /// + /// The event public void Publish(IntegrationEvent @event) { if (!_producerConnection.IsConnected) @@ -80,6 +111,12 @@ public void Publish(IntegrationEvent @event) }); } + /// + /// The Subscribe + /// + /// + /// + /// The handler public void Subscribe(Func handler) where TH : IIntegrationEventHandler { var eventName = typeof(T).Name; @@ -92,17 +129,33 @@ public void Subscribe(Func handler) where TH : IIntegrationEventHandl } _subsManager.AddSubscription(handler, null); } - + + /// + /// The Unsubscribe + /// + /// + /// public void Unsubscribe() where TH : IIntegrationEventHandler { _subsManager.RemoveSubscription(); } - + + /// + /// The ConsumerClient_OnMessage + /// + /// The sender + /// The e private void ConsumerClient_OnMessage(object sender, Message e) { ProcessEvent(e.Topic, e.Value).Wait(); } - + + /// + /// The ProcessEvent + /// + /// The eventName + /// The message + /// The private async Task ProcessEvent(string eventName, string message) { if (_subsManager.HasSubscriptionsForEvent(eventName)) @@ -125,5 +178,29 @@ private async Task ProcessEvent(string eventName, string message) } } } + + /// + /// The SubsManager_OnEventRemoved + /// + /// The sender + /// The tuple + private void SubsManager_OnEventRemoved(object sender, ValueTuple tuple) + { + if (!_consumerConnection.IsConnected) + { + _consumerConnection.TryConnect(); + } + + using (var channel = _consumerConnection.CreateConnect() as Consumer) + { + channel.Unsubscribe(); + if (_subsManager.IsEmpty) + { + _consumerConnection.Dispose(); + } + } + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/KafkaConnectionType.cs b/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/KafkaConnectionType.cs index 894f0883b..6f0a79446 100644 --- a/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/KafkaConnectionType.cs +++ b/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/KafkaConnectionType.cs @@ -4,9 +4,23 @@ namespace Surging.Core.EventBusKafka.Implementation { - public enum KafkaConnectionType + #region 枚举 + + /// + /// Defines the KafkaConnectionType + /// + public enum KafkaConnectionType { + /// + /// Defines the Producer + /// Producer, + + /// + /// Defines the Consumer + /// Consumer } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/KafkaConsumerPersistentConnection.cs b/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/KafkaConsumerPersistentConnection.cs index e7149674f..0f0340b27 100644 --- a/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/KafkaConsumerPersistentConnection.cs +++ b/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/KafkaConsumerPersistentConnection.cs @@ -10,14 +10,46 @@ namespace Surging.Core.EventBusKafka.Implementation { + /// + /// Defines the + /// public class KafkaConsumerPersistentConnection : KafkaPersistentConnectionBase { + #region 字段 + + /// + /// Defines the _logger + /// private readonly ILogger _logger; - private ConcurrentBag> _consumerClients; - private Consumer _consumerClient; + + /// + /// Defines the _stringDeserializer + /// private readonly IDeserializer _stringDeserializer; - bool _disposed; + /// + /// Defines the _disposed + /// + internal bool _disposed; + + /// + /// Defines the _consumerClient + /// + private Consumer _consumerClient; + + /// + /// Defines the _consumerClients + /// + private ConcurrentBag> _consumerClients; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The logger public KafkaConsumerPersistentConnection(ILogger logger) : base(logger, AppConfig.KafkaConsumerConfig) { @@ -26,8 +58,24 @@ public KafkaConsumerPersistentConnection(ILogger>(); } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets a value indicating whether IsConnected + /// public override bool IsConnected => _consumerClient != null && !_disposed; + #endregion 属性 + + #region 方法 + + /// + /// The Connection + /// + /// The options + /// The public override Action Connection(IEnumerable> options) { return () => @@ -36,10 +84,42 @@ public override Action Connection(IEnumerable> opti _consumerClient.OnConsumeError += OnConsumeError; _consumerClient.OnError += OnConnectionException; _consumerClients.Add(_consumerClient); - }; } + /// + /// The CreateConnect + /// + /// The + public override object CreateConnect() + { + TryConnect(); + return _consumerClient; + } + + /// + /// The Dispose + /// + public override void Dispose() + { + if (_disposed) return; + + _disposed = true; + + try + { + _consumerClient.Dispose(); + } + catch (IOException ex) + { + _logger.LogCritical(ex.ToString()); + } + } + + /// + /// The Listening + /// + /// The timeout public void Listening(TimeSpan timeout) { if (!IsConnected) @@ -50,7 +130,6 @@ public void Listening(TimeSpan timeout) { foreach (var client in _consumerClients) { - client.Poll(timeout); if (!client.Consume(out Message msg, (int)timeout.TotalMilliseconds)) @@ -65,12 +144,25 @@ public void Listening(TimeSpan timeout) } } - public override object CreateConnect() + /// + /// The OnConnectionException + /// + /// The sender + /// The error + private void OnConnectionException(object sender, Error error) { + if (_disposed) return; + + _logger.LogWarning($"A Kafka connection throw exception.info:{error} ,Trying to re-connect..."); + TryConnect(); - return _consumerClient; } + /// + /// The OnConsumeError + /// + /// The sender + /// The e private void OnConsumeError(object sender, Message e) { var message = e.Deserialize(null, _stringDeserializer); @@ -82,29 +174,6 @@ private void OnConsumeError(object sender, Message e) TryConnect(); } - private void OnConnectionException(object sender, Error error) - { - if (_disposed) return; - - _logger.LogWarning($"A Kafka connection throw exception.info:{error} ,Trying to re-connect..."); - - TryConnect(); - } - - public override void Dispose() - { - if (_disposed) return; - - _disposed = true; - - try - { - _consumerClient.Dispose(); - } - catch (IOException ex) - { - _logger.LogCritical(ex.ToString()); - } - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/KafkaPersistentConnectionBase.cs b/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/KafkaPersistentConnectionBase.cs index 5d0a6d25b..25fdbbf47 100644 --- a/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/KafkaPersistentConnectionBase.cs +++ b/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/KafkaPersistentConnectionBase.cs @@ -10,22 +10,79 @@ namespace Surging.Core.EventBusKafka.Implementation { + /// + /// Defines the + /// public abstract class KafkaPersistentConnectionBase : IKafkaPersisterConnection { - private readonly ILogger _logger; + #region 字段 + + /// + /// Defines the _config + /// private readonly IEnumerable> _config; - object sync_root = new object(); + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the sync_root + /// + internal object sync_root = new object(); + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The logger + /// The config public KafkaPersistentConnectionBase(ILogger logger, IEnumerable> config) { this._logger = logger; _config = config; } - - public abstract bool IsConnected { get; } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets a value indicating whether IsConnected + /// + public abstract bool IsConnected { get; } + + #endregion 属性 + #region 方法 + + /// + /// The Connection + /// + /// The options + /// The + public abstract Action Connection(IEnumerable> options); + + /// + /// The CreateConnect + /// + /// The + public abstract object CreateConnect(); + + /// + /// The Dispose + /// + public abstract void Dispose(); + + /// + /// The TryConnect + /// + /// The public bool TryConnect() { _logger.LogInformation("Kafka Client is trying to connect"); @@ -56,9 +113,6 @@ public bool TryConnect() } } - public abstract Action Connection(IEnumerable> options); - - public abstract object CreateConnect(); - public abstract void Dispose(); + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/KafkaProducerPersistentConnection.cs b/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/KafkaProducerPersistentConnection.cs index 33221eeab..cafab591d 100644 --- a/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/KafkaProducerPersistentConnection.cs +++ b/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/KafkaProducerPersistentConnection.cs @@ -1,45 +1,95 @@ -using System; +using Confluent.Kafka; +using Confluent.Kafka.Serialization; +using Microsoft.Extensions.Logging; +using System; using System.Collections.Generic; using System.IO; using System.Text; -using Confluent.Kafka; -using Confluent.Kafka.Serialization; -using Microsoft.Extensions.Logging; namespace Surging.Core.EventBusKafka.Implementation { + /// + /// Defines the + /// public class KafkaProducerPersistentConnection : KafkaPersistentConnectionBase { - private Producer _connection; + #region 字段 + + /// + /// Defines the _logger + /// private readonly ILogger _logger; + + /// + /// Defines the _stringSerializer + /// private readonly ISerializer _stringSerializer; - bool _disposed; + /// + /// Defines the _disposed + /// + internal bool _disposed; + + /// + /// Defines the _connection + /// + private Producer _connection; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The logger public KafkaProducerPersistentConnection(ILogger logger) - :base(logger,AppConfig.KafkaProducerConfig) - { + : base(logger, AppConfig.KafkaProducerConfig) + { _logger = logger; _stringSerializer = new StringSerializer(Encoding.UTF8); } - public override bool IsConnected => _connection != null && !_disposed; - + #endregion 构造函数 + + #region 属性 + + /// + /// Gets a value indicating whether IsConnected + /// + public override bool IsConnected => _connection != null && !_disposed; + #endregion 属性 + + #region 方法 + + /// + /// The Connection + /// + /// The options + /// The public override Action Connection(IEnumerable> options) { return () => - { - _connection = new Producer(options,null, _stringSerializer); - _connection.OnError += OnConnectionException; + { + _connection = new Producer(options, null, _stringSerializer); + _connection.OnError += OnConnectionException; }; } + /// + /// The CreateConnect + /// + /// The public override object CreateConnect() { TryConnect(); return _connection; } + /// + /// The Dispose + /// public override void Dispose() { if (_disposed) return; @@ -56,6 +106,11 @@ public override void Dispose() } } + /// + /// The OnConnectionException + /// + /// The sender + /// The error private void OnConnectionException(object sender, Error error) { if (_disposed) return; @@ -65,6 +120,6 @@ private void OnConnectionException(object sender, Error error) TryConnect(); } - + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/KafkaSubscriptionAdapt.cs b/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/KafkaSubscriptionAdapt.cs index 2b93e42f8..4074eefdd 100644 --- a/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/KafkaSubscriptionAdapt.cs +++ b/src/Surging.Core/Surging.Core.EventBusKafka/Implementation/KafkaSubscriptionAdapt.cs @@ -6,27 +6,62 @@ namespace Surging.Core.EventBusKafka.Implementation { - public class KafkaSubscriptionAdapt : ISubscriptionAdapt + /// + /// Defines the + /// + public class KafkaSubscriptionAdapt : ISubscriptionAdapt { + #region 字段 + + /// + /// Defines the _consumeConfigurator + /// private readonly IConsumeConfigurator _consumeConfigurator; + + /// + /// Defines the _integrationEventHandler + /// private readonly IEnumerable _integrationEventHandler; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The consumeConfigurator + /// The integrationEventHandler public KafkaSubscriptionAdapt(IConsumeConfigurator consumeConfigurator, IEnumerable integrationEventHandler) { this._consumeConfigurator = consumeConfigurator; this._integrationEventHandler = integrationEventHandler; } + #endregion 构造函数 + + #region 方法 + + /// + /// The SubscribeAt + /// public void SubscribeAt() { _consumeConfigurator.Configure(GetQueueConsumers()); } + /// + /// The Unsubscribe + /// public void Unsubscribe() { _consumeConfigurator.Unconfigure(GetQueueConsumers()); } - #region 私有方法 + /// + /// The GetQueueConsumers + /// + /// The private List GetQueueConsumers() { var result = new List(); @@ -37,7 +72,7 @@ private List GetQueueConsumers() } return result; } - #endregion - } -} + #endregion 方法 + } +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusKafka/ServiceHostBuilderExtensions.cs b/src/Surging.Core/Surging.Core.EventBusKafka/ServiceHostBuilderExtensions.cs index cf011c136..6019b2787 100644 --- a/src/Surging.Core/Surging.Core.EventBusKafka/ServiceHostBuilderExtensions.cs +++ b/src/Surging.Core/Surging.Core.EventBusKafka/ServiceHostBuilderExtensions.cs @@ -7,8 +7,18 @@ namespace Surging.Core.EventBusKafka { - public static class ServiceHostBuilderExtensions + /// + /// Defines the + /// + public static class ServiceHostBuilderExtensions { + #region 方法 + + /// + /// The SubscribeAt + /// + /// The hostBuilder + /// The public static IServiceHostBuilder SubscribeAt(this IServiceHostBuilder hostBuilder) { return hostBuilder.MapServices(mapper => @@ -16,5 +26,7 @@ public static IServiceHostBuilder SubscribeAt(this IServiceHostBuilder hostBuild mapper.Resolve().SubscribeAt(); }); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusKafka/Utilities/ExtensionsToFastActivator.cs b/src/Surging.Core/Surging.Core.EventBusKafka/Utilities/ExtensionsToFastActivator.cs index 6ebc0f6c4..8cc400a21 100644 --- a/src/Surging.Core/Surging.Core.EventBusKafka/Utilities/ExtensionsToFastActivator.cs +++ b/src/Surging.Core/Surging.Core.EventBusKafka/Utilities/ExtensionsToFastActivator.cs @@ -5,11 +5,25 @@ namespace Surging.Core.EventBusKafka.Utilities { - public static class ExtensionsToFastActivator + /// + /// Defines the + /// + public static class ExtensionsToFastActivator { + #region 方法 + + /// + /// The FastInvoke + /// + /// + /// The target + /// The genericTypes + /// The expression public static void FastInvoke(this T target, Type[] genericTypes, Expression> expression) { FastInvoker.Current.FastInvoke(target, genericTypes, expression); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusKafka/Utilities/FastInvoker.cs b/src/Surging.Core/Surging.Core.EventBusKafka/Utilities/FastInvoker.cs index 1c46da42e..9cde700a2 100644 --- a/src/Surging.Core/Surging.Core.EventBusKafka/Utilities/FastInvoker.cs +++ b/src/Surging.Core/Surging.Core.EventBusKafka/Utilities/FastInvoker.cs @@ -6,10 +6,27 @@ namespace Surging.Core.EventBusKafka.Utilities { + /// + /// Defines the + /// + /// public class FastInvoker { + #region 字段 + + /// + /// Defines the _current + /// [ThreadStatic] - static FastInvoker _current; + internal static FastInvoker _current; + + #endregion 字段 + + #region 属性 + + /// + /// Gets the Current + /// public static FastInvoker Current { get @@ -20,6 +37,15 @@ public static FastInvoker Current } } + #endregion 属性 + + #region 方法 + + /// + /// The FastInvoke + /// + /// The target + /// The expression public void FastInvoke(T target, Expression> expression) { var call = expression.Body as MethodCallExpression; @@ -29,6 +55,12 @@ public void FastInvoke(T target, Expression> expression) invoker(target); } + /// + /// The FastInvoke + /// + /// The target + /// The genericTypes + /// The expression public void FastInvoke(T target, Type[] genericTypes, Expression> expression) { var call = expression.Body as MethodCallExpression; @@ -45,7 +77,13 @@ public void FastInvoke(T target, Type[] genericTypes, Expression> expr invoker(target); } - MethodInfo GetGenericMethodFromTypes(MethodInfo method, Type[] genericTypes) + /// + /// The GetGenericMethodFromTypes + /// + /// The method + /// The genericTypes + /// The + internal MethodInfo GetGenericMethodFromTypes(MethodInfo method, Type[] genericTypes) { if (!method.IsGenericMethod) throw new ArgumentException("不能为非泛型方法指定泛型类型。: " + method.Name); @@ -59,7 +97,12 @@ MethodInfo GetGenericMethodFromTypes(MethodInfo method, Type[] genericTypes) return method; } - Action GetInvoker(Func getMethodInfo) + /// + /// The GetInvoker + /// + /// The getMethodInfo + /// The + internal Action GetInvoker(Func getMethodInfo) { MethodInfo method = getMethodInfo(); @@ -68,8 +111,8 @@ Action GetInvoker(Func getMethodInfo) MethodCallExpression call = Expression.Call(instanceParameter, method); return Expression.Lambda>(call, new[] { instanceParameter }).Compile(); - } + + #endregion 方法 } -} - +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/AppConfig.cs b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/AppConfig.cs index b10f74ddf..293ef4c93 100644 --- a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/AppConfig.cs +++ b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/AppConfig.cs @@ -5,19 +5,43 @@ namespace Surging.Core.EventBusRabbitMQ { + /// + /// Defines the + /// public static class AppConfig { - public static IConfigurationRoot Configuration { get; set; } - + #region 属性 + /// + /// Gets or sets the BrokerName + /// public static string BrokerName { get; internal set; } - public static ushort PrefetchCount { get; set; } - - public static int RetryCount { get; internal set; } = 3; + /// + /// Gets or sets the Configuration + /// + public static IConfigurationRoot Configuration { get; set; } + /// + /// Gets or sets the FailCount + /// public static int FailCount { get; internal set; } = 3; + /// + /// Gets or sets the MessageTTL + /// public static int MessageTTL { get; internal set; } = 30 * 1000; + + /// + /// Gets or sets the PrefetchCount + /// + public static ushort PrefetchCount { get; set; } + + /// + /// Gets or sets the RetryCount + /// + public static int RetryCount { get; internal set; } = 3; + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Attributes/QueueConsumerAttribute.cs b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Attributes/QueueConsumerAttribute.cs index f002cfb40..0dfb57e4a 100644 --- a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Attributes/QueueConsumerAttribute.cs +++ b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Attributes/QueueConsumerAttribute.cs @@ -5,29 +5,56 @@ namespace Surging.Core.EventBusRabbitMQ.Attributes { + /// + /// Defines the + /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)] public class QueueConsumerAttribute : Attribute { - public string QueueName + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The queueName + /// The modes + public QueueConsumerAttribute(string queueName, params QueueConsumerMode[] modes) { - get { return _queueName; } + _queueName = queueName; + _modes = modes.Any() ? modes : + new QueueConsumerMode[] { QueueConsumerMode.Normal }; } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the Modes + /// public QueueConsumerMode[] Modes { get { return _modes; } } - private string _queueName { get; set; } + /// + /// Gets the QueueName + /// + public string QueueName + { + get { return _queueName; } + } + /// + /// Gets or sets the _modes + /// private QueueConsumerMode[] _modes { get; set; } - public QueueConsumerAttribute(string queueName, params QueueConsumerMode[] modes) - { - _queueName = queueName; - _modes = modes.Any() ? modes : - new QueueConsumerMode[] { QueueConsumerMode.Normal }; - } + /// + /// Gets or sets the _queueName + /// + private string _queueName { get; set; } + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Configurations/EventBusConfigurationExtensions.cs b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Configurations/EventBusConfigurationExtensions.cs index a155a4f42..5e2115079 100644 --- a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Configurations/EventBusConfigurationExtensions.cs +++ b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Configurations/EventBusConfigurationExtensions.cs @@ -8,29 +8,24 @@ namespace Surging.Core.EventBusRabbitMQ.Configurations { + /// + /// Defines the + /// public static class EventBusConfigurationExtensions { - public static IConfigurationBuilder AddEventBusFile(this IConfigurationBuilder builder, string path) - { - return AddEventBusFile(builder, provider: null, path: path, basePath: null, optional: false, reloadOnChange: false); - } - - public static IConfigurationBuilder AddEventBusFile(this IConfigurationBuilder builder, string path, bool optional) - { - return AddEventBusFile(builder, provider: null, path: path, basePath: null, optional: optional, reloadOnChange: false); - } + #region 方法 - public static IConfigurationBuilder AddEventBusFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange) - { - return AddEventBusFile(builder, provider: null, path: path,basePath:null, optional: optional, reloadOnChange: reloadOnChange); - } - - public static IConfigurationBuilder AddEventBusFile(this IConfigurationBuilder builder, string path,string basePath, bool optional, bool reloadOnChange) - { - return AddEventBusFile(builder, provider: null, path: path, basePath:basePath, optional: optional, reloadOnChange: reloadOnChange); - } - - public static IConfigurationBuilder AddEventBusFile(this IConfigurationBuilder builder, IFileProvider provider, string path,string basePath, bool optional, bool reloadOnChange) + /// + /// The AddEventBusFile + /// + /// The builder + /// The provider + /// The path + /// The basePath + /// The optional + /// The reloadOnChange + /// The + public static IConfigurationBuilder AddEventBusFile(this IConfigurationBuilder builder, IFileProvider provider, string path, string basePath, bool optional, bool reloadOnChange) { Check.NotNull(builder, "builder"); Check.CheckCondition(() => string.IsNullOrEmpty(path), "path"); @@ -39,7 +34,7 @@ public static IConfigurationBuilder AddEventBusFile(this IConfigurationBuilder b provider = new PhysicalFileProvider(Path.GetDirectoryName(path)); path = Path.GetFileName(path); } - + var source = new EventBusConfigurationSource { FileProvider = provider, @@ -53,5 +48,57 @@ public static IConfigurationBuilder AddEventBusFile(this IConfigurationBuilder b AppConfig.Configuration = builder.Build(); return builder; } + + /// + /// The AddEventBusFile + /// + /// The builder + /// The path + /// The + public static IConfigurationBuilder AddEventBusFile(this IConfigurationBuilder builder, string path) + { + return AddEventBusFile(builder, provider: null, path: path, basePath: null, optional: false, reloadOnChange: false); + } + + /// + /// The AddEventBusFile + /// + /// The builder + /// The path + /// The optional + /// The + public static IConfigurationBuilder AddEventBusFile(this IConfigurationBuilder builder, string path, bool optional) + { + return AddEventBusFile(builder, provider: null, path: path, basePath: null, optional: optional, reloadOnChange: false); + } + + /// + /// The AddEventBusFile + /// + /// The builder + /// The path + /// The optional + /// The reloadOnChange + /// The + public static IConfigurationBuilder AddEventBusFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange) + { + return AddEventBusFile(builder, provider: null, path: path, basePath: null, optional: optional, reloadOnChange: reloadOnChange); + } + + /// + /// The AddEventBusFile + /// + /// The builder + /// The path + /// The basePath + /// The optional + /// The reloadOnChange + /// The + public static IConfigurationBuilder AddEventBusFile(this IConfigurationBuilder builder, string path, string basePath, bool optional, bool reloadOnChange) + { + return AddEventBusFile(builder, provider: null, path: path, basePath: basePath, optional: optional, reloadOnChange: reloadOnChange); + } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Configurations/EventBusConfigurationProvider.cs b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Configurations/EventBusConfigurationProvider.cs index b5d89f41d..4fbd035a6 100644 --- a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Configurations/EventBusConfigurationProvider.cs +++ b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Configurations/EventBusConfigurationProvider.cs @@ -7,14 +7,35 @@ namespace Surging.Core.EventBusRabbitMQ.Configurations { - public class EventBusConfigurationProvider : FileConfigurationProvider + /// + /// Defines the + /// + public class EventBusConfigurationProvider : FileConfigurationProvider { - public EventBusConfigurationProvider(EventBusConfigurationSource source) : base(source) { } + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The source + public EventBusConfigurationProvider(EventBusConfigurationSource source) : base(source) + { + } + + #endregion 构造函数 + + #region 方法 + + /// + /// The Load + /// + /// The stream public override void Load(Stream stream) { var parser = new JsonConfigurationParser(); this.Data = parser.Parse(stream, null); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Configurations/EventBusConfigurationSource.cs b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Configurations/EventBusConfigurationSource.cs index 2a40c6c5a..15c9d9ae1 100644 --- a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Configurations/EventBusConfigurationSource.cs +++ b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Configurations/EventBusConfigurationSource.cs @@ -5,14 +5,33 @@ namespace Surging.Core.EventBusRabbitMQ.Configurations { + /// + /// Defines the + /// public class EventBusConfigurationSource : FileConfigurationSource { + #region 属性 + + /// + /// Gets or sets the ConfigurationKeyPrefix + /// public string ConfigurationKeyPrefix { get; set; } + #endregion 属性 + + #region 方法 + + /// + /// The Build + /// + /// The builder + /// The public override IConfigurationProvider Build(IConfigurationBuilder builder) { FileProvider = FileProvider ?? builder.GetFileProvider(); return new EventBusConfigurationProvider(this); } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Configurations/EventBusOption.cs b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Configurations/EventBusOption.cs index 7ca210181..02f479473 100644 --- a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Configurations/EventBusOption.cs +++ b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Configurations/EventBusOption.cs @@ -4,26 +4,63 @@ namespace Surging.Core.EventBusRabbitMQ.Configurations { + /// + /// Defines the + /// public class EventBusOption { + #region 属性 + + /// + /// Gets or sets the BrokerName + /// + public string BrokerName { get; set; } = "surging"; + + /// + /// Gets or sets the EventBusConnection + /// public string EventBusConnection { get; set; } = ""; + /// + /// Gets or sets the EventBusPassword + /// + public string EventBusPassword { get; set; } = "guest"; + + /// + /// Gets or sets the EventBusUserName + /// public string EventBusUserName { get; set; } = "guest"; - public string EventBusPassword { get; set; } = "guest"; + /// + /// Gets or sets the FailCount + /// + public int FailCount { get; set; } = 3; - public string VirtualHost { get; set; } = "/"; + /// + /// Gets or sets the MessageTTL + /// + public int MessageTTL { get; set; } = 30 * 1000; + /// + /// Gets or sets the Port + /// public string Port { get; set; } = "5672"; - public string BrokerName { get; set; } = "surging"; + /// + /// Gets or sets the PrefetchCount + /// + public ushort PrefetchCount { get; set; } + /// + /// Gets or sets the RetryCount + /// public int RetryCount { get; set; } = 3; - public int FailCount { get; set; } = 3; - - public ushort PrefetchCount { get; set; } + /// + /// Gets or sets the VirtualHost + /// + public string VirtualHost { get; set; } = "/"; - public int MessageTTL { get; set; } = 30 * 1000; + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/ContainerBuilderExtensions.cs b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/ContainerBuilderExtensions.cs index dce04495f..a6695e123 100644 --- a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/ContainerBuilderExtensions.cs +++ b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/ContainerBuilderExtensions.cs @@ -1,21 +1,53 @@ -using Surging.Core.CPlatform; -using Surging.Core.CPlatform.EventBus; -using System; -using Autofac; -using Surging.Core.EventBusRabbitMQ.Implementation; +using Autofac; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using System.Collections.Generic; -using Surging.Core.CPlatform.EventBus.Events; -using Surging.Core.CPlatform.EventBus.Implementation; using Microsoft.Extensions.Logging; using RabbitMQ.Client; +using Surging.Core.CPlatform; +using Surging.Core.CPlatform.EventBus; +using Surging.Core.CPlatform.EventBus.Events; +using Surging.Core.CPlatform.EventBus.Implementation; using Surging.Core.EventBusRabbitMQ.Configurations; -using Microsoft.Extensions.Configuration; +using Surging.Core.EventBusRabbitMQ.Implementation; +using System; +using System.Collections.Generic; namespace Surging.Core.EventBusRabbitMQ { + /// + /// Defines the + /// public static class ContainerBuilderExtensions { + #region 方法 + + /// + /// The AddRabbitMQAdapt + /// + /// The builder + /// The + public static IServiceBuilder AddRabbitMQAdapt(this IServiceBuilder builder) + { + return builder.UseRabbitMQEventAdapt(provider => + new RabbitMqSubscriptionAdapt( + provider.GetService(), + provider.GetService>() + ) + ); + } + + /// + /// The UseRabbitMQEventAdapt + /// + /// The builder + /// The adapt + /// The + public static IServiceBuilder UseRabbitMQEventAdapt(this IServiceBuilder builder, Func adapt) + { + var services = builder.Services; + services.RegisterAdapter(adapt); + return builder; + } /// /// 使用RabbitMQ进行传输。 @@ -55,21 +87,6 @@ public static IServiceBuilder UseRabbitMQTransport(this IServiceBuilder builder) return builder; } - public static IServiceBuilder UseRabbitMQEventAdapt(this IServiceBuilder builder, Func adapt) - { - var services = builder.Services; - services.RegisterAdapter(adapt); - return builder; - } - - public static IServiceBuilder AddRabbitMQAdapt(this IServiceBuilder builder) - { - return builder.UseRabbitMQEventAdapt(provider => - new RabbitMqSubscriptionAdapt( - provider.GetService(), - provider.GetService>() - ) - ); - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/EventBusRabbitMQModule.cs b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/EventBusRabbitMQModule.cs index b8d2a43ed..f4f9eed58 100644 --- a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/EventBusRabbitMQModule.cs +++ b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/EventBusRabbitMQModule.cs @@ -1,23 +1,32 @@ using Autofac; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using RabbitMQ.Client; using Surging.Core.CPlatform; using Surging.Core.CPlatform.EventBus; using Surging.Core.CPlatform.EventBus.Events; +using Surging.Core.CPlatform.EventBus.Implementation; using Surging.Core.CPlatform.Module; +using Surging.Core.CPlatform.Routing; using Surging.Core.EventBusRabbitMQ.Configurations; using Surging.Core.EventBusRabbitMQ.Implementation; using System; using System.Collections.Generic; -using Microsoft.Extensions.DependencyInjection; -using Surging.Core.CPlatform.EventBus.Implementation; -using Surging.Core.CPlatform.Routing; namespace Surging.Core.EventBusRabbitMQ { + /// + /// Defines the + /// public class EventBusRabbitMQModule : EnginePartModule { + #region 方法 + + /// + /// The Initialize + /// + /// The context public override void Initialize(AppModuleContext context) { var serviceProvider = context.ServiceProvoider; @@ -34,16 +43,10 @@ public override void Initialize(AppModuleContext context) } /// - /// Inject dependent third-party components + /// The UseRabbitMQTransport /// - /// - protected override void RegisterBuilder(ContainerBuilderWrapper builder) - { - base.RegisterBuilder(builder); - UseRabbitMQTransport(builder) - .AddRabbitMQAdapt(builder); - } - + /// The builder + /// The public EventBusRabbitMQModule UseRabbitMQTransport(ContainerBuilderWrapper builder) { builder.RegisterType(typeof(Implementation.EventBusRabbitMQ)).As(typeof(IEventBus)).SingleInstance(); @@ -77,12 +80,22 @@ public EventBusRabbitMQModule UseRabbitMQTransport(ContainerBuilderWrapper build return this; } - private ContainerBuilderWrapper UseRabbitMQEventAdapt(ContainerBuilderWrapper builder, Func adapt) + /// + /// Inject dependent third-party components + /// + /// + protected override void RegisterBuilder(ContainerBuilderWrapper builder) { - builder.RegisterAdapter(adapt); - return builder; + base.RegisterBuilder(builder); + UseRabbitMQTransport(builder) + .AddRabbitMQAdapt(builder); } + /// + /// The AddRabbitMQAdapt + /// + /// The builder + /// The private EventBusRabbitMQModule AddRabbitMQAdapt(ContainerBuilderWrapper builder) { UseRabbitMQEventAdapt(builder, provider => @@ -93,5 +106,19 @@ private EventBusRabbitMQModule AddRabbitMQAdapt(ContainerBuilderWrapper builder) ); return this; } + + /// + /// The UseRabbitMQEventAdapt + /// + /// The builder + /// The adapt + /// The + private ContainerBuilderWrapper UseRabbitMQEventAdapt(ContainerBuilderWrapper builder, Func adapt) + { + builder.RegisterAdapter(adapt); + return builder; + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/IConsumeConfigurator.cs b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/IConsumeConfigurator.cs index 451299996..421946bd0 100644 --- a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/IConsumeConfigurator.cs +++ b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/IConsumeConfigurator.cs @@ -4,10 +4,29 @@ namespace Surging.Core.EventBusRabbitMQ { - public interface IConsumeConfigurator + #region 接口 + + /// + /// Defines the + /// + public interface IConsumeConfigurator { + #region 方法 + + /// + /// The Configure + /// + /// The consumers void Configure(List consumers); + /// + /// The Unconfigure + /// + /// The consumers void Unconfigure(List consumers); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/IRabbitMQPersisterConnection.cs b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/IRabbitMQPersisterConnection.cs index 1b32ccf92..545310a0f 100644 --- a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/IRabbitMQPersisterConnection.cs +++ b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/IRabbitMQPersisterConnection.cs @@ -5,15 +5,48 @@ namespace Surging.Core.EventBusRabbitMQ { + #region 接口 + + /// + /// Defines the + /// public interface IRabbitMQPersistentConnection : IDisposable { + #region 事件 + + /// + /// Defines the OnRabbitConnectionShutdown + /// + event EventHandler OnRabbitConnectionShutdown; + + #endregion 事件 + + #region 属性 + + /// + /// Gets a value indicating whether IsConnected + /// bool IsConnected { get; } - bool TryConnect(); + #endregion 属性 + + #region 方法 + /// + /// The CreateModel + /// + /// The IModel CreateModel(); - event EventHandler OnRabbitConnectionShutdown; + /// + /// The TryConnect + /// + /// The + bool TryConnect(); + + #endregion 方法 } + + #endregion 接口 } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Implementation/DefaultConsumeConfigurator.cs b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Implementation/DefaultConsumeConfigurator.cs index 485b3cb4b..6e4895af1 100644 --- a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Implementation/DefaultConsumeConfigurator.cs +++ b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Implementation/DefaultConsumeConfigurator.cs @@ -1,24 +1,54 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Linq; +using Surging.Core.CPlatform; using Surging.Core.CPlatform.EventBus.Events; using Surging.Core.CPlatform.EventBus.Implementation; -using Surging.Core.CPlatform; using Surging.Core.EventBusRabbitMQ.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; namespace Surging.Core.EventBusRabbitMQ.Implementation { - public class DefaultConsumeConfigurator: IConsumeConfigurator + /// + /// Defines the + /// + public class DefaultConsumeConfigurator : IConsumeConfigurator { - private readonly IEventBus _eventBus; + #region 字段 + + /// + /// Defines the _container + /// private readonly CPlatformContainer _container; + + /// + /// Defines the _eventBus + /// + private readonly IEventBus _eventBus; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The eventBus + /// The container public DefaultConsumeConfigurator(IEventBus eventBus, CPlatformContainer container) { _eventBus = eventBus; _container = container; } - + + #endregion 构造函数 + + #region 方法 + + /// + /// The Configure + /// + /// The consumers public void Configure(List consumers) { foreach (var consumer in consumers) @@ -47,6 +77,10 @@ public void Configure(List consumers) } } + /// + /// The Unconfigure + /// + /// The consumers public void Unconfigure(List consumers) { foreach (var consumer in consumers) @@ -75,7 +109,12 @@ public void Unconfigure(List consumers) } } - protected void ConsumerTo() + /// + /// The ConsumerTo + /// + /// + /// + protected void ConsumerTo() where TConsumer : IIntegrationEventHandler where TEvent : class { @@ -83,11 +122,18 @@ protected void ConsumerTo() (() => (TConsumer)_container.GetInstances(typeof(TConsumer))); } + /// + /// The RemoveConsumer + /// + /// + /// protected void RemoveConsumer() where TConsumer : IIntegrationEventHandler where TEvent : class { _eventBus.Unsubscribe(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Implementation/DefaultRabbitMQPersisterConnection.cs b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Implementation/DefaultRabbitMQPersisterConnection.cs index b9eb5c86e..0673c74d4 100644 --- a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Implementation/DefaultRabbitMQPersisterConnection.cs +++ b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Implementation/DefaultRabbitMQPersisterConnection.cs @@ -13,18 +13,48 @@ namespace Surging.Core.EventBusRabbitMQ.Implementation { - public class DefaultRabbitMQPersistentConnection + /// + /// Defines the + /// + public class DefaultRabbitMQPersistentConnection : IRabbitMQPersistentConnection { + #region 字段 + + /// + /// Defines the _connectionFactory + /// private readonly IConnectionFactory _connectionFactory; + + /// + /// Defines the _logger + /// private readonly ILogger _logger; - IConnection _connection; - bool _disposed; - object sync_root = new object(); + /// + /// Defines the _connection + /// + internal IConnection _connection; - public event EventHandler OnRabbitConnectionShutdown; + /// + /// Defines the _disposed + /// + internal bool _disposed; + + /// + /// Defines the sync_root + /// + internal object sync_root = new object(); + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The connectionFactory + /// The logger public DefaultRabbitMQPersistentConnection(IConnectionFactory connectionFactory, ILogger logger) { @@ -32,6 +62,22 @@ public DefaultRabbitMQPersistentConnection(IConnectionFactory connectionFactory, _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } + #endregion 构造函数 + + #region 事件 + + /// + /// Defines the OnRabbitConnectionShutdown + /// + public event EventHandler OnRabbitConnectionShutdown; + + #endregion 事件 + + #region 属性 + + /// + /// Gets a value indicating whether IsConnected + /// public bool IsConnected { get @@ -40,6 +86,14 @@ public bool IsConnected } } + #endregion 属性 + + #region 方法 + + /// + /// The CreateModel + /// + /// The public IModel CreateModel() { if (!IsConnected) @@ -50,6 +104,9 @@ public IModel CreateModel() return _connection.CreateModel(); } + /// + /// The Dispose + /// public void Dispose() { if (_disposed) return; @@ -66,6 +123,10 @@ public void Dispose() } } + /// + /// The TryConnect + /// + /// The public bool TryConnect() { _logger.LogInformation("RabbitMQ Client is trying to connect"); @@ -84,7 +145,6 @@ public bool TryConnect() { _connection = _connectionFactory .CreateConnection(); - }); if (IsConnected) @@ -105,31 +165,48 @@ public bool TryConnect() } } - private void OnConnectionBlocked(object sender, ConnectionBlockedEventArgs e) + /// + /// The OnCallbackException + /// + /// The sender + /// The e + internal void OnCallbackException(object sender, CallbackExceptionEventArgs e) { if (_disposed) return; - _logger.LogWarning("A RabbitMQ connection is shutdown. Trying to re-connect..."); + _logger.LogWarning("A RabbitMQ connection throw exception. Trying to re-connect..."); TryConnect(); } - void OnCallbackException(object sender, CallbackExceptionEventArgs e) + /// + /// The OnConnectionShutdown + /// + /// The sender + /// The reason + internal void OnConnectionShutdown(object sender, ShutdownEventArgs reason) { if (_disposed) return; - _logger.LogWarning("A RabbitMQ connection throw exception. Trying to re-connect..."); - + _logger.LogWarning("A RabbitMQ connection is on shutdown. Trying to re-connect..."); + OnRabbitConnectionShutdown(sender, reason); TryConnect(); } - void OnConnectionShutdown(object sender, ShutdownEventArgs reason) + /// + /// The OnConnectionBlocked + /// + /// The sender + /// The e + private void OnConnectionBlocked(object sender, ConnectionBlockedEventArgs e) { if (_disposed) return; - _logger.LogWarning("A RabbitMQ connection is on shutdown. Trying to re-connect..."); - OnRabbitConnectionShutdown(sender, reason); + _logger.LogWarning("A RabbitMQ connection is shutdown. Trying to re-connect..."); + TryConnect(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Implementation/EventBusRabbitMQ.cs b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Implementation/EventBusRabbitMQ.cs index 20aa268bc..1716751ee 100644 --- a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Implementation/EventBusRabbitMQ.cs +++ b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Implementation/EventBusRabbitMQ.cs @@ -23,22 +23,73 @@ namespace Surging.Core.EventBusRabbitMQ.Implementation { + /// + /// Defines the + /// public class EventBusRabbitMQ : IEventBus, IDisposable { - private readonly string BROKER_NAME; + #region 字段 + + /// + /// Defines the _exchanges + /// + private readonly IDictionary _exchanges; + + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the _messageTTL + /// private readonly int _messageTTL; + + /// + /// Defines the _persistentConnection + /// + private readonly IRabbitMQPersistentConnection _persistentConnection; + + /// + /// Defines the _prefetchCount + /// + private readonly ushort _prefetchCount; + + /// + /// Defines the _retryCount + /// private readonly int _retryCount; + + /// + /// Defines the _rollbackCount + /// private readonly int _rollbackCount; - private readonly ushort _prefetchCount; - private readonly IRabbitMQPersistentConnection _persistentConnection; - private readonly ILogger _logger; + + /// + /// Defines the _subsManager + /// private readonly IEventBusSubscriptionsManager _subsManager; - private readonly IDictionary _exchanges; - private IDictionary, IModel> _consumerChannels; + /// + /// Defines the BROKER_NAME + /// + private readonly string BROKER_NAME; - public event EventHandler OnShutdown; + /// + /// Defines the _consumerChannels + /// + private IDictionary, IModel> _consumerChannels; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The persistentConnection + /// The logger + /// The subsManager public EventBusRabbitMQ(IRabbitMQPersistentConnection persistentConnection, ILogger logger, IEventBusSubscriptionsManager subsManager) { BROKER_NAME = AppConfig.BrokerName; @@ -46,7 +97,7 @@ public EventBusRabbitMQ(IRabbitMQPersistentConnection persistentConnection, ILog _retryCount = AppConfig.RetryCount; _prefetchCount = AppConfig.PrefetchCount; _rollbackCount = AppConfig.FailCount; - _consumerChannels = new Dictionary, IModel>(); + _consumerChannels = new Dictionary, IModel>(); _exchanges = new Dictionary(); _exchanges.Add(QueueConsumerMode.Normal, BROKER_NAME); _exchanges.Add(QueueConsumerMode.Retry, $"{BROKER_NAME}@{QueueConsumerMode.Retry.ToString()}"); @@ -58,21 +109,38 @@ public EventBusRabbitMQ(IRabbitMQPersistentConnection persistentConnection, ILog _subsManager.OnEventRemoved += SubsManager_OnEventRemoved; } - private void SubsManager_OnEventRemoved(object sender, ValueTuple tuple) - { - if (!_persistentConnection.IsConnected) - { - _persistentConnection.TryConnect(); - } + #endregion 构造函数 - using (var channel = _persistentConnection.CreateModel()) + #region 事件 + + /// + /// Defines the OnShutdown + /// + public event EventHandler OnShutdown; + + #endregion 事件 + + #region 方法 + + /// + /// The Dispose + /// + public void Dispose() + { + foreach (var key in _consumerChannels.Keys) { - channel.QueueUnbind(queue: tuple.Item1, - exchange: BROKER_NAME, - routingKey: tuple.Item2); + if (_consumerChannels[key] != null) + { + _consumerChannels[key].Dispose(); + } } + _subsManager.Clear(); } + /// + /// The Publish + /// + /// The event public void Publish(IntegrationEvent @event) { if (!_persistentConnection.IsConnected) @@ -109,6 +177,12 @@ public void Publish(IntegrationEvent @event) } } + /// + /// The Subscribe + /// + /// + /// + /// The handler public void Subscribe(Func handler) where TH : IIntegrationEventHandler { @@ -147,7 +221,7 @@ public void Subscribe(Func handler) CreateConsumerChannel(queueConsumerAttr, eventName, mode)); channel.QueueBind(queue: queueName, exchange: _exchanges[mode], - routingKey: eventName); + routingKey: eventName); } } } @@ -155,6 +229,11 @@ public void Subscribe(Func handler) _subsManager.AddSubscription(handler, queueConsumerAttr.QueueName); } + /// + /// The Unsubscribe + /// + /// + /// public void Unsubscribe() where TH : IIntegrationEventHandler { @@ -162,6 +241,12 @@ public void Unsubscribe() _subsManager.RemoveSubscription(); } + /// + /// The FindHandlerByType + /// + /// The handlerType + /// The handlers + /// The private static Func FindHandlerByType(Type handlerType, IEnumerable> handlers) { foreach (var func in handlers) @@ -175,18 +260,13 @@ private static Func FindHandlerByType(Type handlerType return null; } - public void Dispose() - { - foreach (var key in _consumerChannels.Keys) - { - if (_consumerChannels[key] != null) - { - _consumerChannels[key].Dispose(); - } - } - _subsManager.Clear(); - } - + /// + /// The CreateConsumerChannel + /// + /// The queueConsumer + /// The routeKey + /// The mode + /// The private IModel CreateConsumerChannel(QueueConsumerAttribute queueConsumer, string routeKey, QueueConsumerMode mode) { IModel result = null; @@ -199,6 +279,7 @@ private IModel CreateConsumerChannel(QueueConsumerAttribute queueConsumer, strin bindConsumer); } break; + case QueueConsumerMode.Retry: { var bindConsumer = queueConsumer.Modes.Any(p => p == QueueConsumerMode.Retry); @@ -206,6 +287,7 @@ private IModel CreateConsumerChannel(QueueConsumerAttribute queueConsumer, strin bindConsumer); } break; + case QueueConsumerMode.Fail: { var bindConsumer = queueConsumer.Modes.Any(p => p == QueueConsumerMode.Fail); @@ -217,6 +299,12 @@ private IModel CreateConsumerChannel(QueueConsumerAttribute queueConsumer, strin return result; } + /// + /// The CreateConsumerChannel + /// + /// The queueName + /// The bindConsumer + /// The private IModel CreateConsumerChannel(string queueName, bool bindConsumer) { var mode = QueueConsumerMode.Normal; @@ -257,61 +345,97 @@ private IModel CreateConsumerChannel(string queueName, bool bindConsumer) return channel; } - private IModel CreateRetryConsumerChannel(string queueName, string routeKey, bool bindConsumer) + /// + /// The CreateFailConsumerChannel + /// + /// The queueName + /// The bindConsumer + /// The + private IModel CreateFailConsumerChannel(string queueName, bool bindConsumer) { - var mode = QueueConsumerMode.Retry; + var mode = QueueConsumerMode.Fail; if (!_persistentConnection.IsConnected) { _persistentConnection.TryConnect(); } - IDictionary arguments = new Dictionary(); - arguments.Add("x-dead-letter-exchange", _exchanges[QueueConsumerMode.Fail].ToString()); - arguments.Add("x-message-ttl", _messageTTL); - arguments.Add("x-dead-letter-routing-key", routeKey); var channel = _persistentConnection.CreateModel(); - var retryQueueName = $"{queueName}@{mode.ToString()}"; channel.ExchangeDeclare(exchange: _exchanges[mode], - type: "direct"); - channel.QueueDeclare(retryQueueName, true, false, false, arguments); + type: "direct"); + var failQueueName = $"{queueName}@{mode.ToString()}"; + channel.QueueDeclare(failQueueName, true, false, false, null); var consumer = new EventingBasicConsumer(channel); consumer.Received += async (model, ea) => { var eventName = ea.RoutingKey; - await ProcessEvent(eventName, ea.Body, mode,ea.BasicProperties); + await ProcessEvent(eventName, ea.Body, mode, ea.BasicProperties); channel.BasicAck(ea.DeliveryTag, false); }; if (bindConsumer) { channel.BasicQos(0, _prefetchCount, false); - channel.BasicConsume(queue: retryQueueName, + channel.BasicConsume(queue: failQueueName, autoAck: false, consumer: consumer); channel.CallbackException += (sender, ea) => - { - var key = new Tuple(queueName, mode); - _consumerChannels[key].Dispose(); - _consumerChannels[key] = CreateRetryConsumerChannel(queueName, routeKey, bindConsumer); - }; + { + var key = new Tuple(queueName, mode); + _consumerChannels[key].Dispose(); + _consumerChannels[key] = CreateFailConsumerChannel(queueName, bindConsumer); + }; } else channel.Close(); - return channel; } - private IModel CreateFailConsumerChannel(string queueName, bool bindConsumer) + /// + /// The CreateOverrideProperties + /// + /// The properties + /// The headers + /// The + private IBasicProperties CreateOverrideProperties(IBasicProperties properties, + IDictionary headers) { - var mode = QueueConsumerMode.Fail; + IBasicProperties newProperties = new BasicProperties(); + newProperties.ContentType = properties.ContentType ?? ""; + newProperties.ContentEncoding = properties.ContentEncoding ?? ""; + newProperties.Headers = properties.Headers; + if (newProperties.Headers == null) + newProperties.Headers = new Dictionary(); + foreach (var key in headers.Keys) + { + if (!newProperties.Headers.ContainsKey(key)) + newProperties.Headers.Add(key, headers[key]); + } + newProperties.DeliveryMode = properties.DeliveryMode; + return newProperties; + } + + /// + /// The CreateRetryConsumerChannel + /// + /// The queueName + /// The routeKey + /// The bindConsumer + /// The + private IModel CreateRetryConsumerChannel(string queueName, string routeKey, bool bindConsumer) + { + var mode = QueueConsumerMode.Retry; if (!_persistentConnection.IsConnected) { _persistentConnection.TryConnect(); } + IDictionary arguments = new Dictionary(); + arguments.Add("x-dead-letter-exchange", _exchanges[QueueConsumerMode.Fail].ToString()); + arguments.Add("x-message-ttl", _messageTTL); + arguments.Add("x-dead-letter-routing-key", routeKey); var channel = _persistentConnection.CreateModel(); + var retryQueueName = $"{queueName}@{mode.ToString()}"; channel.ExchangeDeclare(exchange: _exchanges[mode], - type: "direct"); - var failQueueName = $"{queueName}@{mode.ToString()}"; - channel.QueueDeclare(failQueueName, true, false, false, null); + type: "direct"); + channel.QueueDeclare(retryQueueName, true, false, false, arguments); var consumer = new EventingBasicConsumer(channel); consumer.Received += async (model, ea) => { @@ -322,15 +446,15 @@ private IModel CreateFailConsumerChannel(string queueName, bool bindConsumer) if (bindConsumer) { channel.BasicQos(0, _prefetchCount, false); - channel.BasicConsume(queue: failQueueName, + channel.BasicConsume(queue: retryQueueName, autoAck: false, consumer: consumer); channel.CallbackException += (sender, ea) => - { - var key = new Tuple(queueName, mode); - _consumerChannels[key].Dispose(); - _consumerChannels[key] = CreateFailConsumerChannel(queueName, bindConsumer); - }; + { + var key = new Tuple(queueName, mode); + _consumerChannels[key].Dispose(); + _consumerChannels[key] = CreateRetryConsumerChannel(queueName, routeKey, bindConsumer); + }; } else channel.Close(); @@ -338,97 +462,29 @@ private IModel CreateFailConsumerChannel(string queueName, bool bindConsumer) return channel; } - private async Task ProcessEvent(string eventName, byte[] body, QueueConsumerMode mode, IBasicProperties properties) - { - var message = Encoding.UTF8.GetString(body); - if (_subsManager.HasSubscriptionsForEvent(eventName)) - { - var eventType = _subsManager.GetEventTypeByName(eventName); - var integrationEvent = JsonConvert.DeserializeObject(message, eventType); - var handlers = _subsManager.GetHandlersForEvent(eventName); - var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType); - foreach (var handlerfactory in handlers) - { - var handler = handlerfactory.DynamicInvoke(); - long retryCount = 1; - try - { - var fastInvoker = GetHandler($"{concreteType.FullName}.Handle", concreteType.GetMethod("Handle")); - await (Task)fastInvoker(handler, new object[] { integrationEvent }); - } - catch - { - if (!_persistentConnection.IsConnected) - { - _persistentConnection.TryConnect(); - } - retryCount = GetRetryCount(properties); - using (var channel = _persistentConnection.CreateModel()) - { - if (retryCount > _retryCount) - { - // 重试次数大于3次,则自动加入到死信队列 - var rollbackCount = retryCount - _retryCount; - if (rollbackCount < _rollbackCount) - { - IDictionary headers = new Dictionary(); - if (!headers.ContainsKey("x-orig-routing-key")) - headers.Add("x-orig-routing-key", GetOrigRoutingKey(properties, eventName)); - retryCount = rollbackCount; - channel.BasicPublish(_exchanges[QueueConsumerMode.Fail], eventName, CreateOverrideProperties(properties, headers), body); - - } - } - else - { - IDictionary headers = properties.Headers; - if (headers == null) - { - headers = new Dictionary(); - } - if (!headers.ContainsKey("x-orig-routing-key")) - headers.Add("x-orig-routing-key", GetOrigRoutingKey(properties, eventName)); - channel.BasicPublish(_exchanges[QueueConsumerMode.Retry], eventName, CreateOverrideProperties(properties, headers), body); - } - } - } - finally - { - var baseConcreteType = typeof(BaseIntegrationEventHandler<>).MakeGenericType(eventType); - if (handler.GetType().BaseType== baseConcreteType) - { - var context = new EventContext() - { - Content= integrationEvent, - Count = retryCount, - Type = mode.ToString() - }; - var fastInvoker = GetHandler($"{baseConcreteType.FullName}.Handled", baseConcreteType.GetMethod("Handled")); - await (Task)fastInvoker(handler, new object[] { context }); - } - } - } - } - } - - private IBasicProperties CreateOverrideProperties(IBasicProperties properties, - IDictionary headers) + /// + /// The GetHandler + /// + /// The key + /// The method + /// The + private FastInvokeHandler GetHandler(string key, MethodInfo method) { - IBasicProperties newProperties = new BasicProperties(); - newProperties.ContentType = properties.ContentType ?? ""; - newProperties.ContentEncoding = properties.ContentEncoding ?? ""; - newProperties.Headers = properties.Headers; - if (newProperties.Headers == null) - newProperties.Headers = new Dictionary(); - foreach (var key in headers.Keys) + var objInstance = ServiceResolver.Current.GetService(null, key); + if (objInstance == null) { - if (!newProperties.Headers.ContainsKey(key)) - newProperties.Headers.Add(key, headers[key]); + objInstance = FastInvoke.GetMethodInvoker(method); + ServiceResolver.Current.Register(key, objInstance, null); } - newProperties.DeliveryMode = properties.DeliveryMode; - return newProperties; + return objInstance as FastInvokeHandler; } + /// + /// The GetOrigRoutingKey + /// + /// The properties + /// The defaultValue + /// The private String GetOrigRoutingKey(IBasicProperties properties, String defaultValue) { @@ -447,6 +503,11 @@ private String GetOrigRoutingKey(IBasicProperties properties, return routingKey; } + /// + /// The GetRetryCount + /// + /// The properties + /// The private long GetRetryCount(IBasicProperties properties) { long retryCount = 1L; @@ -454,13 +515,12 @@ private long GetRetryCount(IBasicProperties properties) { if (properties != null) { - IDictionary headers = properties.Headers; if (headers != null) { if (headers.ContainsKey("x-death")) { - retryCount= GetRetryCount(properties, headers); + retryCount = GetRetryCount(properties, headers); } else { @@ -475,7 +535,13 @@ private long GetRetryCount(IBasicProperties properties) return retryCount; } - private long GetRetryCount(IBasicProperties properties, IDictionary headers) + /// + /// The GetRetryCount + /// + /// The properties + /// The headers + /// The + private long GetRetryCount(IBasicProperties properties, IDictionary headers) { var retryCount = 1L; if (headers["x-death"] is List) @@ -501,20 +567,116 @@ private long GetRetryCount(IBasicProperties properties, IDictionary + /// The PersistentConnection_OnEventShutDown + /// + /// The sender + /// The reason + private void PersistentConnection_OnEventShutDown(object sender, ShutdownEventArgs reason) { - var objInstance = ServiceResolver.Current.GetService(null, key); - if (objInstance == null) + OnShutdown(this, new EventArgs()); + } + + /// + /// The ProcessEvent + /// + /// The eventName + /// The body + /// The mode + /// The properties + /// The + private async Task ProcessEvent(string eventName, byte[] body, QueueConsumerMode mode, IBasicProperties properties) + { + var message = Encoding.UTF8.GetString(body); + if (_subsManager.HasSubscriptionsForEvent(eventName)) { - objInstance = FastInvoke.GetMethodInvoker(method); - ServiceResolver.Current.Register(key, objInstance, null); + var eventType = _subsManager.GetEventTypeByName(eventName); + var integrationEvent = JsonConvert.DeserializeObject(message, eventType); + var handlers = _subsManager.GetHandlersForEvent(eventName); + var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType); + foreach (var handlerfactory in handlers) + { + var handler = handlerfactory.DynamicInvoke(); + long retryCount = 1; + try + { + var fastInvoker = GetHandler($"{concreteType.FullName}.Handle", concreteType.GetMethod("Handle")); + await (Task)fastInvoker(handler, new object[] { integrationEvent }); + } + catch + { + if (!_persistentConnection.IsConnected) + { + _persistentConnection.TryConnect(); + } + retryCount = GetRetryCount(properties); + using (var channel = _persistentConnection.CreateModel()) + { + if (retryCount > _retryCount) + { + // 重试次数大于3次,则自动加入到死信队列 + var rollbackCount = retryCount - _retryCount; + if (rollbackCount < _rollbackCount) + { + IDictionary headers = new Dictionary(); + if (!headers.ContainsKey("x-orig-routing-key")) + headers.Add("x-orig-routing-key", GetOrigRoutingKey(properties, eventName)); + retryCount = rollbackCount; + channel.BasicPublish(_exchanges[QueueConsumerMode.Fail], eventName, CreateOverrideProperties(properties, headers), body); + } + } + else + { + IDictionary headers = properties.Headers; + if (headers == null) + { + headers = new Dictionary(); + } + if (!headers.ContainsKey("x-orig-routing-key")) + headers.Add("x-orig-routing-key", GetOrigRoutingKey(properties, eventName)); + channel.BasicPublish(_exchanges[QueueConsumerMode.Retry], eventName, CreateOverrideProperties(properties, headers), body); + } + } + } + finally + { + var baseConcreteType = typeof(BaseIntegrationEventHandler<>).MakeGenericType(eventType); + if (handler.GetType().BaseType == baseConcreteType) + { + var context = new EventContext() + { + Content = integrationEvent, + Count = retryCount, + Type = mode.ToString() + }; + var fastInvoker = GetHandler($"{baseConcreteType.FullName}.Handled", baseConcreteType.GetMethod("Handled")); + await (Task)fastInvoker(handler, new object[] { context }); + } + } + } } - return objInstance as FastInvokeHandler; } - private void PersistentConnection_OnEventShutDown(object sender, ShutdownEventArgs reason) + /// + /// The SubsManager_OnEventRemoved + /// + /// The sender + /// The tuple + private void SubsManager_OnEventRemoved(object sender, ValueTuple tuple) { - OnShutdown(this,new EventArgs()); + if (!_persistentConnection.IsConnected) + { + _persistentConnection.TryConnect(); + } + + using (var channel = _persistentConnection.CreateModel()) + { + channel.QueueUnbind(queue: tuple.Item1, + exchange: BROKER_NAME, + routingKey: tuple.Item2); + } } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Implementation/RabbitMqSubscriptionAdapt.cs b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Implementation/RabbitMqSubscriptionAdapt.cs index ecb0e415b..c3246a4a2 100644 --- a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Implementation/RabbitMqSubscriptionAdapt.cs +++ b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Implementation/RabbitMqSubscriptionAdapt.cs @@ -1,36 +1,70 @@ using Surging.Core.CPlatform; using Surging.Core.CPlatform.EventBus; using Surging.Core.CPlatform.EventBus.Events; +using Surging.Core.EventBusRabbitMQ.Attributes; using System; using System.Collections.Generic; -using System.Reflection; -using Surging.Core.EventBusRabbitMQ.Attributes; using System.Linq; +using System.Reflection; namespace Surging.Core.EventBusRabbitMQ.Implementation { + /// + /// Defines the + /// public class RabbitMqSubscriptionAdapt : ISubscriptionAdapt { + #region 字段 + + /// + /// Defines the _consumeConfigurator + /// private readonly IConsumeConfigurator _consumeConfigurator; + + /// + /// Defines the _integrationEventHandler + /// private readonly IEnumerable _integrationEventHandler; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The consumeConfigurator + /// The integrationEventHandler public RabbitMqSubscriptionAdapt(IConsumeConfigurator consumeConfigurator, IEnumerable integrationEventHandler) { this._consumeConfigurator = consumeConfigurator; this._integrationEventHandler = integrationEventHandler; } - + + #endregion 构造函数 + + #region 方法 + + /// + /// The SubscribeAt + /// public void SubscribeAt() { _consumeConfigurator.Configure(GetQueueConsumers()); } - public void Unsubscribe() + /// + /// The Unsubscribe + /// + public void Unsubscribe() { _consumeConfigurator.Unconfigure(GetQueueConsumers()); } - - #region 私有方法 + /// + /// The GetQueueConsumers + /// + /// The private List GetQueueConsumers() { var result = new List(); @@ -41,6 +75,7 @@ private List GetQueueConsumers() } return result; } - #endregion + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/QueueConsumerMode.cs b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/QueueConsumerMode.cs index 7de08cbc8..a887a06da 100644 --- a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/QueueConsumerMode.cs +++ b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/QueueConsumerMode.cs @@ -4,10 +4,28 @@ namespace Surging.Core.EventBusRabbitMQ { - public enum QueueConsumerMode + #region 枚举 + + /// + /// Defines the QueueConsumerMode + /// + public enum QueueConsumerMode { + /// + /// Defines the Normal + /// Normal = 0, + + /// + /// Defines the Retry + /// Retry, + + /// + /// Defines the Fail + /// Fail, } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/RabbitmqLifetime.cs b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/RabbitmqLifetime.cs index ae82633d6..41fd71ef9 100644 --- a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/RabbitmqLifetime.cs +++ b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/RabbitmqLifetime.cs @@ -4,7 +4,10 @@ namespace Surging.Core.EventBusRabbitMQ { + /// + /// Defines the + /// public class RabbitmqLifetime { } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/ServiceHostBuilderExtensions.cs b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/ServiceHostBuilderExtensions.cs index 626ba2ef3..a71226f06 100644 --- a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/ServiceHostBuilderExtensions.cs +++ b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/ServiceHostBuilderExtensions.cs @@ -1,22 +1,32 @@ -using Surging.Core.CPlatform.EventBus; -using Surging.Core.ServiceHosting.Internal; -using Autofac; -using Surging.Core.CPlatform.Engines; +using Autofac; using Surging.Core.CPlatform; -using Surging.Core.CPlatform.Routing; +using Surging.Core.CPlatform.Engines; +using Surging.Core.CPlatform.EventBus; using Surging.Core.CPlatform.EventBus.Implementation; +using Surging.Core.CPlatform.Routing; +using Surging.Core.ServiceHosting.Internal; namespace Surging.Core.EventBusRabbitMQ { + /// + /// Defines the + /// public static class ServiceHostBuilderExtensions { + #region 方法 + + /// + /// The SubscribeAt + /// + /// The hostBuilder + /// The public static IServiceHostBuilder SubscribeAt(this IServiceHostBuilder hostBuilder) { return hostBuilder.MapServices(mapper => { mapper.Resolve().ServiceEngineStarted.Register(() => { - mapper.Resolve().SubscribeAt(); + mapper.Resolve().SubscribeAt(); new ServiceRouteWatch(mapper.Resolve(), () => { var subscriptionAdapt = mapper.Resolve(); @@ -29,5 +39,7 @@ public static IServiceHostBuilder SubscribeAt(this IServiceHostBuilder hostBuild }); }); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Utilities/ExtensionsToFastActivator.cs b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Utilities/ExtensionsToFastActivator.cs index f4e396425..8a7c43779 100644 --- a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Utilities/ExtensionsToFastActivator.cs +++ b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Utilities/ExtensionsToFastActivator.cs @@ -7,12 +7,25 @@ namespace Surging.Core.EventBusRabbitMQ.Utilities { + /// + /// Defines the + /// public static class ExtensionsToFastActivator { + #region 方法 + + /// + /// The FastInvoke + /// + /// + /// The target + /// The genericTypes + /// The expression public static void FastInvoke(this T target, Type[] genericTypes, Expression> expression) { - FastInvoker.Current.FastInvoke(target, genericTypes,expression); + FastInvoker.Current.FastInvoke(target, genericTypes, expression); } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Utilities/FastInvoker.cs b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Utilities/FastInvoker.cs index f2f86f812..dc9fa4684 100644 --- a/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Utilities/FastInvoker.cs +++ b/src/Surging.Core/Surging.Core.EventBusRabbitMQ/Utilities/FastInvoker.cs @@ -7,10 +7,27 @@ namespace Surging.Core.EventBusRabbitMQ.Utilities { + /// + /// Defines the + /// + /// public class FastInvoker { + #region 字段 + + /// + /// Defines the _current + /// [ThreadStatic] - static FastInvoker _current; + internal static FastInvoker _current; + + #endregion 字段 + + #region 属性 + + /// + /// Gets the Current + /// public static FastInvoker Current { get @@ -21,6 +38,15 @@ public static FastInvoker Current } } + #endregion 属性 + + #region 方法 + + /// + /// The FastInvoke + /// + /// The target + /// The expression public void FastInvoke(T target, Expression> expression) { var call = expression.Body as MethodCallExpression; @@ -30,23 +56,35 @@ public void FastInvoke(T target, Expression> expression) invoker(target); } + /// + /// The FastInvoke + /// + /// The target + /// The genericTypes + /// The expression public void FastInvoke(T target, Type[] genericTypes, Expression> expression) { var call = expression.Body as MethodCallExpression; if (call == null) throw new ArgumentException("只支持方法调用表达式", "expression"); - MethodInfo method = call.Method; - Action invoker = GetInvoker( () => - { - if (method.IsGenericMethod) - return GetGenericMethodFromTypes(method.GetGenericMethodDefinition(), genericTypes); - return method; - }); + MethodInfo method = call.Method; + Action invoker = GetInvoker(() => + { + if (method.IsGenericMethod) + return GetGenericMethodFromTypes(method.GetGenericMethodDefinition(), genericTypes); + return method; + }); invoker(target); } - MethodInfo GetGenericMethodFromTypes(MethodInfo method, Type[] genericTypes) + /// + /// The GetGenericMethodFromTypes + /// + /// The method + /// The genericTypes + /// The + internal MethodInfo GetGenericMethodFromTypes(MethodInfo method, Type[] genericTypes) { if (!method.IsGenericMethod) throw new ArgumentException("不能为非泛型方法指定泛型类型。: " + method.Name); @@ -60,16 +98,22 @@ MethodInfo GetGenericMethodFromTypes(MethodInfo method, Type[] genericTypes) return method; } - Action GetInvoker( Func getMethodInfo) - { - MethodInfo method = getMethodInfo(); + /// + /// The GetInvoker + /// + /// The getMethodInfo + /// The + internal Action GetInvoker(Func getMethodInfo) + { + MethodInfo method = getMethodInfo(); - ParameterExpression instanceParameter = Expression.Parameter(typeof(T), "target"); + ParameterExpression instanceParameter = Expression.Parameter(typeof(T), "target"); - MethodCallExpression call = Expression.Call(instanceParameter, method); + MethodCallExpression call = Expression.Call(instanceParameter, method); - return Expression.Lambda>(call, new[] { instanceParameter }).Compile(); - + return Expression.Lambda>(call, new[] { instanceParameter }).Compile(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/Abstractions/ActionContext.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/Abstractions/ActionContext.cs index 55eac5bbf..093d235cd 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/Abstractions/ActionContext.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/Abstractions/ActionContext.cs @@ -7,16 +7,34 @@ namespace Surging.Core.KestrelHttpServer { - public class ActionContext + /// + /// Defines the + /// + public class ActionContext { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public ActionContext() { - } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets or sets the HttpContext + /// public HttpContext HttpContext { get; set; } + /// + /// Gets or sets the Message + /// public TransportMessage Message { get; set; } - + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/Abstractions/ActionResult.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/Abstractions/ActionResult.cs index 04e15f703..98a447297 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/Abstractions/ActionResult.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/Abstractions/ActionResult.cs @@ -5,17 +5,32 @@ namespace Surging.Core.KestrelHttpServer { - public abstract class ActionResult: IActionResult + /// + /// Defines the + /// + public abstract class ActionResult : IActionResult { + #region 方法 + + /// + /// The ExecuteResult + /// + /// The context + public virtual void ExecuteResult(ActionContext context) + { + } + + /// + /// The ExecuteResultAsync + /// + /// The context + /// The public virtual Task ExecuteResultAsync(ActionContext context) { ExecuteResult(context); return Task.CompletedTask; } - - public virtual void ExecuteResult(ActionContext context) - { - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/Abstractions/ContentResult.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/Abstractions/ContentResult.cs index db68d73bd..7f9215116 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/Abstractions/ContentResult.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/Abstractions/ContentResult.cs @@ -4,7 +4,10 @@ namespace Surging.Core.KestrelHttpServer { - class ContentResult + /// + /// Defines the + /// + internal class ContentResult { } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/Abstractions/FileContentResult.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/Abstractions/FileContentResult.cs index 86a04fa6d..d508164f1 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/Abstractions/FileContentResult.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/Abstractions/FileContentResult.cs @@ -1,19 +1,60 @@ using Microsoft.AspNetCore.Http; +using Microsoft.Net.Http.Headers; +using Surging.Core.KestrelHttpServer.Internal; using System; -using System.Collections.Generic; +using System.Collections.Generic; +using System.IO; using System.Text; using System.Threading.Tasks; -using Microsoft.Net.Http.Headers; -using System.IO; -using Surging.Core.KestrelHttpServer.Internal; namespace Surging.Core.KestrelHttpServer { - public class FileContentResult : FileResult + /// + /// Defines the + /// + public class FileContentResult : FileResult { - private byte[] _fileContents; + #region 常量 + + /// + /// Defines the BufferSize + /// protected const int BufferSize = 64 * 1024; + #endregion 常量 + + #region 字段 + + /// + /// Defines the _fileContents + /// + private byte[] _fileContents; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The fileContents + /// The contentType + public FileContentResult(byte[] fileContents, MediaTypeHeaderValue contentType) + : base(contentType?.ToString()) + { + if (fileContents == null) + { + throw new ArgumentNullException(nameof(fileContents)); + } + + FileContents = fileContents; + } + + /// + /// Initializes a new instance of the class. + /// + /// The fileContents + /// The contentType public FileContentResult(byte[] fileContents, string contentType) : this(fileContents, MediaTypeHeaderValue.Parse(contentType)) { @@ -23,7 +64,13 @@ public FileContentResult(byte[] fileContents, string contentType) } } - public FileContentResult(byte[] fileContents, string contentType,string fileDownloadName) + /// + /// Initializes a new instance of the class. + /// + /// The fileContents + /// The contentType + /// The fileDownloadName + public FileContentResult(byte[] fileContents, string contentType, string fileDownloadName) : this(fileContents, MediaTypeHeaderValue.Parse(contentType)) { if (fileContents == null) @@ -37,17 +84,13 @@ public FileContentResult(byte[] fileContents, string contentType,string fileDown this.FileDownloadName = fileDownloadName; } - public FileContentResult(byte[] fileContents, MediaTypeHeaderValue contentType) - : base(contentType?.ToString()) - { - if (fileContents == null) - { - throw new ArgumentNullException(nameof(fileContents)); - } + #endregion 构造函数 - FileContents = fileContents; - } - + #region 属性 + + /// + /// Gets or sets the FileContents + /// public byte[] FileContents { get => _fileContents; @@ -62,6 +105,15 @@ public byte[] FileContents } } + #endregion 属性 + + #region 方法 + + /// + /// The ExecuteResultAsync + /// + /// The context + /// The public override async Task ExecuteResultAsync(ActionContext context) { if (context == null) @@ -86,5 +138,7 @@ public override async Task ExecuteResultAsync(ActionContext context) context.HttpContext.Abort(); } } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/Abstractions/FileResult.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/Abstractions/FileResult.cs index 0f1a74a5f..0bd906c4e 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/Abstractions/FileResult.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/Abstractions/FileResult.cs @@ -4,10 +4,26 @@ namespace Surging.Core.KestrelHttpServer { - public abstract class FileResult: ActionResult + /// + /// Defines the + /// + public abstract class FileResult : ActionResult { + #region 字段 + + /// + /// Defines the _fileDownloadName + /// private string _fileDownloadName; - + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The contentType protected FileResult(string contentType) { if (contentType == null) @@ -17,14 +33,25 @@ protected FileResult(string contentType) ContentType = contentType; } - + + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the ContentType + /// public string ContentType { get; } - + /// + /// Gets or sets the FileDownloadName + /// public string FileDownloadName { get { return _fileDownloadName ?? string.Empty; } set { _fileDownloadName = value; } } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/Abstractions/IActionResult.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/Abstractions/IActionResult.cs index ced3e7bbd..73eb93b93 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/Abstractions/IActionResult.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/Abstractions/IActionResult.cs @@ -6,8 +6,24 @@ namespace Surging.Core.KestrelHttpServer { - public interface IActionResult + #region 接口 + + /// + /// Defines the + /// + public interface IActionResult { + #region 方法 + + /// + /// The ExecuteResultAsync + /// + /// The context + /// The Task ExecuteResultAsync(ActionContext context); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/ApplicationInitializationContext.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/ApplicationInitializationContext.cs index 2eebb8ed7..674fec2bd 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/ApplicationInitializationContext.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/ApplicationInitializationContext.cs @@ -8,8 +8,20 @@ namespace Surging.Core.KestrelHttpServer { + /// + /// Defines the + /// public class ApplicationInitializationContext { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The builder + /// The modules + /// The virtualPaths + /// The configuration public ApplicationInitializationContext(IApplicationBuilder builder, List modules, string[] virtualPaths, @@ -21,12 +33,30 @@ public ApplicationInitializationContext(IApplicationBuilder builder, Configuration = Check.NotNull(configuration, nameof(configuration)); } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the Builder + /// public IApplicationBuilder Builder { get; } + /// + /// Gets the Configuration + /// public IConfigurationRoot Configuration { get; } + /// + /// Gets the Modules + /// public List Modules { get; } + /// + /// Gets the VirtualPaths + /// public string[] VirtualPaths { get; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/ConfigurationContext.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/ConfigurationContext.cs index 0132a761f..2e34b6efc 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/ConfigurationContext.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/ConfigurationContext.cs @@ -8,9 +8,21 @@ namespace Surging.Core.KestrelHttpServer { + /// + /// Defines the + /// public class ConfigurationContext { - public ConfigurationContext( IServiceCollection services, + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The services + /// The modules + /// The virtualPaths + /// The configuration + public ConfigurationContext(IServiceCollection services, List modules, string[] virtualPaths, IConfigurationRoot configuration) @@ -21,11 +33,30 @@ public ConfigurationContext( IServiceCollection services, Configuration = Check.NotNull(configuration, nameof(configuration)); } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the Configuration + /// public IConfigurationRoot Configuration { get; } - public IServiceCollection Services { get; } + /// + /// Gets the Modules + /// public List Modules { get; } + /// + /// Gets the Services + /// + public IServiceCollection Services { get; } + + /// + /// Gets the VirtualPaths + /// public string[] VirtualPaths { get; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/DefaultHttpServiceHost.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/DefaultHttpServiceHost.cs index a991e086f..a70fb986a 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/DefaultHttpServiceHost.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/DefaultHttpServiceHost.cs @@ -9,15 +9,33 @@ namespace Surging.Core.KestrelHttpServer { + /// + /// Defines the + /// public class DefaultHttpServiceHost : ServiceHostAbstract { - #region Field + #region 字段 + /// + /// Defines the _messageListenerFactory + /// private readonly Func> _messageListenerFactory; + + /// + /// Defines the _serverMessageListener + /// private IMessageListener _serverMessageListener; - #endregion Field + #endregion 字段 + + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The messageListenerFactory + /// The serviceExecutor + /// The httpMessageListener public DefaultHttpServiceHost(Func> messageListenerFactory, IServiceExecutor serviceExecutor, HttpMessageListener httpMessageListener) : base(serviceExecutor) { _messageListenerFactory = messageListenerFactory; @@ -31,9 +49,13 @@ await Task.Run(() => }; } - #region Overrides of ServiceHostAbstract + #endregion 构造函数 + #region 方法 + /// + /// The Dispose + /// public override void Dispose() { (_serverMessageListener as IDisposable)?.Dispose(); @@ -49,16 +71,28 @@ public override async Task StartAsync(EndPoint endPoint) await _messageListenerFactory(endPoint); } + /// + /// The StartAsync + /// + /// The ip + /// The port + /// The public override async Task StartAsync(string ip, int port) { _serverMessageListener = await _messageListenerFactory(new IPEndPoint(IPAddress.Parse(ip), port)); } - #endregion Overrides of ServiceHostAbstract - + /// + /// The MessageListener_Received + /// + /// The sender + /// The message + /// The private async Task MessageListener_Received(IMessageSender sender, CPlatform.Messages.TransportMessage message) { await ServiceExecutor.ExecuteAsync(sender, message); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/Extensions/ModuleProviderExtensions.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/Extensions/ModuleProviderExtensions.cs index 6ba357311..327b978ea 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/Extensions/ModuleProviderExtensions.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/Extensions/ModuleProviderExtensions.cs @@ -7,20 +7,29 @@ namespace Surging.Core.KestrelHttpServer.Extensions { - public static class ModuleProviderExtensions + /// + /// Defines the + /// + public static class ModuleProviderExtensions { - public static void Initialize(this IModuleProvider moduleProvider, ApplicationInitializationContext builder) + #region 方法 + + /// + /// The ConfigureHost + /// + /// The moduleProvider + /// The context + public static void ConfigureHost(this IModuleProvider moduleProvider, WebHostContext context) { moduleProvider.Modules.ForEach(p => { try { - using (var abstractModule = p) - if (abstractModule.Enable) - { - var module = abstractModule as KestrelHttpModule; - module?.Initialize(builder); - } + if (p.Enable) + { + var module = p as KestrelHttpModule; + module?.RegisterBuilder(context); + } } catch (Exception ex) { @@ -29,6 +38,11 @@ public static void Initialize(this IModuleProvider moduleProvider, ApplicationIn }); } + /// + /// The ConfigureServices + /// + /// The moduleProvider + /// The context public static void ConfigureServices(this IModuleProvider moduleProvider, ConfigurationContext context) { moduleProvider.Modules.ForEach(p => @@ -48,17 +62,23 @@ public static void ConfigureServices(this IModuleProvider moduleProvider, Config }); } - public static void ConfigureHost(this IModuleProvider moduleProvider, WebHostContext context) + /// + /// The Initialize + /// + /// The moduleProvider + /// The builder + public static void Initialize(this IModuleProvider moduleProvider, ApplicationInitializationContext builder) { moduleProvider.Modules.ForEach(p => { try { - if (p.Enable) - { - var module = p as KestrelHttpModule; - module?.RegisterBuilder(context); - } + using (var abstractModule = p) + if (abstractModule.Enable) + { + var module = abstractModule as KestrelHttpModule; + module?.Initialize(builder); + } } catch (Exception ex) { @@ -66,5 +86,7 @@ public static void ConfigureHost(this IModuleProvider moduleProvider, WebHostCon } }); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/Filters/IAuthorizationFilter.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/Filters/IAuthorizationFilter.cs index d703be8b4..0ed793938 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/Filters/IAuthorizationFilter.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/Filters/IAuthorizationFilter.cs @@ -1,11 +1,25 @@ - -using Surging.Core.KestrelHttpServer.Filters.Implementation; +using Surging.Core.KestrelHttpServer.Filters.Implementation; using System.Threading.Tasks; namespace Surging.Core.KestrelHttpServer.Filters { + #region 接口 + + /// + /// Defines the + /// public interface IAuthorizationFilter : IFilter { + #region 方法 + + /// + /// The OnAuthorization + /// + /// The serviceRouteContext void OnAuthorization(AuthorizationFilterContext serviceRouteContext); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/Filters/IFilter.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/Filters/IFilter.cs index b885d10cc..3d49ddd9e 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/Filters/IFilter.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/Filters/IFilter.cs @@ -4,7 +4,14 @@ namespace Surging.Core.KestrelHttpServer.Filters { - public interface IFilter + #region 接口 + + /// + /// Defines the + /// + public interface IFilter { } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/Filters/Implementation/AuthorizationFilterContext.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/Filters/Implementation/AuthorizationFilterContext.cs index ceb62b6dd..8845aaa91 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/Filters/Implementation/AuthorizationFilterContext.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/Filters/Implementation/AuthorizationFilterContext.cs @@ -6,10 +6,23 @@ namespace Surging.Core.KestrelHttpServer.Filters.Implementation { - public class AuthorizationFilterContext + /// + /// Defines the + /// + public class AuthorizationFilterContext { - public ServiceRoute Route { get; internal set; } + #region 属性 + /// + /// Gets or sets the Context + /// public HttpContext Context { get; internal set; } + + /// + /// Gets or sets the Route + /// + public ServiceRoute Route { get; internal set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/HttpExecutor.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/HttpExecutor.cs index e9e6e2c35..b28a8d7b1 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/HttpExecutor.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/HttpExecutor.cs @@ -10,9 +10,9 @@ using Surging.Core.CPlatform.Utilities; using Surging.Core.ProxyGenerator; using System; -using System.Linq; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; @@ -20,21 +20,62 @@ namespace Surging.Core.KestrelHttpServer { - public class HttpExecutor : IServiceExecutor + /// + /// Defines the + /// + public class HttpExecutor : IServiceExecutor { - #region Field - private readonly IServiceEntryLocate _serviceEntryLocate; - private readonly ILogger _logger; - private readonly IServiceRouteProvider _serviceRouteProvider; + #region 字段 + + /// + /// Defines the _authorizationFilter + /// private readonly IAuthorizationFilter _authorizationFilter; - private readonly CPlatformContainer _serviceProvider; - private readonly ITypeConvertibleService _typeConvertibleService; + + /// + /// Defines the _concurrent + /// private readonly ConcurrentDictionary> _concurrent = new ConcurrentDictionary>(); - #endregion Field - #region Constructor + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the _serviceEntryLocate + /// + private readonly IServiceEntryLocate _serviceEntryLocate; + + /// + /// Defines the _serviceProvider + /// + private readonly CPlatformContainer _serviceProvider; + + /// + /// Defines the _serviceRouteProvider + /// + private readonly IServiceRouteProvider _serviceRouteProvider; + + /// + /// Defines the _typeConvertibleService + /// + private readonly ITypeConvertibleService _typeConvertibleService; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serviceEntryLocate + /// The serviceRouteProvider + /// The authorizationFilter + /// The logger + /// The serviceProvider + /// The typeConvertibleService public HttpExecutor(IServiceEntryLocate serviceEntryLocate, IServiceRouteProvider serviceRouteProvider, IAuthorizationFilter authorizationFilter, ILogger logger, CPlatformContainer serviceProvider, ITypeConvertibleService typeConvertibleService) @@ -46,10 +87,17 @@ public HttpExecutor(IServiceEntryLocate serviceEntryLocate, IServiceRouteProvide _serviceRouteProvider = serviceRouteProvider; _authorizationFilter = authorizationFilter; } - #endregion Constructor - #region Implementation of IExecutor + #endregion 构造函数 + + #region 方法 + /// + /// The ExecuteAsync + /// + /// The sender + /// The message + /// The public async Task ExecuteAsync(IMessageSender sender, TransportMessage message) { if (_logger.IsEnabled(LogLevel.Trace)) @@ -89,43 +137,46 @@ public async Task ExecuteAsync(IMessageSender sender, TransportMessage message) } await SendRemoteInvokeResult(sender, httpResultMessage); } - - #endregion Implementation of IServiceExecutor + /// + /// The GetExceptionMessage + /// + /// The exception + /// The + private static string GetExceptionMessage(Exception exception) + { + if (exception == null) + return string.Empty; - #region Private Method + var message = exception.Message; + if (exception.InnerException != null) + { + message += "|InnerException:" + GetExceptionMessage(exception.InnerException); + } + return message; + } - private async Task> RemoteExecuteAsync(ServiceEntry entry, HttpMessage httpMessage) + /// + /// The LocalExecuteAsync + /// + /// The entry + /// The httpMessage + /// The + private async Task> LocalExecuteAsync(ServiceEntry entry, HttpMessage httpMessage) { HttpResultMessage resultMessage = new HttpResultMessage(); - var provider = _concurrent.GetValueOrDefault(httpMessage.RoutePath); - var list = new List(); - if (provider.Item1 == null) - { - provider.Item2 = ServiceLocator.GetService().CreateProxy(httpMessage.ServiceKey, entry.Type); - provider.Item3 = provider.Item2.GetType().GetTypeInfo().DeclaredMethods.Where(p => p.Name == entry.MethodName).FirstOrDefault(); - provider.Item1 = FastInvoke.GetMethodInvoker(provider.Item3); - _concurrent.GetOrAdd(httpMessage.RoutePath, ValueTuple.Create(provider.Item1, provider.Item2, provider.Item3)); - } - foreach (var parameterInfo in provider.Item3.GetParameters()) - { - var value = httpMessage.Parameters[parameterInfo.Name]; - var parameterType = parameterInfo.ParameterType; - var parameter = _typeConvertibleService.Convert(value, parameterType); - list.Add(parameter); - } try { - var methodResult = provider.Item1(provider.Item2, list.ToArray()); + var result = await entry.Func(httpMessage.ServiceKey, httpMessage.Parameters); + var task = result as Task; - var task = methodResult as Task; if (task == null) { - resultMessage.Entity = methodResult; + resultMessage.Entity = result; } else { - await task; + task.Wait(); var taskType = task.GetType().GetTypeInfo(); if (taskType.IsGenericType) resultMessage.Entity = taskType.GetProperty("Result").GetValue(task); @@ -133,30 +184,53 @@ private async Task> RemoteExecuteAsync(ServiceEntry en resultMessage.IsSucceed = resultMessage.Entity != null; resultMessage.StatusCode = resultMessage.IsSucceed ? (int)StatusCode.Success : (int)StatusCode.RequestError; } - catch (Exception ex) + catch (Exception exception) { if (_logger.IsEnabled(LogLevel.Error)) - _logger.LogError(ex, "执行远程调用逻辑时候发生了错误。"); - resultMessage = new HttpResultMessage { Entity = null, Message = "执行发生了错误。", StatusCode = (int)StatusCode.RequestError }; + _logger.LogError(exception, "执行本地逻辑时候发生了错误。"); + resultMessage.Message = "执行发生了错误。"; + resultMessage.StatusCode = exception.HResult; } return resultMessage; } - private async Task> LocalExecuteAsync(ServiceEntry entry, HttpMessage httpMessage) + /// + /// The RemoteExecuteAsync + /// + /// The entry + /// The httpMessage + /// The + private async Task> RemoteExecuteAsync(ServiceEntry entry, HttpMessage httpMessage) { HttpResultMessage resultMessage = new HttpResultMessage(); + var provider = _concurrent.GetValueOrDefault(httpMessage.RoutePath); + var list = new List(); + if (provider.Item1 == null) + { + provider.Item2 = ServiceLocator.GetService().CreateProxy(httpMessage.ServiceKey, entry.Type); + provider.Item3 = provider.Item2.GetType().GetTypeInfo().DeclaredMethods.Where(p => p.Name == entry.MethodName).FirstOrDefault(); + provider.Item1 = FastInvoke.GetMethodInvoker(provider.Item3); + _concurrent.GetOrAdd(httpMessage.RoutePath, ValueTuple.Create(provider.Item1, provider.Item2, provider.Item3)); + } + foreach (var parameterInfo in provider.Item3.GetParameters()) + { + var value = httpMessage.Parameters[parameterInfo.Name]; + var parameterType = parameterInfo.ParameterType; + var parameter = _typeConvertibleService.Convert(value, parameterType); + list.Add(parameter); + } try { - var result = await entry.Func(httpMessage.ServiceKey, httpMessage.Parameters); - var task = result as Task; + var methodResult = provider.Item1(provider.Item2, list.ToArray()); + var task = methodResult as Task; if (task == null) { - resultMessage.Entity = result; + resultMessage.Entity = methodResult; } else { - task.Wait(); + await task; var taskType = task.GetType().GetTypeInfo(); if (taskType.IsGenericType) resultMessage.Entity = taskType.GetProperty("Result").GetValue(task); @@ -164,16 +238,21 @@ private async Task> LocalExecuteAsync(ServiceEntry ent resultMessage.IsSucceed = resultMessage.Entity != null; resultMessage.StatusCode = resultMessage.IsSucceed ? (int)StatusCode.Success : (int)StatusCode.RequestError; } - catch (Exception exception) + catch (Exception ex) { if (_logger.IsEnabled(LogLevel.Error)) - _logger.LogError(exception, "执行本地逻辑时候发生了错误。"); - resultMessage.Message = "执行发生了错误。"; - resultMessage.StatusCode = exception.HResult; + _logger.LogError(ex, "执行远程调用逻辑时候发生了错误。"); + resultMessage = new HttpResultMessage { Entity = null, Message = "执行发生了错误。", StatusCode = (int)StatusCode.RequestError }; } return resultMessage; } + /// + /// The SendRemoteInvokeResult + /// + /// The sender + /// The resultMessage + /// The private async Task SendRemoteInvokeResult(IMessageSender sender, HttpResultMessage resultMessage) { try @@ -192,21 +271,6 @@ private async Task SendRemoteInvokeResult(IMessageSender sender, HttpResultMessa } } - private static string GetExceptionMessage(Exception exception) - { - if (exception == null) - return string.Empty; - - var message = exception.Message; - if (exception.InnerException != null) - { - message += "|InnerException:" + GetExceptionMessage(exception.InnerException); - } - return message; - } - - #endregion Private Method - + #endregion 方法 } -} - +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/HttpMessageListener.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/HttpMessageListener.cs index 4a83b0858..b500b66dc 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/HttpMessageListener.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/HttpMessageListener.cs @@ -19,14 +19,38 @@ namespace Surging.Core.KestrelHttpServer { + /// + /// Defines the + /// public abstract class HttpMessageListener : IMessageListener { - public event ReceivedDelegate Received; + #region 字段 + + /// + /// Defines the _logger + /// private readonly ILogger _logger; + + /// + /// Defines the _serializer + /// private readonly ISerializer _serializer; - private event RequestDelegate Requested; + + /// + /// Defines the _serviceRouteProvider + /// private readonly IServiceRouteProvider _serviceRouteProvider; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The logger + /// The serializer + /// The serviceRouteProvider public HttpMessageListener(ILogger logger, ISerializer serializer, IServiceRouteProvider serviceRouteProvider) { _logger = logger; @@ -34,18 +58,35 @@ public HttpMessageListener(ILogger logger, ISerializer + /// Defines the Received + /// + public event ReceivedDelegate Received; + /// + /// Defines the Requested + /// + private event RequestDelegate Requested; + + #endregion 事件 + + #region 方法 + + /// + /// The OnReceived + /// + /// The sender + /// The context + /// The public async Task OnReceived(IMessageSender sender, HttpContext context) { var path = HttpUtility.UrlDecode(GetRoutePath(context.Request.Path.ToString())); - var serviceRoute =await _serviceRouteProvider.GetRouteByPathRegex(path); - IDictionary parameters = context.Request.Query.ToDictionary(p => p.Key,p => (object)p.Value.ToString()); + var serviceRoute = await _serviceRouteProvider.GetRouteByPathRegex(path); + IDictionary parameters = context.Request.Query.ToDictionary(p => p.Key, p => (object)p.Value.ToString()); parameters.Remove("servicekey", out object serviceKey); StreamReader streamReader = new StreamReader(context.Request.Body); var data = await streamReader.ReadToEndAsync(); @@ -56,12 +97,12 @@ public async Task OnReceived(IMessageSender sender, HttpContext context) var @params = RouteTemplateSegmenter.Segment(serviceRoute.ServiceDescriptor.RoutePath, path); foreach (var param in @params) { - parameters.Add(param.Key,param.Value); + parameters.Add(param.Key, param.Value); } } if (context.Request.HasFormContentType) { - var collection =await GetFormCollection(context.Request); + var collection = await GetFormCollection(context.Request); parameters.Add("form", collection); await Received(sender, new TransportMessage(new HttpMessage { @@ -72,7 +113,6 @@ public async Task OnReceived(IMessageSender sender, HttpContext context) } else { - if (context.Request.Method == "POST") { await Received(sender, new TransportMessage(new HttpMessage @@ -94,9 +134,27 @@ public async Task OnReceived(IMessageSender sender, HttpContext context) } } + /// + /// The OnReceived + /// + /// The sender + /// The message + /// The + public async Task OnReceived(IMessageSender sender, TransportMessage message) + { + if (Received == null) + return; + await Received(sender, message); + } + + /// + /// The GetFormCollection + /// + /// The request + /// The private async Task GetFormCollection(HttpRequest request) { - var boundary = GetName("boundary=", request.ContentType); + var boundary = GetName("boundary=", request.ContentType); var reader = new MultipartReader(boundary, request.Body); var collection = await GetMultipartForm(reader); var fileCollection = new HttpFormFileCollection(); @@ -112,47 +170,57 @@ private async Task GetFormCollection(HttpRequest request) { var itemCollection = item.Value as Dictionary; fields = fields.Concat(itemCollection).ToDictionary(k => k.Key, v => v.Value); - } } - return new HttpFormCollection(fields, fileCollection); + return new HttpFormCollection(fields, fileCollection); } - private async Task> GetMultipartForm(MultipartReader reader) + /// + /// The GetMultipartForm + /// + /// The reader + /// The + private async Task> GetMultipartForm(MultipartReader reader) { - var section = await reader.ReadNextSectionAsync(); + var section = await reader.ReadNextSectionAsync(); var collection = new Dictionary(); if (section != null) - { - var name=GetName("name=",section.ContentDisposition); - var fileName = GetName("filename=",section.ContentDisposition); + { + var name = GetName("name=", section.ContentDisposition); + var fileName = GetName("filename=", section.ContentDisposition); var buffer = new MemoryStream(); await section.Body.CopyToAsync(buffer); - if(string.IsNullOrEmpty(fileName)) + if (string.IsNullOrEmpty(fileName)) { var fields = new Dictionary(); StreamReader streamReader = new StreamReader(buffer); - fields.Add(name, new StringValues(UTF8Encoding.Default.GetString(buffer.GetBuffer(),0,(int)buffer.Length))); + fields.Add(name, new StringValues(UTF8Encoding.Default.GetString(buffer.GetBuffer(), 0, (int)buffer.Length))); collection.Add(name, fields); } else { var fileCollection = new HttpFormFileCollection(); StreamReader streamReader = new StreamReader(buffer); - fileCollection.Add(new HttpFormFile(buffer.Length,name,fileName,buffer.GetBuffer())); + fileCollection.Add(new HttpFormFile(buffer.Length, name, fileName, buffer.GetBuffer())); collection.Add(name, fileCollection); } - var formCollection= await GetMultipartForm(reader); - foreach(var item in formCollection) + var formCollection = await GetMultipartForm(reader); + foreach (var item in formCollection) { if (!collection.ContainsKey(item.Key)) - collection.Add(item.Key,item.Value); + collection.Add(item.Key, item.Value); } } return collection; } - private string GetName(string type,string content) + /// + /// The GetName + /// + /// The type + /// The content + /// The + private string GetName(string type, string content) { var elements = content.Split(';'); var element = elements.Where(entry => entry.Trim().StartsWith(type)).FirstOrDefault()?.Trim(); @@ -164,6 +232,11 @@ private string GetName(string type,string content) return name; } + /// + /// The GetRoutePath + /// + /// The path + /// The private string GetRoutePath(string path) { string routePath = ""; @@ -182,5 +255,7 @@ private string GetRoutePath(string path) } return routePath; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/HttpServerMessageSender.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/HttpServerMessageSender.cs index 74978dc8e..3aa6a5f3f 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/HttpServerMessageSender.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/HttpServerMessageSender.cs @@ -9,20 +9,51 @@ namespace Surging.Core.KestrelHttpServer { + /// + /// Defines the + /// public class HttpServerMessageSender : IMessageSender { - private readonly ISerializer _serializer; + #region 字段 + + /// + /// Defines the _context + /// private readonly HttpContext _context; - public HttpServerMessageSender(ISerializer serializer,HttpContext httpContext) + + /// + /// Defines the _serializer + /// + private readonly ISerializer _serializer; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serializer + /// The httpContext + public HttpServerMessageSender(ISerializer serializer, HttpContext httpContext) { _serializer = serializer; _context = httpContext; } - + + #endregion 构造函数 + + #region 方法 + + /// + /// The SendAndFlushAsync + /// + /// The message + /// The public async Task SendAndFlushAsync(TransportMessage message) { var httpMessage = message.GetContent>(); - var actionResult= httpMessage.Entity as IActionResult; + var actionResult = httpMessage.Entity as IActionResult; if (actionResult == null) { var text = _serializer.Serialize(message.Content); @@ -42,6 +73,11 @@ await actionResult.ExecuteResultAsync(new ActionContext } } + /// + /// The SendAsync + /// + /// The message + /// The public async Task SendAsync(TransportMessage message) { var actionResult = message.GetContent(); @@ -63,6 +99,7 @@ await actionResult.ExecuteResultAsync(new ActionContext }); } } - + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/HttpServiceHost.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/HttpServiceHost.cs index 9b3027b56..b41858919 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/HttpServiceHost.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/HttpServiceHost.cs @@ -11,15 +11,33 @@ namespace Surging.Core.KestrelHttpServer { - public class HttpServiceHost : ServiceHostAbstract + /// + /// Defines the + /// + public class HttpServiceHost : ServiceHostAbstract { - #region Field + #region 字段 + /// + /// Defines the _messageListenerFactory + /// private readonly Func> _messageListenerFactory; + + /// + /// Defines the _serverMessageListener + /// private IMessageListener _serverMessageListener; - #endregion Field + #endregion 字段 + + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The messageListenerFactory + /// The serviceExecutor + /// The httpMessageListener public HttpServiceHost(Func> messageListenerFactory, IServiceExecutor serviceExecutor, HttpMessageListener httpMessageListener) : base(serviceExecutor) { _messageListenerFactory = messageListenerFactory; @@ -33,9 +51,13 @@ await Task.Run(() => }; } - #region Overrides of ServiceHostAbstract + #endregion 构造函数 + #region 方法 + /// + /// The Dispose + /// public override void Dispose() { (_serverMessageListener as IDisposable)?.Dispose(); @@ -48,19 +70,31 @@ public override void Dispose() /// 一个任务。 public override async Task StartAsync(EndPoint endPoint) { - await _messageListenerFactory(endPoint); + await _messageListenerFactory(endPoint); } + /// + /// The StartAsync + /// + /// The ip + /// The port + /// The public override async Task StartAsync(string ip, int port) { - await _messageListenerFactory(new IPEndPoint(IPAddress.Parse(ip), AppConfig.ServerOptions.Ports.HttpPort??0)); + await _messageListenerFactory(new IPEndPoint(IPAddress.Parse(ip), AppConfig.ServerOptions.Ports.HttpPort ?? 0)); } - #endregion Overrides of ServiceHostAbstract - + /// + /// The MessageListener_Received + /// + /// The sender + /// The message + /// The private async Task MessageListener_Received(IMessageSender sender, TransportMessage message) { await ServiceExecutor.ExecuteAsync(sender, message); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/Internal/FileStreamResult.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/Internal/FileStreamResult.cs index f331d1431..3cf756696 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/Internal/FileStreamResult.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/Internal/FileStreamResult.cs @@ -6,15 +6,27 @@ namespace Surging.Core.KestrelHttpServer.Internal { + /// + /// Defines the + /// public class FileStreamResult : FileResult { + #region 字段 + + /// + /// Defines the _fileStream + /// private Stream _fileStream; - - public FileStreamResult(Stream fileStream, string contentType) - : this(fileStream, MediaTypeHeaderValue.Parse(contentType)) - { - } - + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The fileStream + /// The contentType public FileStreamResult(Stream fileStream, MediaTypeHeaderValue contentType) : base(contentType?.ToString()) { @@ -25,7 +37,24 @@ public FileStreamResult(Stream fileStream, MediaTypeHeaderValue contentType) FileStream = fileStream; } - + + /// + /// Initializes a new instance of the class. + /// + /// The fileStream + /// The contentType + public FileStreamResult(Stream fileStream, string contentType) + : this(fileStream, MediaTypeHeaderValue.Parse(contentType)) + { + } + + #endregion 构造函数 + + #region 属性 + + /// + /// Gets or sets the FileStream + /// public Stream FileStream { get => _fileStream; @@ -40,5 +69,6 @@ public Stream FileStream } } + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/Internal/HttpFormCollection.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/Internal/HttpFormCollection.cs index 09ff41719..0f04c60ae 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/Internal/HttpFormCollection.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/Internal/HttpFormCollection.cs @@ -6,6 +6,105 @@ namespace Surging.Core.KestrelHttpServer.Internal { + /// + /// Defines the + /// + public struct Enumerator : IEnumerator> + { + #region 字段 + + /// + /// Defines the _dictionaryEnumerator + /// + private Dictionary.Enumerator _dictionaryEnumerator; + + /// + /// Defines the _notEmpty + /// + private bool _notEmpty; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The dictionaryEnumerator + internal Enumerator(Dictionary.Enumerator dictionaryEnumerator) + { + _dictionaryEnumerator = dictionaryEnumerator; + _notEmpty = true; + } + + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the Current + /// + public KeyValuePair Current + { + get + { + if (_notEmpty) + { + return _dictionaryEnumerator.Current; + } + return default(KeyValuePair); + } + } + + /// + /// Gets the Current + /// + object IEnumerator.Current + { + get + { + return Current; + } + } + + #endregion 属性 + + #region 方法 + + /// + /// The Dispose + /// + public void Dispose() + { + } + + /// + /// The MoveNext + /// + /// The + public bool MoveNext() + { + if (_notEmpty) + { + return _dictionaryEnumerator.MoveNext(); + } + return false; + } + + /// + /// The Reset + /// + void IEnumerator.Reset() + { + if (_notEmpty) + { + ((IEnumerator)_dictionaryEnumerator).Reset(); + } + } + + #endregion 方法 + } + public class HttpFormCollection : IEnumerable>, IEnumerable { public static readonly HttpFormCollection Empty = new HttpFormCollection(); @@ -31,16 +130,12 @@ public HttpFormCollection(Dictionary fields, HttpFormFileC public HttpFormFileCollection Files { - get - { - return _files ?? EmptyFiles; - } + get { return _files ?? EmptyFiles; } private set { _files = value; } } private Dictionary Store { get; set; } - public StringValues this[string key] { get @@ -59,7 +154,6 @@ public StringValues this[string key] } } - public int Count { get @@ -80,7 +174,6 @@ public ICollection Keys } } - public bool ContainsKey(string key) { if (Store == null) @@ -89,7 +182,7 @@ public bool ContainsKey(string key) } return Store.ContainsKey(key); } - + public bool TryGetValue(string key, out StringValues value) { if (Store == null) @@ -108,7 +201,7 @@ public Enumerator GetEnumerator() } return new Enumerator(Store.GetEnumerator()); } - + IEnumerator> IEnumerable>.GetEnumerator() { if (Store == null || Store.Count == 0) @@ -117,7 +210,7 @@ IEnumerator> IEnumerable> - { - private Dictionary.Enumerator _dictionaryEnumerator; - private bool _notEmpty; - - internal Enumerator(Dictionary.Enumerator dictionaryEnumerator) - { - _dictionaryEnumerator = dictionaryEnumerator; - _notEmpty = true; - } - - public bool MoveNext() - { - if (_notEmpty) - { - return _dictionaryEnumerator.MoveNext(); - } - return false; - } - - public KeyValuePair Current - { - get - { - if (_notEmpty) - { - return _dictionaryEnumerator.Current; - } - return default(KeyValuePair); - } - } - - public void Dispose() - { - } - - object IEnumerator.Current - { - get - { - return Current; - } - } - - void IEnumerator.Reset() - { - if (_notEmpty) - { - ((IEnumerator)_dictionaryEnumerator).Reset(); - } - } - } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/Internal/HttpFormFile.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/Internal/HttpFormFile.cs index 8ab494609..ba23422f6 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/Internal/HttpFormFile.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/Internal/HttpFormFile.cs @@ -4,21 +4,52 @@ namespace Surging.Core.KestrelHttpServer.Internal { - public class HttpFormFile + /// + /// Defines the + /// + public class HttpFormFile { - public HttpFormFile(long length, string name, string fileName,byte[] file) + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The length + /// The name + /// The fileName + /// The file + public HttpFormFile(long length, string name, string fileName, byte[] file) { Length = length; Name = name; FileName = fileName; File = file; } + + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the File + /// + public byte[] File { get; } + + /// + /// Gets the FileName + /// + public string FileName { get; } + + /// + /// Gets the Length + /// public long Length { get; } - + + /// + /// Gets the Name + /// public string Name { get; } - - public string FileName { get; } - public byte[] File { get; } + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/Internal/StreamCopyOperation.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/Internal/StreamCopyOperation.cs index 93ae2542a..cc9b3c529 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/Internal/StreamCopyOperation.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/Internal/StreamCopyOperation.cs @@ -11,30 +11,74 @@ namespace Surging.Core.KestrelHttpServer.Internal { - internal class StreamCopyOperation + /// + /// Defines the + /// + internal class StreamCopyOperation { + #region 常量 + + /// + /// Defines the DefaultBufferSize + /// private const int DefaultBufferSize = 1024 * 16; - private readonly TaskCompletionSource _tcs; - private readonly Stream _source; - private readonly Stream _destination; + #endregion 常量 + + #region 字段 + + /// + /// Defines the _buffer + /// private readonly byte[] _buffer; + + /// + /// Defines the _destination + /// + private readonly Stream _destination; + + /// + /// Defines the _readCallback + /// private readonly AsyncCallback _readCallback; + + /// + /// Defines the _source + /// + private readonly Stream _source; + + /// + /// Defines the _tcs + /// + private readonly TaskCompletionSource _tcs; + + /// + /// Defines the _writeCallback + /// private readonly AsyncCallback _writeCallback; + /// + /// Defines the _bytesRemaining + /// private long? _bytesRemaining; + + /// + /// Defines the _cancel + /// private CancellationToken _cancel; - internal StreamCopyOperation(Stream source, Stream destination, long? bytesRemaining, CancellationToken cancel) - : this(source, destination, bytesRemaining, DefaultBufferSize, cancel) - { - } + #endregion 字段 - internal StreamCopyOperation(Stream source, Stream destination, long? bytesRemaining, int bufferSize, CancellationToken cancel) - : this(source, destination, bytesRemaining, new byte[bufferSize], cancel) - { - } + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The source + /// The destination + /// The bytesRemaining + /// The buffer + /// The cancel internal StreamCopyOperation(Stream source, Stream destination, long? bytesRemaining, byte[] buffer, CancellationToken cancel) { Contract.Assert(source != null); @@ -53,6 +97,44 @@ internal StreamCopyOperation(Stream source, Stream destination, long? bytesRemai _writeCallback = new AsyncCallback(WriteCallback); } + /// + /// Initializes a new instance of the class. + /// + /// The source + /// The destination + /// The bytesRemaining + /// The cancel + internal StreamCopyOperation(Stream source, Stream destination, long? bytesRemaining, CancellationToken cancel) + : this(source, destination, bytesRemaining, DefaultBufferSize, cancel) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The source + /// The destination + /// The bytesRemaining + /// The bufferSize + /// The cancel + internal StreamCopyOperation(Stream source, Stream destination, long? bytesRemaining, int bufferSize, CancellationToken cancel) + : this(source, destination, bytesRemaining, new byte[bufferSize], cancel) + { + } + + #endregion 构造函数 + + #region 方法 + + /// + /// The CopyToAsync + /// + /// The source + /// The destination + /// The count + /// The bufferSize + /// The cancel + /// The public static async Task CopyToAsync(Stream source, Stream destination, long? count, int bufferSize, CancellationToken cancel) { long? bytesRemaining = count; @@ -85,7 +167,7 @@ public static async Task CopyToAsync(Stream source, Stream destination, long? co { bytesRemaining -= read; } - + if (read <= 0) { return; @@ -102,17 +184,20 @@ public static async Task CopyToAsync(Stream source, Stream destination, long? co } } + /// + /// The Start + /// + /// The internal Task Start() { ReadNextSegment(); return _tcs.Task; } - private void Complete() - { - _tcs.TrySetResult(null); - } - + /// + /// The CheckCancelled + /// + /// The private bool CheckCancelled() { if (_cancel.IsCancellationRequested) @@ -123,11 +208,49 @@ private bool CheckCancelled() return false; } + /// + /// The Complete + /// + private void Complete() + { + _tcs.TrySetResult(null); + } + + /// + /// The Fail + /// + /// The ex private void Fail(Exception ex) { _tcs.TrySetException(ex); } + /// + /// The ReadCallback + /// + /// The async + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Redirecting")] + private void ReadCallback(IAsyncResult async) + { + if (async.CompletedSynchronously) + { + return; + } + + try + { + int read = _source.EndRead(async); + WriteToOutputStream(read); + } + catch (Exception ex) + { + Fail(ex); + } + } + + /// + /// The ReadNextSegment + /// [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Redirecting")] private void ReadNextSegment() { @@ -164,8 +287,12 @@ private void ReadNextSegment() } } + /// + /// The WriteCallback + /// + /// The async [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Redirecting")] - private void ReadCallback(IAsyncResult async) + private void WriteCallback(IAsyncResult async) { if (async.CompletedSynchronously) { @@ -174,8 +301,8 @@ private void ReadCallback(IAsyncResult async) try { - int read = _source.EndRead(async); - WriteToOutputStream(read); + _destination.EndWrite(async); + ReadNextSegment(); } catch (Exception ex) { @@ -183,6 +310,10 @@ private void ReadCallback(IAsyncResult async) } } + /// + /// The WriteToOutputStream + /// + /// The count [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Redirecting")] private void WriteToOutputStream(int count) { @@ -190,7 +321,7 @@ private void WriteToOutputStream(int count) { _bytesRemaining -= count; } - + if (count == 0) { Complete(); @@ -217,23 +348,6 @@ private void WriteToOutputStream(int count) } } - [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Redirecting")] - private void WriteCallback(IAsyncResult async) - { - if (async.CompletedSynchronously) - { - return; - } - - try - { - _destination.EndWrite(async); - ReadNextSegment(); - } - catch (Exception ex) - { - Fail(ex); - } - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/KestrelHttpMessageListener.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/KestrelHttpMessageListener.cs index 6cc751c9f..41b9b0cee 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/KestrelHttpMessageListener.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/KestrelHttpMessageListener.cs @@ -2,41 +2,90 @@ using Autofac.Extensions.DependencyInjection; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; +using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Logging; using Surging.Core.CPlatform; using Surging.Core.CPlatform.Engines; using Surging.Core.CPlatform.Module; +using Surging.Core.CPlatform.Routing; using Surging.Core.CPlatform.Runtime.Server; using Surging.Core.CPlatform.Serialization; using Surging.Core.KestrelHttpServer.Extensions; -using Surging.Core.KestrelHttpServer.Internal; +using Surging.Core.KestrelHttpServer.Internal; using System; using System.IO; using System.Linq; using System.Net; using System.Reflection; using System.Threading.Tasks; -using Surging.Core.CPlatform.Routing; -using Microsoft.AspNetCore.Server.Kestrel.Core; namespace Surging.Core.KestrelHttpServer { + /// + /// Defines the + /// public class KestrelHttpMessageListener : HttpMessageListener, IDisposable { - private readonly ILogger _logger; - private IWebHost _host; - private bool _isCompleted; - private readonly ISerializer _serializer; + #region 字段 + + /// + /// Defines the _container + /// + private readonly CPlatformContainer _container; + + /// + /// Defines the _lifetime + /// private readonly IServiceEngineLifetime _lifetime; + + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the _moduleProvider + /// private readonly IModuleProvider _moduleProvider; - private readonly CPlatformContainer _container; + + /// + /// Defines the _serializer + /// + private readonly ISerializer _serializer; + + /// + /// Defines the _serviceRouteProvider + /// private readonly IServiceRouteProvider _serviceRouteProvider; + /// + /// Defines the _host + /// + private IWebHost _host; + + /// + /// Defines the _isCompleted + /// + private bool _isCompleted; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The logger + /// The serializer + /// The lifetime + /// The moduleProvider + /// The serviceRouteProvider + /// The container public KestrelHttpMessageListener(ILogger logger, - ISerializer serializer, + ISerializer serializer, IServiceEngineLifetime lifetime, IModuleProvider moduleProvider, IServiceRouteProvider serviceRouteProvider, @@ -50,18 +99,63 @@ public KestrelHttpMessageListener(ILogger logger, _serviceRouteProvider = serviceRouteProvider; } - public async Task StartAsync(IPAddress address,int? port) - { + #endregion 构造函数 + + #region 方法 + + /// + /// The ConfigureHost + /// + /// The context + /// The options + /// The ipAddress + public void ConfigureHost(WebHostBuilderContext context, KestrelServerOptions options, IPAddress ipAddress) + { + _moduleProvider.ConfigureHost(new WebHostContext(context, options, ipAddress)); + } + + /// + /// The ConfigureServices + /// + /// The services + public void ConfigureServices(IServiceCollection services) + { + var builder = new ContainerBuilder(); + services.AddMvc(); + _moduleProvider.ConfigureServices(new ConfigurationContext(services, + _moduleProvider.Modules, + _moduleProvider.VirtualPaths, + AppConfig.Configuration)); + builder.Populate(services); + + builder.Update(_container.Current.ComponentRegistry); + } + + /// + /// The Dispose + /// + public void Dispose() + { + _host.Dispose(); + } + + /// + /// The StartAsync + /// + /// The address + /// The port + /// The + public async Task StartAsync(IPAddress address, int? port) + { try { var hostBuilder = new WebHostBuilder() .UseContentRoot(Directory.GetCurrentDirectory()) - .UseKestrel((context,options) => + .UseKestrel((context, options) => { - if (port!=null && port >0) - options.Listen(address,port.Value); + if (port != null && port > 0) + options.Listen(address, port.Value); ConfigureHost(context, options, address); - }) .ConfigureServices(ConfigureServices) .ConfigureLogging((logger) => @@ -78,35 +172,19 @@ public async Task StartAsync(IPAddress address,int? port) { await _host.RunAsync(); }); - } catch { _logger.LogError($"http服务主机启动失败,监听地址:{address}:{port}。 "); } - - } - - public void ConfigureHost(WebHostBuilderContext context, KestrelServerOptions options,IPAddress ipAddress) - { - _moduleProvider.ConfigureHost(new WebHostContext(context, options, ipAddress)); - } - - public void ConfigureServices(IServiceCollection services) - { - var builder = new ContainerBuilder(); - services.AddMvc(); - _moduleProvider.ConfigureServices(new ConfigurationContext(services, - _moduleProvider.Modules, - _moduleProvider.VirtualPaths, - AppConfig.Configuration)); - builder.Populate(services); - - builder.Update(_container.Current.ComponentRegistry); } + /// + /// The AppResolve + /// + /// The app private void AppResolve(IApplicationBuilder app) - { + { app.UseStaticFiles(); app.UseMvc(); _moduleProvider.Initialize(new ApplicationInitializationContext(app, _moduleProvider.Modules, @@ -119,10 +197,6 @@ private void AppResolve(IApplicationBuilder app) }); } - public void Dispose() - { - _host.Dispose(); - } - + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/KestrelHttpModule.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/KestrelHttpModule.cs index 68ca9b651..69036215e 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/KestrelHttpModule.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/KestrelHttpModule.cs @@ -12,25 +12,46 @@ namespace Surging.Core.KestrelHttpServer { + /// + /// Defines the + /// public class KestrelHttpModule : EnginePartModule { - public override void Initialize(AppModuleContext context) - { - base.Initialize(context); - } + #region 方法 + /// + /// The Initialize + /// + /// The builder public virtual void Initialize(ApplicationInitializationContext builder) { } - public virtual void RegisterBuilder(WebHostContext context) + /// + /// The Initialize + /// + /// The context + public override void Initialize(AppModuleContext context) { + base.Initialize(context); } + /// + /// The RegisterBuilder + /// + /// The context public virtual void RegisterBuilder(ConfigurationContext context) { } + /// + /// The RegisterBuilder + /// + /// The context + public virtual void RegisterBuilder(WebHostContext context) + { + } + /// /// Inject dependent third-party components /// @@ -50,6 +71,10 @@ protected override void RegisterBuilder(ContainerBuilderWrapper builder) } } + /// + /// The RegisterDefaultProtocol + /// + /// The builder private static void RegisterDefaultProtocol(ContainerBuilderWrapper builder) { builder.Register(provider => @@ -73,10 +98,13 @@ private static void RegisterDefaultProtocol(ContainerBuilderWrapper builder) await messageListener.StartAsync(address?.Address, address?.Port); return messageListener; }, executor, messageListener); - }).As(); } + /// + /// The RegisterHttpProtocol + /// + /// The builder private static void RegisterHttpProtocol(ContainerBuilderWrapper builder) { builder.Register(provider => @@ -98,11 +126,12 @@ private static void RegisterHttpProtocol(ContainerBuilderWrapper builder) return new HttpServiceHost(async endPoint => { var address = endPoint as IPEndPoint; - await messageListener.StartAsync(address?.Address,address?.Port); + await messageListener.StartAsync(address?.Address, address?.Port); return messageListener; }, executor, messageListener); - }).As(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/StatusCode.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/StatusCode.cs index 3296cfaec..671501d87 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/StatusCode.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/StatusCode.cs @@ -4,10 +4,28 @@ namespace Surging.Core.KestrelHttpServer { - public enum StatusCode + #region 枚举 + + /// + /// Defines the StatusCode + /// + public enum StatusCode { + /// + /// Defines the Success + /// Success = 200, + + /// + /// Defines the RequestError + /// RequestError = 400, + + /// + /// Defines the AuthorizationFailed + /// AuthorizationFailed = 401, } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.KestrelHttpServer/WebHostContext.cs b/src/Surging.Core/Surging.Core.KestrelHttpServer/WebHostContext.cs index 300a46eec..b233e9161 100644 --- a/src/Surging.Core/Surging.Core.KestrelHttpServer/WebHostContext.cs +++ b/src/Surging.Core/Surging.Core.KestrelHttpServer/WebHostContext.cs @@ -8,8 +8,19 @@ namespace Surging.Core.KestrelHttpServer { + /// + /// Defines the + /// public class WebHostContext { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The context + /// The options + /// The ipAddress public WebHostContext(WebHostBuilderContext context, KestrelServerOptions options, IPAddress ipAddress) { WebHostBuilderContext = Check.NotNull(context, nameof(context)); @@ -17,11 +28,25 @@ public WebHostContext(WebHostBuilderContext context, KestrelServerOptions option Address = ipAddress; } - public WebHostBuilderContext WebHostBuilderContext { get; } + #endregion 构造函数 - public KestrelServerOptions KestrelOptions { get; } + #region 属性 + /// + /// Gets the Address + /// public IPAddress Address { get; } + /// + /// Gets the KestrelOptions + /// + public KestrelServerOptions KestrelOptions { get; } + + /// + /// Gets the WebHostBuilderContext + /// + public WebHostBuilderContext WebHostBuilderContext { get; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Log4net/Log4NetLogger.cs b/src/Surging.Core/Surging.Core.Log4net/Log4NetLogger.cs index 355c053b2..169b7b456 100644 --- a/src/Surging.Core/Surging.Core.Log4net/Log4NetLogger.cs +++ b/src/Surging.Core/Surging.Core.Log4net/Log4NetLogger.cs @@ -11,12 +11,42 @@ namespace Surging.Core.Log4net { + /// + /// Defines the + /// public class Log4NetLogger : Microsoft.Extensions.Logging.ILogger { + #region 字段 + + /// + /// Defines the _log + /// + private readonly ILog _log; + + /// + /// Defines the _name + /// private readonly string _name; + + /// + /// Defines the _xmlElement + /// private readonly XmlElement _xmlElement; - private readonly ILog _log; - private ILoggerRepository _loggerRepository; + + /// + /// Defines the _loggerRepository + /// + private ILoggerRepository _loggerRepository; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The name + /// The xmlElement public Log4NetLogger(string name, XmlElement xmlElement) { _name = name; @@ -27,11 +57,26 @@ public Log4NetLogger(string name, XmlElement xmlElement) log4net.Config.XmlConfigurator.Configure(_loggerRepository, xmlElement); } + #endregion 构造函数 + + #region 方法 + + /// + /// The BeginScope + /// + /// + /// The state + /// The public IDisposable BeginScope(TState state) { return NoopDisposable.Instance; } + /// + /// The IsEnabled + /// + /// The logLevel + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsEnabled(LogLevel logLevel) { @@ -39,20 +84,34 @@ public bool IsEnabled(LogLevel logLevel) { case LogLevel.Critical: return _log.IsFatalEnabled; + case LogLevel.Debug: case LogLevel.Trace: return _log.IsDebugEnabled; + case LogLevel.Error: return _log.IsErrorEnabled; + case LogLevel.Information: return _log.IsInfoEnabled; + case LogLevel.Warning: return _log.IsWarnEnabled; + default: throw new ArgumentOutOfRangeException(nameof(logLevel)); } } + /// + /// The Log + /// + /// + /// The logLevel + /// The eventId + /// The state + /// The exception + /// The formatter public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { @@ -77,19 +136,24 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, case LogLevel.Critical: _log.Fatal(message); break; + case LogLevel.Debug: case LogLevel.Trace: _log.Debug(message); break; + case LogLevel.Error: _log.Error(message, exception); break; + case LogLevel.Information: _log.Info(message); break; + case LogLevel.Warning: _log.Warn(message); break; + default: _log.Warn($"遇到未知日志级别{logLevel}"); _log.Info(message, exception); @@ -98,14 +162,32 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, } } + #endregion 方法 + + /// + /// Defines the + /// private class NoopDisposable : IDisposable { + #region 字段 + + /// + /// Defines the Instance + /// public static NoopDisposable Instance = new NoopDisposable(); + #endregion 字段 + + #region 方法 + + /// + /// The Dispose + /// public void Dispose() { } + + #endregion 方法 } } -} - +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Log4net/Log4NetProvider.cs b/src/Surging.Core/Surging.Core.Log4net/Log4NetProvider.cs index 8e0a9b37a..2d073c38e 100644 --- a/src/Surging.Core/Surging.Core.Log4net/Log4NetProvider.cs +++ b/src/Surging.Core/Surging.Core.Log4net/Log4NetProvider.cs @@ -1,5 +1,4 @@ - -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -9,37 +8,83 @@ namespace Surging.Core.Log4net { + /// + /// Defines the + /// public class Log4NetProvider : ILoggerProvider { + #region 字段 + + /// + /// Defines the _log4NetConfigFile + /// private readonly string _log4NetConfigFile; + + /// + /// Defines the _loggers + /// private readonly ConcurrentDictionary _loggers = new ConcurrentDictionary(); + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The log4NetConfigFile public Log4NetProvider(string log4NetConfigFile) { _log4NetConfigFile = log4NetConfigFile; } + #endregion 构造函数 + + #region 方法 + + /// + /// The CreateLogger + /// + /// The categoryName + /// The public ILogger CreateLogger(string categoryName) { return _loggers.GetOrAdd(categoryName, CreateLoggerImplementation); } + /// + /// The Dispose + /// public void Dispose() { _loggers.Clear(); } - private Log4NetLogger CreateLoggerImplementation(string name) - { - return new Log4NetLogger(name, Parselog4NetConfigFile(_log4NetConfigFile)); - } + /// + /// The Parselog4NetConfigFile + /// + /// The filename + /// The private static XmlElement Parselog4NetConfigFile(string filename) { XmlDocument log4netConfig = new XmlDocument(); - var stream= File.OpenRead(filename); + var stream = File.OpenRead(filename); log4netConfig.Load(stream); stream.Close(); return log4netConfig["log4net"]; } + + /// + /// The CreateLoggerImplementation + /// + /// The name + /// The + private Log4NetLogger CreateLoggerImplementation(string name) + { + return new Log4NetLogger(name, Parselog4NetConfigFile(_log4NetConfigFile)); + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Log4net/Log4netModule.cs b/src/Surging.Core/Surging.Core.Log4net/Log4netModule.cs index bbffd8721..177be5f2b 100644 --- a/src/Surging.Core/Surging.Core.Log4net/Log4netModule.cs +++ b/src/Surging.Core/Surging.Core.Log4net/Log4netModule.cs @@ -1,13 +1,30 @@ -using Surging.Core.CPlatform; +using Microsoft.Extensions.Logging; +using Surging.Core.CPlatform; using Surging.Core.CPlatform.Module; using Surging.Core.CPlatform.Utilities; -using Microsoft.Extensions.Logging; namespace Surging.Core.Log4net { - public class Log4netModule : EnginePartModule + /// + /// Defines the + /// + public class Log4netModule : EnginePartModule { + #region 字段 + + /// + /// Defines the log4NetConfigFile + /// private string log4NetConfigFile = "${LogPath}|log4net.config"; + + #endregion 字段 + + #region 方法 + + /// + /// The Initialize + /// + /// The context public override void Initialize(AppModuleContext context) { var serviceProvider = context.ServiceProvoider; @@ -24,7 +41,8 @@ public override void Initialize(AppModuleContext context) protected override void RegisterBuilder(ContainerBuilderWrapper builder) { base.RegisterBuilder(builder); - } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Log4net/ServiceHostBuilderExtensions.cs b/src/Surging.Core/Surging.Core.Log4net/ServiceHostBuilderExtensions.cs index eeb430026..2d4654c54 100644 --- a/src/Surging.Core/Surging.Core.Log4net/ServiceHostBuilderExtensions.cs +++ b/src/Surging.Core/Surging.Core.Log4net/ServiceHostBuilderExtensions.cs @@ -7,23 +7,40 @@ namespace Surging.Core.Log4net { - public static class ServiceHostBuilderExtensions + /// + /// Defines the + /// + public static class ServiceHostBuilderExtensions { - public static IServiceHostBuilder UseLog4net(this IServiceHostBuilder hostBuilder,string log4NetConfigFile= "log4net.config") + #region 方法 + + /// + /// The UseLog4net + /// + /// The hostBuilder + /// The filter + /// The log4NetConfigFile + /// The + public static IServiceHostBuilder UseLog4net(this IServiceHostBuilder hostBuilder, Func filter, string log4NetConfigFile = "log4net.config") { hostBuilder.ConfigureLogging(logger => { - logger.AddConfiguration(CPlatform.AppConfig.GetSection("Logging")); + logger.AddFilter(filter); }); return hostBuilder.MapServices(mapper => { - - var section = CPlatform.AppConfig.GetSection("Logging"); log4NetConfigFile = EnvironmentHelper.GetEnvironmentVariable(log4NetConfigFile); mapper.Resolve().AddProvider(new Log4NetProvider(log4NetConfigFile)); }); } + /// + /// The UseLog4net + /// + /// The hostBuilder + /// The minLevel + /// The log4NetConfigFile + /// The public static IServiceHostBuilder UseLog4net(this IServiceHostBuilder hostBuilder, LogLevel minLevel, string log4NetConfigFile = "log4net.config") { hostBuilder.ConfigureLogging(logger => @@ -37,17 +54,26 @@ public static IServiceHostBuilder UseLog4net(this IServiceHostBuilder hostBuilde }); } - public static IServiceHostBuilder UseLog4net(this IServiceHostBuilder hostBuilder, Func filter, string log4NetConfigFile = "log4net.config") + /// + /// The UseLog4net + /// + /// The hostBuilder + /// The log4NetConfigFile + /// The + public static IServiceHostBuilder UseLog4net(this IServiceHostBuilder hostBuilder, string log4NetConfigFile = "log4net.config") { hostBuilder.ConfigureLogging(logger => { - logger.AddFilter(filter); + logger.AddConfiguration(CPlatform.AppConfig.GetSection("Logging")); }); return hostBuilder.MapServices(mapper => { + var section = CPlatform.AppConfig.GetSection("Logging"); log4NetConfigFile = EnvironmentHelper.GetEnvironmentVariable(log4NetConfigFile); mapper.Resolve().AddProvider(new Log4NetProvider(log4NetConfigFile)); }); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.NLog/NLogModule.cs b/src/Surging.Core/Surging.Core.NLog/NLogModule.cs index eee3dca34..a5cff269c 100644 --- a/src/Surging.Core/Surging.Core.NLog/NLogModule.cs +++ b/src/Surging.Core/Surging.Core.NLog/NLogModule.cs @@ -8,9 +8,26 @@ namespace Surging.Core.Nlog { + /// + /// Defines the + /// public class NLogModule : EnginePartModule { + #region 字段 + + /// + /// Defines the nlogConfigFile + /// private string nlogConfigFile = "${LogPath}|NLog.config"; + + #endregion 字段 + + #region 方法 + + /// + /// The Initialize + /// + /// The context public override void Initialize(AppModuleContext context) { var serviceProvider = context.ServiceProvoider; @@ -28,7 +45,8 @@ public override void Initialize(AppModuleContext context) protected override void RegisterBuilder(ContainerBuilderWrapper builder) { base.RegisterBuilder(builder); - } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.NLog/NLogProvider.cs b/src/Surging.Core/Surging.Core.NLog/NLogProvider.cs index f01cda8bb..9e68e83fb 100644 --- a/src/Surging.Core/Surging.Core.NLog/NLogProvider.cs +++ b/src/Surging.Core/Surging.Core.NLog/NLogProvider.cs @@ -6,28 +6,62 @@ namespace Surging.Core.Nlog { + /// + /// Defines the + /// public class NLogProvider : ILoggerProvider { + #region 字段 + + /// + /// Defines the _loggers + /// private readonly ConcurrentDictionary _loggers = new ConcurrentDictionary(); + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public NLogProvider() { } + #endregion 构造函数 + + #region 方法 + + /// + /// The CreateLogger + /// + /// The categoryName + /// The public ILogger CreateLogger(string categoryName) { return _loggers.GetOrAdd(categoryName, CreateLoggerImplementation); } - private NLogger CreateLoggerImplementation(string name) + /// + /// The Dispose + /// + public void Dispose() { - return new NLogger(name); + _loggers.Clear(); } - public void Dispose() + /// + /// The CreateLoggerImplementation + /// + /// The name + /// The + private NLogger CreateLoggerImplementation(string name) { - _loggers.Clear(); + return new NLogger(name); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.NLog/NLogger.cs b/src/Surging.Core/Surging.Core.NLog/NLogger.cs index 192d1b9d9..63e5af0dc 100644 --- a/src/Surging.Core/Surging.Core.NLog/NLogger.cs +++ b/src/Surging.Core/Surging.Core.NLog/NLogger.cs @@ -1,23 +1,54 @@ using Microsoft.Extensions.Logging; - using System; using System.Runtime.CompilerServices; namespace Surging.Core.Nlog { + /// + /// Defines the + /// public class NLogger : Microsoft.Extensions.Logging.ILogger { + #region 字段 + + /// + /// Defines the _log + /// private readonly NLog.Logger _log; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The name public NLogger(string name) - { + { _log = NLog.LogManager.GetLogger(name); } + #endregion 构造函数 + + #region 方法 + + /// + /// The BeginScope + /// + /// + /// The state + /// The public IDisposable BeginScope(TState state) { return NoopDisposable.Instance; } + /// + /// The IsEnabled + /// + /// The logLevel + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsEnabled(Microsoft.Extensions.Logging.LogLevel logLevel) { @@ -25,21 +56,36 @@ public bool IsEnabled(Microsoft.Extensions.Logging.LogLevel logLevel) { case LogLevel.Critical: return _log.IsFatalEnabled; + case LogLevel.Debug: return _log.IsDebugEnabled; + case LogLevel.Trace: return _log.IsTraceEnabled; + case LogLevel.Error: return _log.IsErrorEnabled; + case LogLevel.Information: return _log.IsInfoEnabled; + case LogLevel.Warning: return _log.IsWarnEnabled; + default: throw new ArgumentOutOfRangeException(nameof(logLevel)); } } + /// + /// The Log + /// + /// + /// The logLevel + /// The eventId + /// The state + /// The exception + /// The formatter public void Log(Microsoft.Extensions.Logging.LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { @@ -64,37 +110,61 @@ public void Log(Microsoft.Extensions.Logging.LogLevel logLevel, EventId case LogLevel.Critical: _log.Fatal(message); break; + case LogLevel.Debug: _log.Debug(message); break; + case LogLevel.Trace: _log.Trace(message); break; + case LogLevel.Error: - _log.Error(message, exception,null); + _log.Error(message, exception, null); break; + case LogLevel.Information: _log.Info(message); break; + case LogLevel.Warning: _log.Warn(message); break; + default: _log.Warn($"遇到未知日志级别{logLevel}"); - _log.Info(message, exception,null); + _log.Info(message, exception, null); break; } } } + #endregion 方法 + + /// + /// Defines the + /// private class NoopDisposable : IDisposable { + #region 字段 + + /// + /// Defines the Instance + /// public static NoopDisposable Instance = new NoopDisposable(); + #endregion 字段 + + #region 方法 + + /// + /// The Dispose + /// public void Dispose() { - } + + #endregion 方法 } } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.NLog/ServiceHostBuilderExtensions.cs b/src/Surging.Core/Surging.Core.NLog/ServiceHostBuilderExtensions.cs index 84ec4263b..71eddedf5 100644 --- a/src/Surging.Core/Surging.Core.NLog/ServiceHostBuilderExtensions.cs +++ b/src/Surging.Core/Surging.Core.NLog/ServiceHostBuilderExtensions.cs @@ -9,23 +9,41 @@ namespace Surging.Core.Nlog { - public static class ServiceHostBuilderExtensions + /// + /// Defines the + /// + public static class ServiceHostBuilderExtensions { - public static IServiceHostBuilder UseNLog(this IServiceHostBuilder hostBuilder, string nlogConfigFile = "nLog.config") + #region 方法 + + /// + /// The UseNLog + /// + /// The hostBuilder + /// The filter + /// The nlogConfigFile + /// The + public static IServiceHostBuilder UseNLog(this IServiceHostBuilder hostBuilder, Func filter, string nlogConfigFile = "nLog.config") { hostBuilder.ConfigureLogging(logger => { - logger.AddConfiguration(CPlatform.AppConfig.GetSection("Logging")); + logger.AddFilter(filter); }); return hostBuilder.MapServices(mapper => { - var section = CPlatform.AppConfig.GetSection("Logging"); nlogConfigFile = EnvironmentHelper.GetEnvironmentVariable(nlogConfigFile); NLog.LogManager.LoadConfiguration(nlogConfigFile); mapper.Resolve().AddProvider(new NLogProvider()); }); } + /// + /// The UseNLog + /// + /// The hostBuilder + /// The minLevel + /// The nlogConfigFile + /// The public static IServiceHostBuilder UseNLog(this IServiceHostBuilder hostBuilder, LogLevel minLevel, string nlogConfigFile = "nLog.config") { hostBuilder.ConfigureLogging(logger => @@ -34,24 +52,33 @@ public static IServiceHostBuilder UseNLog(this IServiceHostBuilder hostBuilder, }); return hostBuilder.MapServices(mapper => { - nlogConfigFile =EnvironmentHelper.GetEnvironmentVariable(nlogConfigFile); + nlogConfigFile = EnvironmentHelper.GetEnvironmentVariable(nlogConfigFile); NLog.LogManager.LoadConfiguration(nlogConfigFile); mapper.Resolve().AddProvider(new NLogProvider()); }); } - public static IServiceHostBuilder UseNLog(this IServiceHostBuilder hostBuilder, Func filter, string nlogConfigFile = "nLog.config") + /// + /// The UseNLog + /// + /// The hostBuilder + /// The nlogConfigFile + /// The + public static IServiceHostBuilder UseNLog(this IServiceHostBuilder hostBuilder, string nlogConfigFile = "nLog.config") { hostBuilder.ConfigureLogging(logger => { - logger.AddFilter(filter); + logger.AddConfiguration(CPlatform.AppConfig.GetSection("Logging")); }); return hostBuilder.MapServices(mapper => { + var section = CPlatform.AppConfig.GetSection("Logging"); nlogConfigFile = EnvironmentHelper.GetEnvironmentVariable(nlogConfigFile); NLog.LogManager.LoadConfiguration(nlogConfigFile); mapper.Resolve().AddProvider(new NLogProvider()); }); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Http/ContainerBuilderExtensions.cs b/src/Surging.Core/Surging.Core.Protocol.Http/ContainerBuilderExtensions.cs index 69eb8a5ee..c82fe1da6 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Http/ContainerBuilderExtensions.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Http/ContainerBuilderExtensions.cs @@ -9,8 +9,13 @@ namespace Surging.Core.Protocol.Http { + /// + /// Defines the + /// public static class ContainerBuilderExtensions { + #region 方法 + /// /// 添加http协议 /// @@ -32,6 +37,10 @@ public static IServiceBuilder AddHttpProtocol(this IServiceBuilder builder) return builder; } + /// + /// The RegisterDefaultProtocol + /// + /// The builder private static void RegisterDefaultProtocol(ContainerBuilder builder) { builder.Register(provider => @@ -44,7 +53,6 @@ private static void RegisterDefaultProtocol(ContainerBuilder builder) }).SingleInstance(); builder.Register(provider => { - var serviceExecutor = provider.ResolveKeyed(CommunicationProtocol.Http.ToString()); var messageListener = provider.Resolve(); return new DefaultServiceHost(async endPoint => @@ -52,13 +60,15 @@ private static void RegisterDefaultProtocol(ContainerBuilder builder) await messageListener.StartAsync(endPoint); return messageListener; }, serviceExecutor); - }).As(); } + /// + /// The RegisterHttpProtocol + /// + /// The builder private static void RegisterHttpProtocol(ContainerBuilder builder) { - builder.Register(provider => { return new DotNettyHttpServerMessageListener(provider.Resolve>(), @@ -76,8 +86,9 @@ private static void RegisterHttpProtocol(ContainerBuilder builder) await messageListener.StartAsync(endPoint); return messageListener; }, serviceExecutor); - }).As(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Http/DotNettyHttpServerMessageListener.cs b/src/Surging.Core/Surging.Core.Protocol.Http/DotNettyHttpServerMessageListener.cs index 6613d5948..211e89c6d 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Http/DotNettyHttpServerMessageListener.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Http/DotNettyHttpServerMessageListener.cs @@ -21,24 +21,57 @@ namespace Surging.Core.Protocol.Http { - class DotNettyHttpServerMessageListener : IMessageListener, IDisposable + /// + /// Defines the + /// + internal class DotNettyHttpServerMessageListener : IMessageListener, IDisposable { - #region Field + #region 字段 + /// + /// Defines the _logger + /// private readonly ILogger _logger; + + /// + /// Defines the _serializer + /// + private readonly ISerializer _serializer; + + /// + /// Defines the _serviceRouteProvider + /// + private readonly IServiceRouteProvider _serviceRouteProvider; + + /// + /// Defines the _transportMessageDecoder + /// private readonly ITransportMessageDecoder _transportMessageDecoder; + + /// + /// Defines the _transportMessageEncoder + /// private readonly ITransportMessageEncoder _transportMessageEncoder; + + /// + /// Defines the _channel + /// private IChannel _channel; - private readonly ISerializer _serializer; - private readonly IServiceRouteProvider _serviceRouteProvider; - #endregion Field + #endregion 字段 - #region Constructor + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The logger + /// The codecFactory + /// The serializer + /// The serviceRouteProvider public DotNettyHttpServerMessageListener(ILogger logger, - ITransportMessageCodecFactory codecFactory, - ISerializer serializer, + ITransportMessageCodecFactory codecFactory, + ISerializer serializer, IServiceRouteProvider serviceRouteProvider) { _logger = logger; @@ -48,12 +81,42 @@ public DotNettyHttpServerMessageListener(ILogger + /// Defines the Received + /// public event ReceivedDelegate Received; + #endregion 事件 + + #region 方法 + + /// + /// The CloseAsync + /// + public void CloseAsync() + { + Task.Run(async () => + { + await _channel.EventLoop.ShutdownGracefullyAsync(); + await _channel.CloseAsync(); + }).Wait(); + } + + /// + /// The Dispose + /// + public void Dispose() + { + Task.Run(async () => + { + await _channel.DisconnectAsync(); + }).Wait(); + } + /// /// 触发接收到消息事件。 /// @@ -67,8 +130,11 @@ public async Task OnReceived(IMessageSender sender, TransportMessage message) await Received(sender, message); } - #endregion Implementation of IMessageListener - + /// + /// The StartAsync + /// + /// The endPoint + /// The public async Task StartAsync(EndPoint endPoint) { if (_logger.IsEnabled(LogLevel.Debug)) @@ -106,43 +172,55 @@ public async Task StartAsync(EndPoint endPoint) { _logger.LogError($"Http服务主机启动失败,监听地址:{endPoint}。 "); } - - } - - public void CloseAsync() - { - Task.Run(async () => - { - await _channel.EventLoop.ShutdownGracefullyAsync(); - await _channel.CloseAsync(); - }).Wait(); } - #region Implementation of IDisposable + #endregion 方法 - - public void Dispose() + /// + /// Defines the + /// + private class ServerHandler : SimpleChannelInboundHandler { - Task.Run(async () => - { - await _channel.DisconnectAsync(); - }).Wait(); - } + #region 字段 - #endregion Implementation of IDisposable + /// + /// Defines the completion + /// + internal readonly TaskCompletionSource completion = new TaskCompletionSource(); - #region Help Class - private class ServerHandler : SimpleChannelInboundHandler - { - readonly TaskCompletionSource completion = new TaskCompletionSource(); + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + /// + /// Defines the _readAction + /// private readonly Action _readAction; - private readonly ILogger _logger; + + /// + /// Defines the _serializer + /// private readonly ISerializer _serializer; + + /// + /// Defines the _serviceRouteProvider + /// private readonly IServiceRouteProvider _serviceRouteProvider; - public ServerHandler(Action readAction, - ILogger logger, + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The readAction + /// The logger + /// The serializer + /// The serviceRouteProvider + public ServerHandler(Action readAction, + ILogger logger, ISerializer serializer, IServiceRouteProvider serviceRouteProvider) { @@ -152,12 +230,53 @@ public ServerHandler(Action readAction _serviceRouteProvider = serviceRouteProvider; } + #endregion 构造函数 + + #region 方法 + + /// + /// The ExceptionCaught + /// + /// The context + /// The exception + public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) => this.completion.TrySetException(exception); + + /// + /// The GetParameters + /// + /// The msg + /// The routePath + /// The + public IDictionary GetParameters(string msg, out string routePath) + { + var urlSpan = msg.AsSpan(); + var len = urlSpan.IndexOf("?"); + if (len == -1) + { + routePath = urlSpan.TrimStart("/").ToString().ToLower(); + return new Dictionary(); + } + routePath = urlSpan.Slice(0, len).TrimStart("/").ToString().ToLower(); + var paramStr = urlSpan.Slice(len + 1).ToString(); + var parameters = paramStr.Split('&'); + return parameters.ToList().Select(p => p.Split("=")).ToDictionary(p => p[0].ToLower(), p => (object)p[1]); + } + + /// + /// The WaitForCompletion + /// + /// The public bool WaitForCompletion() { this.completion.Task.Wait(TimeSpan.FromSeconds(5)); return this.completion.Task.Status == TaskStatus.RanToCompletion; } + /// + /// The ChannelRead0 + /// + /// The ctx + /// The msg protected override void ChannelRead0(IChannelHandlerContext ctx, IFullHttpRequest msg) { var data = new byte[msg.Content.ReadableBytes]; @@ -175,7 +294,7 @@ protected override void ChannelRead0(IChannelHandlerContext ctx, IFullHttpReques var @params = RouteTemplateSegmenter.Segment(serviceRoute.ServiceDescriptor.RoutePath, path); foreach (var param in @params) { - parameters.Add(param.Key,param.Value); + parameters.Add(param.Key, param.Value); } } if (msg.Method.Name == "POST") @@ -199,24 +318,7 @@ protected override void ChannelRead0(IChannelHandlerContext ctx, IFullHttpReques }); } - public IDictionary GetParameters(string msg, out string routePath) - { - var urlSpan = msg.AsSpan(); - var len = urlSpan.IndexOf("?"); - if (len == -1) - { - routePath = urlSpan.TrimStart("/").ToString().ToLower(); - return new Dictionary(); - } - routePath = urlSpan.Slice(0, len).TrimStart("/").ToString().ToLower(); - var paramStr = urlSpan.Slice(len + 1).ToString(); - var parameters = paramStr.Split('&'); - return parameters.ToList().Select(p => p.Split("=")).ToDictionary(p => p[0].ToLower(), p => (object)p[1]); - } - - public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) => this.completion.TrySetException(exception); + #endregion 方法 } - - #endregion Help Class } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Http/DotNettyHttpServerMessageSender.cs b/src/Surging.Core/Surging.Core.Protocol.Http/DotNettyHttpServerMessageSender.cs index 1fe022693..b53e1d0e3 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Http/DotNettyHttpServerMessageSender.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Http/DotNettyHttpServerMessageSender.cs @@ -12,49 +12,95 @@ namespace Surging.Core.Protocol.Http { - public class DotNettyHttpServerMessageSender: DotNettyMessageSender, IMessageSender + /// + /// Defines the + /// + public class DotNettyHttpServerMessageSender : DotNettyMessageSender, IMessageSender { + #region 字段 + + /// + /// Defines the _context + /// private readonly IChannelHandlerContext _context; + + /// + /// Defines the _serializer + /// private readonly ISerializer _serializer; + + /// + /// Defines the ContentLengthEntity + /// + private readonly AsciiString ContentLengthEntity = HttpHeaderNames.ContentLength; + + /// + /// Defines the ContentTypeEntity + /// private readonly AsciiString ContentTypeEntity = HttpHeaderNames.ContentType; + + /// + /// Defines the ServerEntity + /// private readonly AsciiString ServerEntity = HttpHeaderNames.Server; - private readonly AsciiString ContentLengthEntity = HttpHeaderNames.ContentLength; + + /// + /// Defines the TypeJson + /// private readonly AsciiString TypeJson = AsciiString.Cached("application/json"); + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The transportMessageEncoder + /// The context + /// The serializer public DotNettyHttpServerMessageSender(ITransportMessageEncoder transportMessageEncoder, IChannelHandlerContext context, ISerializer serializer) : base(transportMessageEncoder) { _context = context; _serializer = serializer; } - #region Implementation of IMessageSender + #endregion 构造函数 + + #region 方法 /// - /// 发送消息。 + /// 发送消息并清空缓冲区。 /// /// 消息内容。 /// 一个任务。 - public async Task SendAsync(TransportMessage message) + public async Task SendAndFlushAsync(TransportMessage message) { var buffer = GetByteBuffer(message, out int contentLength); - var response = WriteResponse(_context, buffer, TypeJson, AsciiString.Cached($"{contentLength}")); - await _context.WriteAsync(response); + var response = WriteResponse(_context, buffer, TypeJson, AsciiString.Cached($"{ contentLength}")); + + await _context.WriteAndFlushAsync(response); + await _context.CloseAsync(); } /// - /// 发送消息并清空缓冲区。 + /// 发送消息。 /// /// 消息内容。 /// 一个任务。 - public async Task SendAndFlushAsync(TransportMessage message) + public async Task SendAsync(TransportMessage message) { var buffer = GetByteBuffer(message, out int contentLength); - var response = WriteResponse(_context, buffer, TypeJson, AsciiString.Cached($"{ contentLength}")); - - await _context.WriteAndFlushAsync(response); - await _context.CloseAsync(); + var response = WriteResponse(_context, buffer, TypeJson, AsciiString.Cached($"{contentLength}")); + await _context.WriteAsync(response); } + /// + /// The GetByteBuffer + /// + /// The message + /// The contentLength + /// The private IByteBuffer GetByteBuffer(TransportMessage message, out int contentLength) { contentLength = 0; @@ -67,6 +113,14 @@ private IByteBuffer GetByteBuffer(TransportMessage message, out int contentLengt return Unpooled.WrappedBuffer(data); } + /// + /// The WriteResponse + /// + /// The ctx + /// The buf + /// The contentType + /// The contentLength + /// The private DefaultFullHttpResponse WriteResponse(IChannelHandlerContext ctx, IByteBuffer buf, ICharSequence contentType, ICharSequence contentLength) { var response = new DefaultFullHttpResponse(HttpVersion.Http11, HttpResponseStatus.OK, buf, false); @@ -75,7 +129,7 @@ private DefaultFullHttpResponse WriteResponse(IChannelHandlerContext ctx, IByteB headers.Set(ContentLengthEntity, contentLength); return response; } - #endregion Implementation of IMessageSender - } -} + #endregion 方法 + } +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Http/HttpProtocolModule.cs b/src/Surging.Core/Surging.Core.Protocol.Http/HttpProtocolModule.cs index dc57f236c..361879daa 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Http/HttpProtocolModule.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Http/HttpProtocolModule.cs @@ -13,8 +13,17 @@ namespace Surging.Core.Protocol.Http { + /// + /// Defines the + /// public class HttpProtocolModule : EnginePartModule { + #region 方法 + + /// + /// The Initialize + /// + /// The context public override void Initialize(AppModuleContext context) { base.Initialize(context); @@ -39,6 +48,10 @@ protected override void RegisterBuilder(ContainerBuilderWrapper builder) } } + /// + /// The RegisterDefaultProtocol + /// + /// The builder private static void RegisterDefaultProtocol(ContainerBuilderWrapper builder) { builder.Register(provider => @@ -51,7 +64,6 @@ private static void RegisterDefaultProtocol(ContainerBuilderWrapper builder) }).SingleInstance(); builder.Register(provider => { - var serviceExecutor = provider.ResolveKeyed(CommunicationProtocol.Http.ToString()); var messageListener = provider.Resolve(); return new DefaultServiceHost(async endPoint => @@ -59,13 +71,15 @@ private static void RegisterDefaultProtocol(ContainerBuilderWrapper builder) await messageListener.StartAsync(endPoint); return messageListener; }, serviceExecutor); - }).As(); } + /// + /// The RegisterHttpProtocol + /// + /// The builder private static void RegisterHttpProtocol(ContainerBuilderWrapper builder) { - builder.Register(provider => { return new DotNettyHttpServerMessageListener(provider.Resolve>(), @@ -83,8 +97,9 @@ private static void RegisterHttpProtocol(ContainerBuilderWrapper builder) await messageListener.StartAsync(endPoint); return messageListener; }, serviceExecutor); - }).As(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Http/HttpServiceExecutor.cs b/src/Surging.Core/Surging.Core.Protocol.Http/HttpServiceExecutor.cs index a2b548af9..6debdaab2 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Http/HttpServiceExecutor.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Http/HttpServiceExecutor.cs @@ -18,22 +18,62 @@ namespace Surging.Core.Protocol.Http { + /// + /// Defines the + /// public class HttpServiceExecutor : IServiceExecutor { - #region Field + #region 字段 - private readonly IServiceEntryLocate _serviceEntryLocate; - private readonly ILogger _logger; - private readonly IServiceRouteProvider _serviceRouteProvider; + /// + /// Defines the _authorizationFilter + /// private readonly IAuthorizationFilter _authorizationFilter; + + /// + /// Defines the _concurrent + /// + private readonly ConcurrentDictionary> _concurrent = + new ConcurrentDictionary>(); + + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the _serviceEntryLocate + /// + private readonly IServiceEntryLocate _serviceEntryLocate; + + /// + /// Defines the _serviceProvider + /// private readonly CPlatformContainer _serviceProvider; + + /// + /// Defines the _serviceRouteProvider + /// + private readonly IServiceRouteProvider _serviceRouteProvider; + + /// + /// Defines the _typeConvertibleService + /// private readonly ITypeConvertibleService _typeConvertibleService; - private readonly ConcurrentDictionary> _concurrent = - new ConcurrentDictionary>(); - #endregion Field - #region Constructor + #endregion 字段 + + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The serviceEntryLocate + /// The serviceRouteProvider + /// The authorizationFilter + /// The logger + /// The serviceProvider + /// The typeConvertibleService public HttpServiceExecutor(IServiceEntryLocate serviceEntryLocate, IServiceRouteProvider serviceRouteProvider, IAuthorizationFilter authorizationFilter, ILogger logger, CPlatformContainer serviceProvider, ITypeConvertibleService typeConvertibleService) @@ -46,15 +86,16 @@ public HttpServiceExecutor(IServiceEntryLocate serviceEntryLocate, IServiceRoute _authorizationFilter = authorizationFilter; } - #endregion Constructor + #endregion 构造函数 - #region Implementation of IServiceExecutor + #region 方法 /// /// 执行。 /// /// 消息发送者。 /// 调用消息。 + /// The public async Task ExecuteAsync(IMessageSender sender, TransportMessage message) { if (_logger.IsEnabled(LogLevel.Trace)) @@ -95,41 +136,45 @@ public async Task ExecuteAsync(IMessageSender sender, TransportMessage message) await SendRemoteInvokeResult(sender, httpResultMessage); } - #endregion Implementation of IServiceExecutor + /// + /// The GetExceptionMessage + /// + /// The exception + /// The + private static string GetExceptionMessage(Exception exception) + { + if (exception == null) + return string.Empty; - #region Private Method + var message = exception.Message; + if (exception.InnerException != null) + { + message += "|InnerException:" + GetExceptionMessage(exception.InnerException); + } + return message; + } - private async Task> RemoteExecuteAsync(ServiceEntry entry, HttpMessage httpMessage) + /// + /// The LocalExecuteAsync + /// + /// The entry + /// The httpMessage + /// The + private async Task> LocalExecuteAsync(ServiceEntry entry, HttpMessage httpMessage) { HttpResultMessage resultMessage = new HttpResultMessage(); - var provider = _concurrent.GetValueOrDefault(httpMessage.RoutePath); - var list = new List(); - if (provider.Item1 == null) - { - provider.Item2 = ServiceLocator.GetService().CreateProxy(httpMessage.ServiceKey, entry.Type); - provider.Item3 = provider.Item2.GetType().GetTypeInfo().DeclaredMethods.Where(p => p.Name == entry.MethodName).FirstOrDefault(); ; - provider.Item1 = FastInvoke.GetMethodInvoker(provider.Item3); - _concurrent.GetOrAdd(httpMessage.RoutePath, ValueTuple.Create(provider.Item1, provider.Item2, provider.Item3)); - } - foreach (var parameterInfo in provider.Item3.GetParameters()) - { - var value = httpMessage.Parameters[parameterInfo.Name]; - var parameterType = parameterInfo.ParameterType; - var parameter = _typeConvertibleService.Convert(value, parameterType); - list.Add(parameter); - } try { - var methodResult = provider.Item1(provider.Item2, list.ToArray()); + var result = await entry.Func(httpMessage.ServiceKey, httpMessage.Parameters); + var task = result as Task; - var task = methodResult as Task; if (task == null) { - resultMessage.Entity = methodResult; + resultMessage.Entity = result; } else { - await task; + task.Wait(); var taskType = task.GetType().GetTypeInfo(); if (taskType.IsGenericType) resultMessage.Entity = taskType.GetProperty("Result").GetValue(task); @@ -137,30 +182,53 @@ private async Task> RemoteExecuteAsync(ServiceEntry en resultMessage.IsSucceed = resultMessage.Entity != null; resultMessage.StatusCode = resultMessage.IsSucceed ? (int)StatusCode.Success : (int)StatusCode.RequestError; } - catch (Exception ex) + catch (Exception exception) { if (_logger.IsEnabled(LogLevel.Error)) - _logger.LogError(ex, "执行远程调用逻辑时候发生了错误。"); - resultMessage = new HttpResultMessage { Entity = null, Message = "执行发生了错误。", StatusCode = (int)StatusCode.RequestError }; + _logger.LogError(exception, "执行本地逻辑时候发生了错误。"); + resultMessage.Message = "执行发生了错误。"; + resultMessage.StatusCode = exception.HResult; } return resultMessage; } - private async Task> LocalExecuteAsync(ServiceEntry entry, HttpMessage httpMessage) + /// + /// The RemoteExecuteAsync + /// + /// The entry + /// The httpMessage + /// The + private async Task> RemoteExecuteAsync(ServiceEntry entry, HttpMessage httpMessage) { HttpResultMessage resultMessage = new HttpResultMessage(); + var provider = _concurrent.GetValueOrDefault(httpMessage.RoutePath); + var list = new List(); + if (provider.Item1 == null) + { + provider.Item2 = ServiceLocator.GetService().CreateProxy(httpMessage.ServiceKey, entry.Type); + provider.Item3 = provider.Item2.GetType().GetTypeInfo().DeclaredMethods.Where(p => p.Name == entry.MethodName).FirstOrDefault(); ; + provider.Item1 = FastInvoke.GetMethodInvoker(provider.Item3); + _concurrent.GetOrAdd(httpMessage.RoutePath, ValueTuple.Create(provider.Item1, provider.Item2, provider.Item3)); + } + foreach (var parameterInfo in provider.Item3.GetParameters()) + { + var value = httpMessage.Parameters[parameterInfo.Name]; + var parameterType = parameterInfo.ParameterType; + var parameter = _typeConvertibleService.Convert(value, parameterType); + list.Add(parameter); + } try { - var result = await entry.Func(httpMessage.ServiceKey, httpMessage.Parameters); - var task = result as Task; + var methodResult = provider.Item1(provider.Item2, list.ToArray()); + var task = methodResult as Task; if (task == null) { - resultMessage.Entity = result; + resultMessage.Entity = methodResult; } else { - task.Wait(); + await task; var taskType = task.GetType().GetTypeInfo(); if (taskType.IsGenericType) resultMessage.Entity = taskType.GetProperty("Result").GetValue(task); @@ -168,16 +236,21 @@ private async Task> LocalExecuteAsync(ServiceEntry ent resultMessage.IsSucceed = resultMessage.Entity != null; resultMessage.StatusCode = resultMessage.IsSucceed ? (int)StatusCode.Success : (int)StatusCode.RequestError; } - catch (Exception exception) + catch (Exception ex) { if (_logger.IsEnabled(LogLevel.Error)) - _logger.LogError(exception, "执行本地逻辑时候发生了错误。"); - resultMessage.Message = "执行发生了错误。"; - resultMessage.StatusCode = exception.HResult; + _logger.LogError(ex, "执行远程调用逻辑时候发生了错误。"); + resultMessage = new HttpResultMessage { Entity = null, Message = "执行发生了错误。", StatusCode = (int)StatusCode.RequestError }; } return resultMessage; } + /// + /// The SendRemoteInvokeResult + /// + /// The sender + /// The resultMessage + /// The private async Task SendRemoteInvokeResult(IMessageSender sender, HttpResultMessage resultMessage) { try @@ -196,19 +269,6 @@ private async Task SendRemoteInvokeResult(IMessageSender sender, HttpResultMessa } } - private static string GetExceptionMessage(Exception exception) - { - if (exception == null) - return string.Empty; - - var message = exception.Message; - if (exception.InnerException != null) - { - message += "|InnerException:" + GetExceptionMessage(exception.InnerException); - } - return message; - } - - #endregion Private Method + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Http/HttpServiceHost.cs b/src/Surging.Core/Surging.Core.Protocol.Http/HttpServiceHost.cs index 174e10278..818c5e21f 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Http/HttpServiceHost.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Http/HttpServiceHost.cs @@ -13,23 +13,41 @@ namespace Surging.Core.Protocol.Http /// /// HTTP服务主机 /// - public class HttpServiceHost : ServiceHostAbstract + public class HttpServiceHost : ServiceHostAbstract { - #region Field + #region 字段 + /// + /// Defines the _messageListenerFactory + /// private readonly Func> _messageListenerFactory; + + /// + /// Defines the _serverMessageListener + /// private IMessageListener _serverMessageListener; - #endregion Field + #endregion 字段 + + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The messageListenerFactory + /// The serviceExecutor public HttpServiceHost(Func> messageListenerFactory, IServiceExecutor serviceExecutor) : base(serviceExecutor) { _messageListenerFactory = messageListenerFactory; } - #region Overrides of ServiceHostAbstract + #endregion 构造函数 + + #region 方法 - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + /// The Dispose + /// public override void Dispose() { (_serverMessageListener as IDisposable)?.Dispose(); @@ -54,11 +72,17 @@ await Task.Run(() => }; } - public override async Task StartAsync(string ip,int port) + /// + /// The StartAsync + /// + /// The ip + /// The port + /// The + public override async Task StartAsync(string ip, int port) { if (_serverMessageListener != null) return; - _serverMessageListener = await _messageListenerFactory(new IPEndPoint(IPAddress.Parse(ip), AppConfig.ServerOptions.Ports.HttpPort??0)); + _serverMessageListener = await _messageListenerFactory(new IPEndPoint(IPAddress.Parse(ip), AppConfig.ServerOptions.Ports.HttpPort ?? 0)); _serverMessageListener.Received += async (sender, message) => { await Task.Run(() => @@ -68,6 +92,6 @@ await Task.Run(() => }; } - #endregion Overrides of ServiceHostAbstract + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Http/StatusCode.cs b/src/Surging.Core/Surging.Core.Protocol.Http/StatusCode.cs index 9c52d5e72..d9be0598a 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Http/StatusCode.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Http/StatusCode.cs @@ -4,10 +4,28 @@ namespace Surging.Core.Protocol.Http { - public enum StatusCode + #region 枚举 + + /// + /// Defines the StatusCode + /// + public enum StatusCode { - Success=200, - RequestError =400, - AuthorizationFailed=401, + /// + /// Defines the Success + /// + Success = 200, + + /// + /// Defines the RequestError + /// + RequestError = 400, + + /// + /// Defines the AuthorizationFailed + /// + AuthorizationFailed = 401, } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/DotNettyMqttServerMessageListener.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/DotNettyMqttServerMessageListener.cs index a3c7bd41d..b2b9379b4 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/DotNettyMqttServerMessageListener.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/DotNettyMqttServerMessageListener.cs @@ -25,20 +25,44 @@ namespace Surging.Core.Protocol.Mqtt { + /// + /// Defines the + /// public class DotNettyMqttServerMessageListener : IMessageListener, IDisposable { - #region Field + #region 字段 - private readonly ILogger _logger; - private IChannel _channel; + /// + /// Defines the _channelService + /// private readonly IChannelService _channelService; + + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the _mqttBehaviorProvider + /// private readonly IMqttBehaviorProvider _mqttBehaviorProvider; - #endregion Field - public event ReceivedDelegate Received; + /// + /// Defines the _channel + /// + private IChannel _channel; + + #endregion 字段 + + #region 构造函数 - #region Constructor - public DotNettyMqttServerMessageListener(ILogger logger, + /// + /// Initializes a new instance of the class. + /// + /// The logger + /// The channelService + /// The mqttBehaviorProvider + public DotNettyMqttServerMessageListener(ILogger logger, IChannelService channelService, IMqttBehaviorProvider mqttBehaviorProvider) { @@ -46,13 +70,100 @@ public DotNettyMqttServerMessageListener(ILogger + /// Defines the Received + /// + public event ReceivedDelegate Received; + + #endregion 事件 + + #region 方法 + + /// + /// The ChannelWrite + /// + /// The context + /// The message + /// The packetType + /// The mqttHandlerService + /// The + public async Task ChannelWrite(IChannelHandlerContext context, object message, PacketType packetType, ServerMqttHandlerService mqttHandlerService) + { + switch (packetType) + { + case PacketType.CONNECT: + await mqttHandlerService.Login(context, message as ConnectPacket); + break; + + case PacketType.PUBLISH: + await mqttHandlerService.Publish(context, message as PublishPacket); + break; + + case PacketType.PUBACK: + await mqttHandlerService.PubAck(context, message as PubAckPacket); + break; + + case PacketType.PUBREC: + await mqttHandlerService.PubRec(context, message as PubRecPacket); + break; + + case PacketType.PUBREL: + await mqttHandlerService.PubRel(context, message as PubRelPacket); + break; + + case PacketType.PUBCOMP: + await mqttHandlerService.PubComp(context, message as PubCompPacket); + break; + + case PacketType.SUBSCRIBE: + await mqttHandlerService.Subscribe(context, message as SubscribePacket); + break; + + case PacketType.SUBACK: + await mqttHandlerService.SubAck(context, message as SubAckPacket); + break; + + case PacketType.UNSUBSCRIBE: + await mqttHandlerService.Unsubscribe(context, message as UnsubscribePacket); + break; + + case PacketType.UNSUBACK: + await mqttHandlerService.UnsubAck(context, message as UnsubAckPacket); + break; + + case PacketType.PINGREQ: + await mqttHandlerService.PingReq(context, message as PingReqPacket); + break; + + case PacketType.PINGRESP: + await mqttHandlerService.PingResp(context, message as PingRespPacket); + break; + + case PacketType.DISCONNECT: + await mqttHandlerService.Disconnect(context, message as DisconnectPacket); + break; + } + } + + /// + /// The Dispose + /// public void Dispose() { throw new NotImplementedException(); } + /// + /// The OnReceived + /// + /// The sender + /// The message + /// The public async Task OnReceived(IMessageSender sender, TransportMessage message) { if (Received == null) @@ -60,6 +171,11 @@ public async Task OnReceived(IMessageSender sender, TransportMessage message) await Received(sender, message); } + /// + /// The StartAsync + /// + /// The endPoint + /// The public async Task StartAsync(EndPoint endPoint) { if (_logger.IsEnabled(LogLevel.Debug)) @@ -107,89 +223,93 @@ public async Task StartAsync(EndPoint endPoint) } } - public async Task ChannelWrite(IChannelHandlerContext context,object message, PacketType packetType, ServerMqttHandlerService mqttHandlerService) - { - switch (packetType) - { - case PacketType.CONNECT: - await mqttHandlerService.Login(context, message as ConnectPacket); - break; - case PacketType.PUBLISH: - await mqttHandlerService.Publish(context, message as PublishPacket); - break; - case PacketType.PUBACK: - await mqttHandlerService.PubAck(context, message as PubAckPacket); - break; - case PacketType.PUBREC: - await mqttHandlerService.PubRec(context, message as PubRecPacket); - break; - case PacketType.PUBREL: - await mqttHandlerService.PubRel(context, message as PubRelPacket); - break; - case PacketType.PUBCOMP: - await mqttHandlerService.PubComp(context, message as PubCompPacket); - break; - case PacketType.SUBSCRIBE: - await mqttHandlerService.Subscribe(context, message as SubscribePacket); - break; - case PacketType.SUBACK: - await mqttHandlerService.SubAck(context, message as SubAckPacket); - break; - case PacketType.UNSUBSCRIBE: - await mqttHandlerService.Unsubscribe(context, message as UnsubscribePacket); - break; - case PacketType.UNSUBACK: - await mqttHandlerService.UnsubAck(context, message as UnsubAckPacket); - break; - case PacketType.PINGREQ: - await mqttHandlerService.PingReq(context, message as PingReqPacket); - break; - case PacketType.PINGRESP: - await mqttHandlerService.PingResp(context, message as PingRespPacket); - break; - case PacketType.DISCONNECT: - await mqttHandlerService.Disconnect(context, message as DisconnectPacket); - break; - } - } + #endregion 方法 + /// + /// Defines the + /// private class ServerHandler : ChannelHandlerAdapter { + #region 字段 + + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the _readAction + /// private readonly Action _readAction; - private readonly ILogger _logger; - public ServerHandler(Action readAction, + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The readAction + /// The logger + /// The channelService + /// The mqttBehaviorProvider + public ServerHandler(Action readAction, ILogger logger, IChannelService channelService, - IMqttBehaviorProvider mqttBehaviorProvider) + IMqttBehaviorProvider mqttBehaviorProvider) { _readAction = readAction; _logger = logger; } - - public override void ChannelRead(IChannelHandlerContext context, object message) - { - var buffer = message as Packet; - _readAction(context, buffer.PacketType, buffer); - ReferenceCountUtil.Release(message); - } + #endregion 构造函数 + + #region 方法 + + /// + /// The ChannelInactive + /// + /// The context public override void ChannelInactive(IChannelHandlerContext context) { this.SetException(new InvalidOperationException("Channel is closed.")); base.ChannelInactive(context); } - public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) { - _readAction.Invoke(context,PacketType.DISCONNECT,DisconnectPacket.Instance); + /// + /// The ChannelRead + /// + /// The context + /// The message + public override void ChannelRead(IChannelHandlerContext context, object message) + { + var buffer = message as Packet; + _readAction(context, buffer.PacketType, buffer); + ReferenceCountUtil.Release(message); + } + + /// + /// The ExceptionCaught + /// + /// The context + /// The exception + public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) + { + _readAction.Invoke(context, PacketType.DISCONNECT, DisconnectPacket.Instance); this.SetException(exception); } - void SetException(Exception ex) + /// + /// The SetException + /// + /// The ex + internal void SetException(Exception ex) { if (_logger.IsEnabled(LogLevel.Error)) _logger.LogError($"message:{ex.Message},Source:{ex.Source},Trace:{ex.StackTrace}"); } + + #endregion 方法 } } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Implementation/DefaultMqttServiceFactory.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Implementation/DefaultMqttServiceFactory.cs index dd6f68a1b..68dc0f1e9 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Implementation/DefaultMqttServiceFactory.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Implementation/DefaultMqttServiceFactory.cs @@ -10,18 +10,46 @@ namespace Surging.Core.Protocol.Mqtt.Implementation { + /// + /// Defines the + /// public class DefaultMqttServiceFactory : IMqttServiceFactory { + #region 字段 - private readonly ISerializer _serializer; + /// + /// Defines the _addressModel + /// private readonly ConcurrentDictionary _addressModel = new ConcurrentDictionary(); + /// + /// Defines the _serializer + /// + private readonly ISerializer _serializer; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serializer public DefaultMqttServiceFactory(ISerializer serializer) { _serializer = serializer; } + #endregion 构造函数 + + #region 方法 + + /// + /// The CreateMqttServiceRoutesAsync + /// + /// The descriptors + /// The public Task> CreateMqttServiceRoutesAsync(IEnumerable descriptors) { if (descriptors == null) @@ -39,7 +67,11 @@ public Task> CreateMqttServiceRoutesAsync(IEnumera return Task.FromResult(routes.AsEnumerable()); } - + /// + /// The CreateAddress + /// + /// The descriptors + /// The private IEnumerable CreateAddress(IEnumerable descriptors) { if (descriptors == null) @@ -57,5 +89,6 @@ private IEnumerable CreateAddress(IEnumerable + /// Defines the + /// + public class ServerMqttHandlerService { - private readonly ILogger _logger; + #region 字段 + + /// + /// Defines the _channelService + /// private readonly IChannelService _channelService; + + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the _mqttBehaviorProvider + /// private readonly IMqttBehaviorProvider _mqttBehaviorProvider; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The logger + /// The channelService + /// The mqttBehaviorProvider public ServerMqttHandlerService( ILogger logger, IChannelService channelService, IMqttBehaviorProvider mqttBehaviorProvider) { @@ -25,17 +52,44 @@ public ServerMqttHandlerService( _mqttBehaviorProvider = mqttBehaviorProvider; } + #endregion 构造函数 + + #region 方法 + + /// + /// The ConnAck + /// + /// The context + /// The packet + /// The public async Task ConnAck(IChannelHandlerContext context, ConnAckPacket packet) { - await context.WriteAndFlushAsync(packet); + await context.WriteAndFlushAsync(packet); } + /// + /// The Disconnect + /// + /// The context + /// The packet + /// The + public async Task Disconnect(IChannelHandlerContext context, DisconnectPacket packet) + { + await _channelService.Close(await _channelService.GetDeviceId(context.Channel), true); + } + + /// + /// The Login + /// + /// The context + /// The packet + /// The public async Task Login(IChannelHandlerContext context, ConnectPacket packet) { string deviceId = packet.ClientId; if (string.IsNullOrEmpty(deviceId)) { - await ConnAck(context, new ConnAckPacket + await ConnAck(context, new ConnAckPacket { ReturnCode = ConnectReturnCode.RefusedIdentifierRejected }); @@ -56,7 +110,7 @@ public async Task Login(IChannelHandlerContext context, ConnectPacket packet) bytes = new byte[packet.WillMessage.ReadableBytes]; packet.WillMessage.ReadBytes(bytes); } - await _channelService.Login(context.Channel, deviceId, new ConnectMessage + await _channelService.Login(context.Channel, deviceId, new ConnectMessage { CleanSession = packet.CleanSession, ClientId = packet.ClientId, @@ -75,13 +129,12 @@ public async Task Login(IChannelHandlerContext context, ConnectPacket packet) WillQualityOfService = (int)packet.WillQualityOfService, WillRetain = packet.WillRetain, WillTopic = packet.WillTopicName - }); } } else { - await ConnAck(context, new ConnAckPacket + await ConnAck(context, new ConnAckPacket { ReturnCode = ConnectReturnCode.RefusedBadUsernameOrPassword }); @@ -89,19 +142,20 @@ public async Task Login(IChannelHandlerContext context, ConnectPacket packet) } else { - await ConnAck(context, new ConnAckPacket + await ConnAck(context, new ConnAckPacket { ReturnCode = ConnectReturnCode.RefusedServerUnavailable }); } } - public async Task Disconnect(IChannelHandlerContext context, DisconnectPacket packet) - { - await _channelService.Close(await _channelService.GetDeviceId(context.Channel), true); - } - - public async Task PingReq(IChannelHandlerContext context, PingReqPacket packet) + /// + /// The PingReq + /// + /// The context + /// The packet + /// The + public async Task PingReq(IChannelHandlerContext context, PingReqPacket packet) { var channel = context.Channel; if (channel.Open && channel.Active && channel.IsWritable) @@ -113,11 +167,23 @@ public async Task PingReq(IChannelHandlerContext context, PingReqPacket packet) } } + /// + /// The PingResp + /// + /// The context + /// The packet + /// The public async Task PingResp(IChannelHandlerContext context, PingRespPacket packet) { - await context.WriteAndFlushAsync(packet); + await context.WriteAndFlushAsync(packet); } + /// + /// The PubAck + /// + /// The context + /// The packet + /// The public async Task PubAck(IChannelHandlerContext context, PubAckPacket packet) { int messageId = packet.PacketId; @@ -130,6 +196,12 @@ public async Task PubAck(IChannelHandlerContext context, PubAckPacket packet) await context.WriteAndFlushAsync(packet); } + /// + /// The PubComp + /// + /// The context + /// The packet + /// The public async Task PubComp(IChannelHandlerContext context, PubCompPacket packet) { int messageId = packet.PacketId; @@ -142,16 +214,28 @@ public async Task PubComp(IChannelHandlerContext context, PubCompPacket packet) await context.WriteAndFlushAsync(packet); } + /// + /// The Publish + /// + /// The context + /// The packet + /// The public async Task Publish(IChannelHandlerContext context, PublishPacket packet) { await _channelService.Publish(context.Channel, packet); } + /// + /// The PubRec + /// + /// The context + /// The packet + /// The public async Task PubRec(IChannelHandlerContext context, PubRecPacket packet) { int messageId = packet.PacketId; var mqttChannel = _channelService.GetMqttChannel(await _channelService.GetDeviceId(context.Channel)); - var message= mqttChannel.GetMqttMessage(messageId); + var message = mqttChannel.GetMqttMessage(messageId); if (message != null) { message.ConfirmStatus = ConfirmStatus.PUBREC; @@ -159,6 +243,12 @@ public async Task PubRec(IChannelHandlerContext context, PubRecPacket packet) await _channelService.Pubrec(mqttChannel, messageId); } + /// + /// The PubRel + /// + /// The context + /// The packet + /// The public async Task PubRel(IChannelHandlerContext context, PubRelPacket packet) { int messageId = packet.PacketId; @@ -171,11 +261,23 @@ public async Task PubRel(IChannelHandlerContext context, PubRelPacket packet) await _channelService.Pubrel(context.Channel, messageId); } + /// + /// The SubAck + /// + /// The context + /// The packet + /// The public async Task SubAck(IChannelHandlerContext context, SubAckPacket packet) { - await context.WriteAndFlushAsync(packet); + await context.WriteAndFlushAsync(packet); } + /// + /// The Subscribe + /// + /// The context + /// The packet + /// The public async Task Subscribe(IChannelHandlerContext context, SubscribePacket packet) { if (packet != null) @@ -187,16 +289,30 @@ await SubAck(context, SubAckPacket.InResponseTo(packet, QualityOfService.Exactly } } - public async Task UnsubAck(IChannelHandlerContext context, UnsubAckPacket packet) + /// + /// The UnsubAck + /// + /// The context + /// The packet + /// The + public async Task UnsubAck(IChannelHandlerContext context, UnsubAckPacket packet) { - await context.WriteAndFlushAsync(packet); + await context.WriteAndFlushAsync(packet); } - public async Task Unsubscribe(IChannelHandlerContext context, UnsubscribePacket packet) + /// + /// The Unsubscribe + /// + /// The context + /// The packet + /// The + public async Task Unsubscribe(IChannelHandlerContext context, UnsubscribePacket packet) { - string [] topics = packet.TopicFilters.ToArray(); + string[] topics = packet.TopicFilters.ToArray(); await _channelService.UnSubscribe(await _channelService.GetDeviceId(context.Channel), topics); - await UnsubAck(context, UnsubAckPacket.InResponseTo(packet)); + await UnsubAck(context, UnsubAckPacket.InResponseTo(packet)); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Channel/MqttChannel.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Channel/MqttChannel.cs index f943cec0e..53965301b 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Channel/MqttChannel.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Channel/MqttChannel.cs @@ -11,25 +11,101 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Channel { + /// + /// Defines the + /// public class MqttChannel { + #region 属性 + + /// + /// Gets or sets the Channel + /// public IChannel Channel { get; set; } + + /// + /// Gets or sets a value indicating whether CleanSession + /// + public bool CleanSession { get; set; } + + /// + /// Gets or sets the ClientId + /// public string ClientId { get; set; } + + /// + /// Gets or sets a value indicating whether IsWill + /// public bool IsWill { get; set; } - public SubscribeStatus SubscribeStatus { get; set; } - public List Topics { get; set; } - public SessionStatus SessionStatus { get; set; } + + /// + /// Gets or sets the KeepAliveInSeconds + /// public int KeepAliveInSeconds { get; set; } - public DateTime PingReqTime { get; set; } = DateTime.Now; - public bool CleanSession { get; set; } + /// + /// Gets or sets the Messages + /// public ConcurrentDictionary Messages { get; set; } - + + /// + /// Gets or sets the PingReqTime + /// + public DateTime PingReqTime { get; set; } = DateTime.Now; + + /// + /// Gets or sets the SessionStatus + /// + public SessionStatus SessionStatus { get; set; } + + /// + /// Gets or sets the SubscribeStatus + /// + public SubscribeStatus SubscribeStatus { get; set; } + + /// + /// Gets or sets the Topics + /// + public List Topics { get; set; } + + #endregion 属性 + + #region 方法 + + /// + /// The AddMqttMessage + /// + /// The messageId + /// The msg public void AddMqttMessage(int messageId, SendMqttMessage msg) { - Messages.AddOrUpdate(messageId, msg,(id,message)=>msg); + Messages.AddOrUpdate(messageId, msg, (id, message) => msg); + } + + /// + /// The AddTopic + /// + /// The topics + public void AddTopic(params string[] topics) + { + Topics.AddRange(topics); + } + + /// + /// The Close + /// + /// The + public async Task Close() + { + if (Channel != null) + await Channel.CloseAsync(); } - + + /// + /// The GetMqttMessage + /// + /// The messageId + /// The public SendMqttMessage GetMqttMessage(int messageId) { SendMqttMessage mqttMessage = null; @@ -37,50 +113,55 @@ public SendMqttMessage GetMqttMessage(int messageId) return mqttMessage; } - - public void RemoveMqttMessage(int messageId) + /// + /// The IsActive + /// + /// The + public bool IsActive() { - SendMqttMessage mqttMessage = null; - Messages.Remove(messageId,out mqttMessage); + return Channel != null && Channel.Active; } + /// + /// The IsLogin + /// + /// The public bool IsLogin() { bool result = false; if (Channel != null) { AttributeKey _login = AttributeKey.ValueOf("login"); - result= Channel.Active && Channel.HasAttribute(_login); + result = Channel.Active && Channel.HasAttribute(_login); } return result; } - public async Task Close() - { - if (Channel != null) - await Channel.CloseAsync(); - } - - + /// + /// The IsOnine + /// + /// The public async Task IsOnine() { //如果保持连接的值非零,并且服务端在2倍的保持连接时间内没有收到客户端的报文,需要断开客户端的连接 - bool isOnline= (DateTime.Now - PingReqTime).TotalSeconds <= (this.KeepAliveInSeconds*2) && SessionStatus== SessionStatus.OPEN; - if(!isOnline) + bool isOnline = (DateTime.Now - PingReqTime).TotalSeconds <= (this.KeepAliveInSeconds * 2) && SessionStatus == SessionStatus.OPEN; + if (!isOnline) { - await Close(); + await Close(); } return isOnline; } - public bool IsActive() - { - return Channel != null && Channel.Active; - } - - public void AddTopic(params string[] topics) + /// + /// The RemoveMqttMessage + /// + /// The messageId + public void RemoveMqttMessage(int messageId) { - Topics.AddRange(topics); + SendMqttMessage mqttMessage = null; + Messages.Remove(messageId, out mqttMessage); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Enums/Behavior.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Enums/Behavior.cs index b2b4db088..ba73699c7 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Enums/Behavior.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Enums/Behavior.cs @@ -4,9 +4,23 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Enums { + #region 枚举 + + /// + /// Defines the Behavior + /// public enum Behavior { + /// + /// Defines the Publish + /// Publish, + + /// + /// Defines the Suscribe + /// Suscribe, } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Enums/ConfirmStatus.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Enums/ConfirmStatus.cs index 9b10580e0..6470a53aa 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Enums/ConfirmStatus.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Enums/ConfirmStatus.cs @@ -4,11 +4,33 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Enums { + #region 枚举 + + /// + /// Defines the ConfirmStatus + /// public enum ConfirmStatus { + /// + /// Defines the PUB + /// PUB, + + /// + /// Defines the PUBREC + /// PUBREC, + + /// + /// Defines the PUBREL + /// PUBREL, + + /// + /// Defines the COMPLETE + /// COMPLETE, } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Enums/ConnReturnCode.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Enums/ConnReturnCode.cs index c19ba4255..65fd21b1f 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Enums/ConnReturnCode.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Enums/ConnReturnCode.cs @@ -4,13 +4,43 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Enums { + #region 枚举 + + /// + /// Defines the ConnReturnCode + /// public enum ConnReturnCode { + /// + /// Defines the Accepted + /// Accepted = 0x00, + + /// + /// Defines the RefusedUnacceptableProtocolVersion + /// RefusedUnacceptableProtocolVersion = 0X01, + + /// + /// Defines the RefusedIdentifierRejected + /// RefusedIdentifierRejected = 0x02, + + /// + /// Defines the RefusedServerUnavailable + /// RefusedServerUnavailable = 0x03, + + /// + /// Defines the RefusedBadUsernameOrPassword + /// RefusedBadUsernameOrPassword = 0x04, + + /// + /// Defines the RefusedNotAuthorized + /// RefusedNotAuthorized = 0x05 } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Enums/MessageType.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Enums/MessageType.cs index aa6efa2b2..acb79b5a1 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Enums/MessageType.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Enums/MessageType.cs @@ -4,21 +4,83 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Enums { - public enum MessageType + #region 枚举 + + /// + /// Defines the MessageType + /// + public enum MessageType { + /// + /// Defines the CONNECT + /// CONNECT = 1, + + /// + /// Defines the CONNACK + /// CONNACK = 2, + + /// + /// Defines the PUBLISH + /// PUBLISH = 3, + + /// + /// Defines the PUBACK + /// PUBACK = 4, + + /// + /// Defines the PUBREC + /// PUBREC = 5, + + /// + /// Defines the PUBREL + /// PUBREL = 6, + + /// + /// Defines the PUBCOMP + /// PUBCOMP = 7, + + /// + /// Defines the SUBSCRIBE + /// SUBSCRIBE = 8, + + /// + /// Defines the SUBACK + /// SUBACK = 9, + + /// + /// Defines the UNSUBSCRIBE + /// UNSUBSCRIBE = 10, + + /// + /// Defines the UNSUBACK + /// UNSUBACK = 11, + + /// + /// Defines the PINGREQ + /// PINGREQ = 12, + + /// + /// Defines the PINGRESP + /// PINGRESP = 13, + + /// + /// Defines the DISCONNECT + /// DISCONNECT = 14 } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Enums/SessionStatus.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Enums/SessionStatus.cs index 0f8c91c4f..f546b84da 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Enums/SessionStatus.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Enums/SessionStatus.cs @@ -4,9 +4,23 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Enums { - public enum SessionStatus + #region 枚举 + + /// + /// Defines the SessionStatus + /// + public enum SessionStatus { + /// + /// Defines the OPEN + /// OPEN, + + /// + /// Defines the CLOSE + /// CLOSE } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Enums/SubscribeStatus.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Enums/SubscribeStatus.cs index 5e0486066..a2b0e8f4c 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Enums/SubscribeStatus.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Enums/SubscribeStatus.cs @@ -4,9 +4,23 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Enums { - public enum SubscribeStatus + #region 枚举 + + /// + /// Defines the SubscribeStatus + /// + public enum SubscribeStatus { + /// + /// Defines the Yes + /// Yes, + + /// + /// Defines the No + /// No } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Messages/ConnectMessage.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Messages/ConnectMessage.cs index 86f7a5c23..4df63a193 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Messages/ConnectMessage.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Messages/ConnectMessage.cs @@ -4,35 +4,88 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Enums { - public class ConnectMessage:MqttMessage + /// + /// Defines the + /// + public class ConnectMessage : MqttMessage { - public override MessageType MessageType => MessageType.CONNECT; - public string ProtocolName { get; set; } - - public int ProtocolLevel { get; set; } + #region 属性 + /// + /// Gets or sets a value indicating whether CleanSession + /// public bool CleanSession { get; set; } - public bool HasWill { get; set; } - - public int WillQualityOfService { get; set; } - - public bool WillRetain { get; set; } + /// + /// Gets or sets the ClientId + /// + public string ClientId { get; set; } + /// + /// Gets or sets a value indicating whether HasPassword + /// public bool HasPassword { get; set; } + /// + /// Gets or sets a value indicating whether HasUsername + /// public bool HasUsername { get; set; } + /// + /// Gets or sets a value indicating whether HasWill + /// + public bool HasWill { get; set; } + + /// + /// Gets or sets the KeepAliveInSeconds + /// public int KeepAliveInSeconds { get; set; } - public string Username { get; set; } + /// + /// Gets the MessageType + /// + public override MessageType MessageType => MessageType.CONNECT; + /// + /// Gets or sets the Password + /// public string Password { get; set; } - public string ClientId { get; set; } + /// + /// Gets or sets the ProtocolLevel + /// + public int ProtocolLevel { get; set; } - public string WillTopic { get; set; } + /// + /// Gets or sets the ProtocolName + /// + public string ProtocolName { get; set; } + /// + /// Gets or sets the Username + /// + public string Username { get; set; } + + /// + /// Gets or sets the WillMessage + /// public byte[] WillMessage { get; set; } + + /// + /// Gets or sets the WillQualityOfService + /// + public int WillQualityOfService { get; set; } + + /// + /// Gets or sets a value indicating whether WillRetain + /// + public bool WillRetain { get; set; } + + /// + /// Gets or sets the WillTopic + /// + public string WillTopic { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Messages/MqttMessage.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Messages/MqttMessage.cs index e3191dbf0..5b2789de4 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Messages/MqttMessage.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Messages/MqttMessage.cs @@ -5,18 +5,46 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Enums { - public abstract class MqttMessage + /// + /// Defines the + /// + public abstract class MqttMessage { - public abstract MessageType MessageType { get; } + #region 属性 + /// + /// Gets or sets a value indicating whether Duplicate + /// public virtual bool Duplicate { get; set; } - public virtual int Qos { get; set; } =(int) QualityOfService.AtMostOnce; + + /// + /// Gets the MessageType + /// + public abstract MessageType MessageType { get; } + + /// + /// Gets or sets the Qos + /// + public virtual int Qos { get; set; } = (int)QualityOfService.AtMostOnce; + + /// + /// Gets or sets a value indicating whether RetainRequested + /// public virtual bool RetainRequested { get; set; } - + #endregion 属性 + + #region 方法 + + /// + /// The ToString + /// + /// The public override string ToString() { return $"{this.GetType().Name}[Qos={this.Qos}, Duplicate={this.Duplicate}, Retain={this.RetainRequested}]"; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Messages/MqttWillMessage.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Messages/MqttWillMessage.cs index 4968ef511..b440d80a9 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Messages/MqttWillMessage.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Messages/MqttWillMessage.cs @@ -4,15 +4,33 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Messages { + /// + /// Defines the + /// public class MqttWillMessage { - public string Topic{ get; set; } + #region 属性 - public string WillMessage { get; set; } + /// + /// Gets or sets the Qos + /// + public int Qos { get; set; } + /// + /// Gets or sets the Topic + /// + public string Topic { get; set; } + /// + /// Gets or sets the WillMessage + /// + public string WillMessage { get; set; } + + /// + /// Gets or sets a value indicating whether WillRetain + /// public bool WillRetain { get; set; } - public int Qos { get; set; } + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Messages/RetainMessage.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Messages/RetainMessage.cs index b937eabd1..c0a1cadd0 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Messages/RetainMessage.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Messages/RetainMessage.cs @@ -4,11 +4,28 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Messages { - public class RetainMessage + /// + /// Defines the + /// + public class RetainMessage { + #region 属性 + + /// + /// Gets or sets the ByteBuf + /// public byte[] ByteBuf { get; set; } + /// + /// Gets or sets the QoS + /// public int QoS { get; set; } + + /// + /// Gets the ToString + /// public new string ToString => Encoding.UTF8.GetString(ByteBuf); + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Messages/SendMqttMessage.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Messages/SendMqttMessage.cs index 8e82024a1..83644c0ae 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Messages/SendMqttMessage.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Messages/SendMqttMessage.cs @@ -6,22 +6,53 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Messages { - public class SendMqttMessage + /// + /// Defines the + /// + public class SendMqttMessage { - public int MessageId { get; set; } + #region 属性 + + /// + /// Gets or sets the ByteBuf + /// + public byte[] ByteBuf { get; set; } + /// + /// Gets or sets the Channel + /// public IChannel Channel { get; set; } - public ConfirmStatus ConfirmStatus { get; set; } + /// + /// Gets or sets the ConfirmStatus + /// + public ConfirmStatus ConfirmStatus { get; set; } - public long Time { get; set; } + /// + /// Gets or sets the MessageId + /// + public int MessageId { get; set; } - public byte[] ByteBuf { get; set; } + /// + /// Gets or sets the Qos + /// + public int Qos { get; set; } + /// + /// Gets or sets a value indicating whether Retain + /// public bool Retain { get; set; } - public int Qos { get; set; } + /// + /// Gets or sets the Time + /// + public long Time { get; set; } + /// + /// Gets or sets the Topic + /// public string Topic { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Messages/SessionMessage.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Messages/SessionMessage.cs index aa5bdd080..a2dc06ffa 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Messages/SessionMessage.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Messages/SessionMessage.cs @@ -4,13 +4,28 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Messages { - public class SessionMessage + /// + /// Defines the + /// + public class SessionMessage { - public byte[] Message { get; set; } + #region 属性 + /// + /// Gets or sets the Message + /// + public byte[] Message { get; set; } + + /// + /// Gets or sets the QoS + /// public int QoS { get; set; } + /// + /// Gets or sets the Topic + /// public string Topic { get; set; } + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/IMqttBehaviorProvider.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/IMqttBehaviorProvider.cs index c95c13539..001d85a6c 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/IMqttBehaviorProvider.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/IMqttBehaviorProvider.cs @@ -5,8 +5,23 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Runtime { - public interface IMqttBehaviorProvider + #region 接口 + + /// + /// Defines the + /// + public interface IMqttBehaviorProvider { + #region 方法 + + /// + /// The GetMqttBehavior + /// + /// The MqttBehavior GetMqttBehavior(); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/IMqttBrokerEntryManger.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/IMqttBrokerEntryManger.cs index e48fa5d5a..c67f6303f 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/IMqttBrokerEntryManger.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/IMqttBrokerEntryManger.cs @@ -6,12 +6,40 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Runtime { - public interface IMqttBrokerEntryManger + #region 接口 + + /// + /// Defines the + /// + public interface IMqttBrokerEntryManger { - ValueTask> GetMqttBrokerAddress(string topic); + #region 方法 + + /// + /// The CancellationReg + /// + /// The topic + /// The addressModel + /// The + Task CancellationReg(string topic, AddressModel addressModel); - Task CancellationReg(string topic,AddressModel addressModel); + /// + /// The GetMqttBrokerAddress + /// + /// The topic + /// The + ValueTask> GetMqttBrokerAddress(string topic); + /// + /// The Register + /// + /// The topic + /// The addressModel + /// The Task Register(string topic, AddressModel addressModel); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/IMqttRemoteInvokeService.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/IMqttRemoteInvokeService.cs index 519a85bd4..14b5244d5 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/IMqttRemoteInvokeService.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/IMqttRemoteInvokeService.cs @@ -7,13 +7,40 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Runtime { + #region 接口 + + /// + /// Defines the + /// public interface IMqttRemoteInvokeService { - + #region 方法 + + /// + /// The InvokeAsync + /// + /// The context + /// The Task InvokeAsync(RemoteInvokeContext context); - + + /// + /// The InvokeAsync + /// + /// The context + /// The cancellationToken + /// The Task InvokeAsync(RemoteInvokeContext context, CancellationToken cancellationToken); - + + /// + /// The InvokeAsync + /// + /// The context + /// The requestTimeout + /// The Task InvokeAsync(RemoteInvokeContext context, int requestTimeout); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/IMqttRomtePublishService.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/IMqttRomtePublishService.cs index ab7a4182b..1507dba6a 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/IMqttRomtePublishService.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/IMqttRomtePublishService.cs @@ -10,10 +10,27 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Runtime { + #region 接口 + + /// + /// Defines the + /// [ServiceBundle("Device")] public interface IMqttRomtePublishService : IServiceKey { + #region 方法 + + /// + /// The Publish + /// + /// The deviceId + /// The message + /// The [Command(ShuntStrategy = AddressSelectorMode.HashAlgorithm)] Task Publish(string deviceId, MqttWillMessage message); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/Implementation/DefaultMqttBehaviorProvider.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/Implementation/DefaultMqttBehaviorProvider.cs index 33ab3dba6..7eb233f76 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/Implementation/DefaultMqttBehaviorProvider.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/Implementation/DefaultMqttBehaviorProvider.cs @@ -5,34 +5,61 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Runtime.Implementation { + /// + /// Defines the + /// public class DefaultMqttBehaviorProvider : IMqttBehaviorProvider { - #region Field + #region 字段 + /// + /// Defines the _serviceEntryProvider + /// private readonly IServiceEntryProvider _serviceEntryProvider; + + /// + /// Defines the _serviceProvider + /// private readonly CPlatformContainer _serviceProvider; + + /// + /// Defines the _mqttBehavior + /// private MqttBehavior _mqttBehavior; - #endregion Field + #endregion 字段 - #region Constructor + #region 构造函数 - public DefaultMqttBehaviorProvider(IServiceEntryProvider serviceEntryProvider, CPlatformContainer serviceProvider) + /// + /// Initializes a new instance of the class. + /// + /// The serviceEntryProvider + /// The serviceProvider + public DefaultMqttBehaviorProvider(IServiceEntryProvider serviceEntryProvider, CPlatformContainer serviceProvider) { _serviceEntryProvider = serviceEntryProvider; _serviceProvider = serviceProvider; } - #endregion Constructor + #endregion 构造函数 + #region 方法 + + /// + /// The GetMqttBehavior + /// + /// The public MqttBehavior GetMqttBehavior() { if (_mqttBehavior == null) { - _mqttBehavior = _serviceEntryProvider.GetTypes() - .Select(type=> _serviceProvider.GetInstances(type) as MqttBehavior ).Where(p=>p!=null).FirstOrDefault(); + _mqttBehavior = _serviceEntryProvider.GetTypes() + .Select(type => _serviceProvider.GetInstances(type) as MqttBehavior).Where(p => p != null).FirstOrDefault(); } return _mqttBehavior; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/Implementation/DefaultMqttBrokerEntryManager.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/Implementation/DefaultMqttBrokerEntryManager.cs index a5186d5d4..9cd42562e 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/Implementation/DefaultMqttBrokerEntryManager.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/Implementation/DefaultMqttBrokerEntryManager.cs @@ -1,25 +1,51 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using Surging.Core.CPlatform.Address; using Surging.Core.CPlatform.Mqtt; using Surging.Core.CPlatform.Mqtt.Implementation; using Surging.Core.CPlatform.Runtime.Client.HealthChecks; using Surging.Core.CPlatform.Runtime.Client.HealthChecks.Implementation; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; namespace Surging.Core.Protocol.Mqtt.Internal.Runtime.Implementation { + /// + /// Defines the + /// public class DefaultMqttBrokerEntryManager : IMqttBrokerEntryManger { - private readonly IMqttServiceRouteManager _mqttServiceRouteManager; - private readonly ILogger _logger; + #region 字段 + + /// + /// Defines the _brokerEntries + /// private readonly ConcurrentDictionary> _brokerEntries = new ConcurrentDictionary>(); + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the _mqttServiceRouteManager + /// + private readonly IMqttServiceRouteManager _mqttServiceRouteManager; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The mqttServiceRouteManager + /// The logger + /// The healthCheckService public DefaultMqttBrokerEntryManager(IMqttServiceRouteManager mqttServiceRouteManager, ILogger logger, IHealthCheckService healthCheckService) { @@ -30,18 +56,33 @@ public DefaultMqttBrokerEntryManager(IMqttServiceRouteManager mqttServiceRouteMa healthCheckService.Removed += MqttRouteManager_Removed; } + #endregion 构造函数 + + #region 方法 + + /// + /// The CancellationReg + /// + /// The topic + /// The addressModel + /// The public async Task CancellationReg(string topic, AddressModel addressModel) { await _mqttServiceRouteManager.RemoveByTopicAsync(topic, new AddressModel[] { addressModel }); } + /// + /// The GetMqttBrokerAddress + /// + /// The topic + /// The public async ValueTask> GetMqttBrokerAddress(string topic) { _brokerEntries.TryGetValue(topic, out IEnumerable addresses); - if (addresses==null || !addresses.Any()) + if (addresses == null || !addresses.Any()) { var routes = await _mqttServiceRouteManager.GetRoutesAsync(); - var route= routes.Where(p => p.MqttDescriptor.Topic == topic).SingleOrDefault(); + var route = routes.Where(p => p.MqttDescriptor.Topic == topic).SingleOrDefault(); if (route != null) { _brokerEntries.TryAdd(topic, route.MqttEndpoint); @@ -51,6 +92,12 @@ public async ValueTask> GetMqttBrokerAddress(string to return addresses; } + /// + /// The Register + /// + /// The topic + /// The addressModel + /// The public async Task Register(string topic, AddressModel addressModel) { await _mqttServiceRouteManager.SetRoutesAsync(new MqttServiceRoute[] { new MqttServiceRoute { @@ -64,20 +111,37 @@ public async Task Register(string topic, AddressModel addressModel) }); } + /// + /// The GetCacheKey + /// + /// The descriptor + /// The private static string GetCacheKey(MqttDescriptor descriptor) { return descriptor.Topic; } + /// + /// The MqttRouteManager_Removed + /// + /// The sender + /// The e + private void MqttRouteManager_Removed(object sender, HealthCheckEventArgs e) + { + _mqttServiceRouteManager.RemveAddressAsync(new AddressModel[] { e.Address }); + } + + /// + /// The MqttRouteManager_Removed + /// + /// The sender + /// The e private void MqttRouteManager_Removed(object sender, MqttServiceRouteEventArgs e) { var key = GetCacheKey(e.Route.MqttDescriptor); _brokerEntries.TryRemove(key, out IEnumerable value); } - private void MqttRouteManager_Removed(object sender, HealthCheckEventArgs e) - { - _mqttServiceRouteManager.RemveAddressAsync(new AddressModel[] { e.Address }); - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/Implementation/MqttRemoteInvokeService.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/Implementation/MqttRemoteInvokeService.cs index 06e61a570..e6c7b82e9 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/Implementation/MqttRemoteInvokeService.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/Implementation/MqttRemoteInvokeService.cs @@ -14,38 +14,82 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Runtime.Implementation { - public class MqttRemoteInvokeService:IMqttRemoteInvokeService - { - private readonly ITransportClientFactory _transportClientFactory; - private readonly ILogger _logger; + /// + /// Defines the + /// + public class MqttRemoteInvokeService : IMqttRemoteInvokeService + { + #region 字段 + + /// + /// Defines the _healthCheckService + /// private readonly IHealthCheckService _healthCheckService; + + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the _mqttBrokerEntryManger + /// private readonly IMqttBrokerEntryManger _mqttBrokerEntryManger; - public MqttRemoteInvokeService( ITransportClientFactory transportClientFactory, - ILogger logger, + /// + /// Defines the _transportClientFactory + /// + private readonly ITransportClientFactory _transportClientFactory; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The transportClientFactory + /// The logger + /// The healthCheckService + /// The mqttBrokerEntryManger + public MqttRemoteInvokeService(ITransportClientFactory transportClientFactory, + ILogger logger, IHealthCheckService healthCheckService, IMqttBrokerEntryManger mqttBrokerEntryManger) - { + { _transportClientFactory = transportClientFactory; _logger = logger; _healthCheckService = healthCheckService; _mqttBrokerEntryManger = mqttBrokerEntryManger; } - #region Implementation of IRemoteInvokeService + #endregion 构造函数 + #region 方法 + + /// + /// The InvokeAsync + /// + /// The context + /// The public async Task InvokeAsync(RemoteInvokeContext context) { - await InvokeAsync(context, Task.Factory.CancellationToken); + await InvokeAsync(context, Task.Factory.CancellationToken); } + /// + /// The InvokeAsync + /// + /// The context + /// The cancellationToken + /// The public async Task InvokeAsync(RemoteInvokeContext context, CancellationToken cancellationToken) { var mqttContext = context as MqttRemoteInvokeContext; if (mqttContext != null) { var invokeMessage = context.InvokeMessage; - var host= NetUtils.GetHostAddress(); + var host = NetUtils.GetHostAddress(); var addresses = await _mqttBrokerEntryManger.GetMqttBrokerAddress(mqttContext.topic); addresses = addresses.Except(new AddressModel[] { host }); foreach (var address in addresses) @@ -55,7 +99,7 @@ public async Task InvokeAsync(RemoteInvokeContext context, CancellationToken can var endPoint = address.CreateEndPoint(); if (_logger.IsEnabled(LogLevel.Debug)) _logger.LogDebug($"使用地址:'{endPoint}'进行调用。"); - var client =await _transportClientFactory.CreateClientAsync(endPoint); + var client = await _transportClientFactory.CreateClientAsync(endPoint); await client.SendAsync(invokeMessage, cancellationToken).WithCancellation(cancellationToken); } catch (CommunicationException) @@ -70,6 +114,12 @@ public async Task InvokeAsync(RemoteInvokeContext context, CancellationToken can } } + /// + /// The InvokeAsync + /// + /// The context + /// The requestTimeout + /// The public async Task InvokeAsync(RemoteInvokeContext context, int requestTimeout) { var mqttContext = context as MqttRemoteInvokeContext; @@ -88,10 +138,10 @@ public async Task InvokeAsync(RemoteInvokeContext context, int requestTimeout) var endPoint = address.CreateEndPoint(); if (_logger.IsEnabled(LogLevel.Debug)) _logger.LogDebug($"使用地址:'{endPoint}'进行调用。"); - var client =await _transportClientFactory.CreateClientAsync(endPoint); + var client = await _transportClientFactory.CreateClientAsync(endPoint); using (var cts = new CancellationTokenSource()) { - await client.SendAsync(invokeMessage,cts.Token).WithCancellation(cts,requestTimeout); + await client.SendAsync(invokeMessage, cts.Token).WithCancellation(cts, requestTimeout); } } catch (CommunicationException) @@ -101,14 +151,12 @@ public async Task InvokeAsync(RemoteInvokeContext context, int requestTimeout) catch (Exception exception) { _logger.LogError(exception, $"发起mqtt请求中发生了错误,服务Id:{invokeMessage.ServiceId}。"); - } } } } } - - #endregion Implementation of IRemoteInvokeService + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/Implementation/MqttRomtePublishService.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/Implementation/MqttRomtePublishService.cs index eb01b8401..e565b3aed 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/Implementation/MqttRomtePublishService.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/Implementation/MqttRomtePublishService.cs @@ -9,11 +9,24 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Runtime.Implementation { + /// + /// Defines the + /// public class MqttRomtePublishService : ServiceBase, IMqttRomtePublishService { - public async Task Publish(string deviceId, MqttWillMessage message) + #region 方法 + + /// + /// The Publish + /// + /// The deviceId + /// The message + /// The + public async Task Publish(string deviceId, MqttWillMessage message) { await ServiceLocator.GetService().Publish(deviceId, message); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/MqttRemoteInvokeContext.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/MqttRemoteInvokeContext.cs index 843652bc0..768783fad 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/MqttRemoteInvokeContext.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/MqttRemoteInvokeContext.cs @@ -5,9 +5,18 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Runtime { - public class MqttRemoteInvokeContext: RemoteInvokeContext + /// + /// Defines the + /// + public class MqttRemoteInvokeContext : RemoteInvokeContext { - public string topic { get; set; } + #region 属性 + + /// + /// Gets or sets the topic + /// + public string topic { get; set; } + + #endregion 属性 } -} - \ No newline at end of file +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/Runnable.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/Runnable.cs index 51c731314..0a89c815b 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/Runnable.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/Runnable.cs @@ -5,10 +5,30 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Runtime { - public abstract class Runnable + /// + /// Defines the + /// + public abstract class Runnable { - private volatile Thread _runnableThread; + #region 字段 + + /// + /// Defines the _timer + /// private readonly Timer _timer; + + /// + /// Defines the _runnableThread + /// + private volatile Thread _runnableThread; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public Runnable() { var timeSpan = TimeSpan.FromSeconds(3); @@ -18,7 +38,15 @@ public Runnable() }, null, timeSpan, timeSpan); } + #endregion 构造函数 + + #region 方法 + + /// + /// The Run + /// public abstract void Run(); - + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/SacnScheduled.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/SacnScheduled.cs index 3a3deeb18..8413499a0 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/SacnScheduled.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/SacnScheduled.cs @@ -9,31 +9,43 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Runtime { - public class SacnScheduled: ScanRunnable + /// + /// Defines the + /// + public class SacnScheduled : ScanRunnable { + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// public SacnScheduled() { } - private bool CheckTime(long time) - { - return DateTime.Now.Millisecond - time >= 10 * 1000; - } - + #endregion 构造函数 + + #region 方法 + + /// + /// The Execute + /// + /// The poll public override void Execute(SendMqttMessage poll) { if (CheckTime(poll.Time) && poll.Channel.Active) { - poll.Time=DateTime.Now.Ticks / 10000; + poll.Time = DateTime.Now.Ticks / 10000; switch (poll.ConfirmStatus) { case ConfirmStatus.PUB: PubMessage(poll.Channel, poll); break; + case ConfirmStatus.PUBREL: PubRelAck(poll); break; + case ConfirmStatus.PUBREC: PubRecAck(poll); break; @@ -41,26 +53,49 @@ public override void Execute(SendMqttMessage poll) } } - private void PubMessage(IChannel channel, SendMqttMessage mqttMessage) + /// + /// The PubRelAck + /// + /// The mqttMessage + protected void PubRelAck(SendMqttMessage mqttMessage) { - PublishPacket mqttPublishMessage = new PublishPacket((QualityOfService)mqttMessage.Qos, true, mqttMessage.Retain) + PubRelPacket mqttPubAckMessage = new PubRelPacket() { - TopicName = mqttMessage.Topic, PacketId = mqttMessage.MessageId, - Payload = Unpooled.WrappedBuffer(mqttMessage.ByteBuf) }; - channel.WriteAndFlushAsync(mqttPublishMessage); + mqttMessage.Channel.WriteAndFlushAsync(mqttPubAckMessage); } - protected void PubRelAck( SendMqttMessage mqttMessage) + /// + /// The CheckTime + /// + /// The time + /// The + private bool CheckTime(long time) { - PubRelPacket mqttPubAckMessage = new PubRelPacket() + return DateTime.Now.Millisecond - time >= 10 * 1000; + } + + /// + /// The PubMessage + /// + /// The channel + /// The mqttMessage + private void PubMessage(IChannel channel, SendMqttMessage mqttMessage) + { + PublishPacket mqttPublishMessage = new PublishPacket((QualityOfService)mqttMessage.Qos, true, mqttMessage.Retain) { + TopicName = mqttMessage.Topic, PacketId = mqttMessage.MessageId, + Payload = Unpooled.WrappedBuffer(mqttMessage.ByteBuf) }; - mqttMessage.Channel.WriteAndFlushAsync(mqttPubAckMessage); + channel.WriteAndFlushAsync(mqttPublishMessage); } + /// + /// The PubRecAck + /// + /// The mqttMessage private void PubRecAck(SendMqttMessage mqttMessage) { PubRecPacket mqttPubAckMessage = new PubRecPacket() @@ -69,5 +104,7 @@ private void PubRecAck(SendMqttMessage mqttMessage) }; mqttMessage.Channel.WriteAndFlushAsync(mqttPubAckMessage); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/ScanRunnable.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/ScanRunnable.cs index d9378c4bf..b17df5e28 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/ScanRunnable.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Runtime/ScanRunnable.cs @@ -6,19 +6,49 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Runtime { + /// + /// Defines the + /// public abstract class ScanRunnable : Runnable { + #region 字段 + + /// + /// Defines the _queue + /// private ConcurrentQueue _queue = new ConcurrentQueue(); - public void Enqueue(SendMqttMessage t) + + #endregion 字段 + + #region 方法 + + /// + /// The Enqueue + /// + /// The ts + public void Enqueue(List ts) { - _queue.Enqueue(t); + ts.ForEach(message => _queue.Enqueue(message)); } - public void Enqueue(List ts) + /// + /// The Enqueue + /// + /// The t + public void Enqueue(SendMqttMessage t) { - ts.ForEach(message=> _queue.Enqueue(message)); + _queue.Enqueue(t); } + /// + /// The Execute + /// + /// The poll + public abstract void Execute(SendMqttMessage poll); + + /// + /// The Run + /// public override void Run() { if (!_queue.IsEmpty) @@ -36,6 +66,6 @@ public override void Run() } } - public abstract void Execute(SendMqttMessage poll); + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/AbstractChannelService.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/AbstractChannelService.cs index 1b457e148..b7fa61dfe 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/AbstractChannelService.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/AbstractChannelService.cs @@ -1,38 +1,89 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Text; +using DotNetty.Codecs.Mqtt.Packets; using DotNetty.Common.Utilities; using DotNetty.Transport.Channels; +using Surging.Core.CPlatform; +using Surging.Core.CPlatform.Address; +using Surging.Core.CPlatform.Ids; +using Surging.Core.CPlatform.Messages; +using Surging.Core.CPlatform.Utilities; using Surging.Core.Protocol.Mqtt.Internal.Channel; using Surging.Core.Protocol.Mqtt.Internal.Enums; using Surging.Core.Protocol.Mqtt.Internal.Messages; +using Surging.Core.Protocol.Mqtt.Internal.Runtime; +using System; using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.Linq; -using DotNetty.Codecs.Mqtt.Packets; -using System.Threading.Tasks; -using Surging.Core.Protocol.Mqtt.Internal.Runtime; using System.Net; -using Surging.Core.CPlatform.Address; -using Surging.Core.CPlatform; -using Surging.Core.CPlatform.Utilities; -using Surging.Core.CPlatform.Messages; -using Surging.Core.CPlatform.Ids; +using System.Text; +using System.Threading.Tasks; namespace Surging.Core.Protocol.Mqtt.Internal.Services { + /// + /// Defines the + /// public abstract class AbstractChannelService : IChannelService { - private readonly AttributeKey _loginAttrKey = AttributeKey.ValueOf("login"); + #region 字段 + + /// + /// Defines the _retain + /// + protected readonly ConcurrentDictionary> _retain = new ConcurrentDictionary>(); + + /// + /// Defines the _deviceIdAttrKey + /// private readonly AttributeKey _deviceIdAttrKey = AttributeKey.ValueOf("deviceId"); + + /// + /// Defines the _loginAttrKey + /// + private readonly AttributeKey _loginAttrKey = AttributeKey.ValueOf("login"); + + /// + /// Defines the _messagePushService + /// private readonly IMessagePushService _messagePushService; - private readonly ConcurrentDictionary> _topics = new ConcurrentDictionary>(); - private readonly ConcurrentDictionary _mqttChannels = new ConcurrentDictionary(); - protected readonly ConcurrentDictionary> _retain = new ConcurrentDictionary>(); + + /// + /// Defines the _mqttBrokerEntryManger + /// private readonly IMqttBrokerEntryManger _mqttBrokerEntryManger; + + /// + /// Defines the _mqttChannels + /// + private readonly ConcurrentDictionary _mqttChannels = new ConcurrentDictionary(); + + /// + /// Defines the _mqttRemoteInvokeService + /// private readonly IMqttRemoteInvokeService _mqttRemoteInvokeService; + + /// + /// Defines the _publishServiceId + /// private readonly string _publishServiceId; + /// + /// Defines the _topics + /// + private readonly ConcurrentDictionary> _topics = new ConcurrentDictionary>(); + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The messagePushService + /// The mqttBrokerEntryManger + /// The mqttRemoteInvokeService + /// The serviceIdGenerator public AbstractChannelService(IMessagePushService messagePushService, IMqttBrokerEntryManger mqttBrokerEntryManger, IMqttRemoteInvokeService mqttRemoteInvokeService, @@ -42,14 +93,16 @@ IServiceIdGenerator serviceIdGenerator _messagePushService = messagePushService; _mqttBrokerEntryManger = mqttBrokerEntryManger; _mqttRemoteInvokeService = mqttRemoteInvokeService; - _publishServiceId= serviceIdGenerator.GenerateServiceId(typeof(IMqttRomtePublishService).GetMethod("Publish")); + _publishServiceId = serviceIdGenerator.GenerateServiceId(typeof(IMqttRomtePublishService).GetMethod("Publish")); } - public ConcurrentDictionary MqttChannels { get { - return _mqttChannels; - } - } + #endregion 构造函数 + #region 属性 + + /// + /// Gets the DeviceIdAttrKey + /// public AttributeKey DeviceIdAttrKey { get @@ -58,6 +111,9 @@ public AttributeKey DeviceIdAttrKey } } + /// + /// Gets the LoginAttrKey + /// public AttributeKey LoginAttrKey { get @@ -66,14 +122,20 @@ public AttributeKey LoginAttrKey } } - public ConcurrentDictionary> Topics + /// + /// Gets the MqttChannels + /// + public ConcurrentDictionary MqttChannels { get { - return _topics; + return _mqttChannels; } } + /// + /// Gets the Retain + /// public ConcurrentDictionary> Retain { get @@ -82,48 +144,93 @@ public ConcurrentDictionary> Retain } } - public abstract Task Close(string deviceId, bool isDisconnect); + /// + /// Gets the Topics + /// + public ConcurrentDictionary> Topics + { + get + { + return _topics; + } + } - public abstract Task Connect(string deviceId, MqttChannel build); + #endregion 属性 - public bool RemoveChannel(string topic, MqttChannel mqttChannel) + #region 方法 + + /// + /// The AddChannel + /// + /// The topic + /// The mqttChannel + /// The + public bool AddChannel(string topic, MqttChannel mqttChannel) { var result = false; if (!string.IsNullOrEmpty(topic) && mqttChannel != null) { _topics.TryGetValue(topic, out IEnumerable mqttChannels); var channels = mqttChannels == null ? new List() : mqttChannels.ToList(); - channels.Remove(mqttChannel); + channels.Add(mqttChannel); _topics.AddOrUpdate(topic, channels, (key, value) => channels); result = true; } return result; } + /// + /// The Close + /// + /// The deviceId + /// The isDisconnect + /// The + public abstract Task Close(string deviceId, bool isDisconnect); + + /// + /// The Connect + /// + /// The deviceId + /// The build + /// The + public abstract Task Connect(string deviceId, MqttChannel build); + + /// + /// The GetDeviceId + /// + /// The channel + /// The public async ValueTask GetDeviceId(IChannel channel) { string deviceId = null; if (channel != null) - { + { deviceId = channel.GetAttribute(DeviceIdAttrKey).Get(); } return await new ValueTask(deviceId); } - public bool AddChannel(string topic, MqttChannel mqttChannel) + /// + /// The GetDeviceIsOnine + /// + /// The deviceId + /// The + public async ValueTask GetDeviceIsOnine(string deviceId) { - var result = false; - if (!string.IsNullOrEmpty(topic) && mqttChannel != null) + bool result = false; + if (!string.IsNullOrEmpty(deviceId)) { - _topics.TryGetValue(topic, out IEnumerable mqttChannels); - var channels = mqttChannels==null ? new List(): mqttChannels.ToList(); - channels.Add(mqttChannel); - _topics.AddOrUpdate(topic, channels, (key, value) => channels); - result = true; + MqttChannels.TryGetValue(deviceId, out MqttChannel mqttChannel); + result = mqttChannel == null ? false : await mqttChannel.IsOnine(); } return result; } + /// + /// The GetMqttChannel + /// + /// The deviceId + /// The public MqttChannel GetMqttChannel(string deviceId) { MqttChannel channel = null; @@ -134,27 +241,60 @@ public MqttChannel GetMqttChannel(string deviceId) return channel; } - protected async Task RegisterMqttBroker(string topic) - { - var addresses = await _mqttBrokerEntryManger.GetMqttBrokerAddress(topic); - var host = NetUtils.GetHostAddress(); - if (addresses==null || !addresses.Any(p => p.ToString() == host.ToString())) - await _mqttBrokerEntryManger.Register(topic, host); - } + /// + /// The Login + /// + /// The channel + /// The deviceId + /// The mqttConnectMessage + /// The + public abstract Task Login(IChannel channel, string deviceId, ConnectMessage mqttConnectMessage); - protected async Task BrokerCancellationReg(string topic) - { - if (Topics.ContainsKey(topic)) - { - if (Topics["topic"].Count() == 0) - await _mqttBrokerEntryManger.CancellationReg(topic, NetUtils.GetHostAddress()); - } - else - { - await _mqttBrokerEntryManger.CancellationReg(topic, NetUtils.GetHostAddress()); - } - } + /// + /// The PingReq + /// + /// The channel + /// The + public abstract ValueTask PingReq(IChannel channel); + /// + /// The Publish + /// + /// The channel + /// The mqttPublishMessage + /// The + public abstract Task Publish(IChannel channel, PublishPacket mqttPublishMessage); + + /// + /// The Publish + /// + /// The deviceId + /// The willMessage + /// The + public abstract Task Publish(string deviceId, MqttWillMessage willMessage); + + /// + /// The Pubrec + /// + /// The channel + /// The messageId + /// The + public abstract Task Pubrec(MqttChannel channel, int messageId); + + /// + /// The Pubrel + /// + /// The channel + /// The messageId + /// The + public abstract Task Pubrel(IChannel channel, int messageId); + + /// + /// The RemotePublishMessage + /// + /// The deviceId + /// The willMessage + /// The public async Task RemotePublishMessage(string deviceId, MqttWillMessage willMessage) { await _mqttRemoteInvokeService.InvokeAsync(new MqttRemoteInvokeContext @@ -168,36 +308,83 @@ await _mqttRemoteInvokeService.InvokeAsync(new MqttRemoteInvokeContext { "message",willMessage} } }, - }, AppConfig.ServerOptions.ExecutionTimeoutInMilliseconds); } - public abstract Task Login(IChannel channel, string deviceId, ConnectMessage mqttConnectMessage); - - public abstract Task Publish(IChannel channel, PublishPacket mqttPublishMessage); - - public abstract Task Pubrec(MqttChannel channel, int messageId); - - public abstract ValueTask PingReq(IChannel channel); - - public abstract Task Pubrel(IChannel channel, int messageId); + /// + /// The RemoveChannel + /// + /// The topic + /// The mqttChannel + /// The + public bool RemoveChannel(string topic, MqttChannel mqttChannel) + { + var result = false; + if (!string.IsNullOrEmpty(topic) && mqttChannel != null) + { + _topics.TryGetValue(topic, out IEnumerable mqttChannels); + var channels = mqttChannels == null ? new List() : mqttChannels.ToList(); + channels.Remove(mqttChannel); + _topics.AddOrUpdate(topic, channels, (key, value) => channels); + result = true; + } + return result; + } + /// + /// The SendWillMsg + /// + /// The willMeaasge + /// The public abstract Task SendWillMsg(MqttWillMessage willMeaasge); + + /// + /// The Suscribe + /// + /// The deviceId + /// The topics + /// The public abstract Task Suscribe(string deviceId, params string[] topics); + /// + /// The UnSubscribe + /// + /// The deviceId + /// The topics + /// The public abstract Task UnSubscribe(string deviceId, params string[] topics); - public abstract Task Publish(string deviceId, MqttWillMessage willMessage); - - public async ValueTask GetDeviceIsOnine(string deviceId) + /// + /// The BrokerCancellationReg + /// + /// The topic + /// The + protected async Task BrokerCancellationReg(string topic) { - bool result = false; - if (!string.IsNullOrEmpty(deviceId)) + if (Topics.ContainsKey(topic)) { - MqttChannels.TryGetValue(deviceId, out MqttChannel mqttChannel); - result = mqttChannel==null?false: await mqttChannel.IsOnine(); + if (Topics["topic"].Count() == 0) + await _mqttBrokerEntryManger.CancellationReg(topic, NetUtils.GetHostAddress()); + } + else + { + await _mqttBrokerEntryManger.CancellationReg(topic, NetUtils.GetHostAddress()); } - return result; } + + /// + /// The RegisterMqttBroker + /// + /// The topic + /// The + protected async Task RegisterMqttBroker(string topic) + { + var addresses = await _mqttBrokerEntryManger.GetMqttBrokerAddress(topic); + var host = NetUtils.GetHostAddress(); + if (addresses == null || !addresses.Any(p => p.ToString() == host.ToString())) + await _mqttBrokerEntryManger.Register(topic, host); + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/IChannelService.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/IChannelService.cs index 921f96ca5..c4ee255f5 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/IChannelService.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/IChannelService.cs @@ -10,14 +10,23 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Services { + #region 接口 + + /// + /// Defines the + /// public interface IChannelService { + #region 方法 + /// - /// 获取mqtt通道 + /// The Close /// - /// - /// - MqttChannel GetMqttChannel(string deviceId); + /// The deviceId + /// The isDisconnect + /// The + Task Close(string deviceId, bool isDisconnect); + /// /// 获取设备是否连接 /// @@ -25,14 +34,44 @@ public interface IChannelService /// /// Task Connect(string deviceId, MqttChannel mqttChannel); + /// - /// 订阅 + /// The GetDeviceId + /// + /// The channel + /// The + ValueTask GetDeviceId(IChannel channel); + + /// + /// The GetDeviceIsOnine + /// + /// The deviceId + /// The + ValueTask GetDeviceIsOnine(string deviceId); + + /// + /// 获取mqtt通道 /// /// - /// 主题列表 /// - Task Suscribe(String deviceId, params string[] topics); + MqttChannel GetMqttChannel(string deviceId); + + /// + /// The Login + /// + /// The channel + /// The deviceId + /// The mqttConnectMessage + /// The Task Login(IChannel channel, string deviceId, ConnectMessage mqttConnectMessage); + + /// + /// The PingReq + /// + /// The channel + /// The + ValueTask PingReq(IChannel channel); + /// /// 发布 /// @@ -40,13 +79,54 @@ public interface IChannelService /// /// Task Publish(IChannel channel, PublishPacket mqttPublishMessage); - ValueTask PingReq(IChannel channel); + + /// + /// The Publish + /// + /// The deviceId + /// The willMessage + /// The Task Publish(string deviceId, MqttWillMessage willMessage); + + /// + /// The Pubrec + /// + /// The channel + /// The messageId + /// The + Task Pubrec(MqttChannel channel, int messageId); + + /// + /// The Pubrel + /// + /// The channel + /// The messageId + /// The + Task Pubrel(IChannel channel, int messageId); + + /// + /// The RemotePublishMessage + /// + /// The deviceId + /// The willMessage + /// The Task RemotePublishMessage(string deviceId, MqttWillMessage willMessage); - Task Close(string deviceId, bool isDisconnect); - ValueTask GetDeviceIsOnine(string deviceId); + + /// + /// The SendWillMsg + /// + /// The willMeaasge + /// The Task SendWillMsg(MqttWillMessage willMeaasge); - ValueTask GetDeviceId(IChannel channel); + + /// + /// 订阅 + /// + /// + /// 主题列表 + /// + Task Suscribe(String deviceId, params string[] topics); + /// /// 取消订阅 /// @@ -54,7 +134,9 @@ public interface IChannelService /// /// Task UnSubscribe(string deviceId, params string[] topics); - Task Pubrel(IChannel channel, int messageId); - Task Pubrec(MqttChannel channel, int messageId); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/IClientSessionService.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/IClientSessionService.cs index 9c9ad7556..9ed5e5a5b 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/IClientSessionService.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/IClientSessionService.cs @@ -6,10 +6,31 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Services { - public interface IClientSessionService + #region 接口 + + /// + /// Defines the + /// + public interface IClientSessionService { - void SaveMessage(string deviceId, SessionMessage sessionMessage); + #region 方法 + /// + /// The GetMessages + /// + /// The deviceId + /// The ConcurrentQueue GetMessages(string deviceId); + + /// + /// The SaveMessage + /// + /// The deviceId + /// The sessionMessage + void SaveMessage(string deviceId, SessionMessage sessionMessage); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/IMessagePushService.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/IMessagePushService.cs index 43e43287e..49ce6e100 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/IMessagePushService.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/IMessagePushService.cs @@ -9,20 +9,76 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Services { + #region 接口 + + /// + /// Defines the + /// public interface IMessagePushService { - Task WriteWillMsg(MqttChannel mqttChannel, MqttWillMessage willMeaasge); - - Task SendQosConfirmMsg(QualityOfService qos, MqttChannel mqttChannel, string topic, byte[] bytes); + #region 方法 + /// + /// The SendPubBack + /// + /// The channel + /// The messageId + /// The Task SendPubBack(IChannel channel, int messageId); + /// + /// The SendPubRec + /// + /// The mqttChannel + /// The messageId + /// The Task SendPubRec(MqttChannel mqttChannel, int messageId); + /// + /// The SendPubRel + /// + /// The channel + /// The messageId + /// The Task SendPubRel(IChannel channel, int messageId); + /// + /// The SendQos0Msg + /// + /// The channel + /// The topic + /// The byteBuf + /// The + Task SendQos0Msg(IChannel channel, String topic, byte[] byteBuf); + + /// + /// The SendQosConfirmMsg + /// + /// The qos + /// The mqttChannel + /// The topic + /// The bytes + /// The + Task SendQosConfirmMsg(QualityOfService qos, MqttChannel mqttChannel, string topic, byte[] bytes); + + /// + /// The SendToPubComp + /// + /// The channel + /// The messageId + /// The Task SendToPubComp(IChannel channel, int messageId); - Task SendQos0Msg(IChannel channel, String topic, byte[] byteBuf); + /// + /// The WriteWillMsg + /// + /// The mqttChannel + /// The willMeaasge + /// The + Task WriteWillMsg(MqttChannel mqttChannel, MqttWillMessage willMeaasge); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/IWillService.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/IWillService.cs index fc99ed19c..b35ed9f00 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/IWillService.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/IWillService.cs @@ -6,12 +6,37 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Services { + #region 接口 + + /// + /// Defines the + /// public interface IWillService { + #region 方法 + + /// + /// The Add + /// + /// The deviceid + /// The willMessage void Add(string deviceid, MqttWillMessage willMessage); + /// + /// The Remove + /// + /// The deviceid + void Remove(string deviceid); + + /// + /// The SendWillMessage + /// + /// The deviceId + /// The Task SendWillMessage(string deviceId); - void Remove(string deviceid); + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/Implementation/ClientSessionService.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/Implementation/ClientSessionService.cs index 768d5b48d..c8224d73c 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/Implementation/ClientSessionService.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/Implementation/ClientSessionService.cs @@ -4,17 +4,39 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Services.Implementation { - public class ClientSessionService: IClientSessionService + /// + /// Defines the + /// + public class ClientSessionService : IClientSessionService { - private readonly ConcurrentDictionary> _clientsessionMessages = + #region 字段 + + /// + /// Defines the _clientsessionMessages + /// + private readonly ConcurrentDictionary> _clientsessionMessages = new ConcurrentDictionary>(); + #endregion 字段 + + #region 方法 + + /// + /// The GetMessages + /// + /// The deviceId + /// The public ConcurrentQueue GetMessages(string deviceId) { _clientsessionMessages.TryGetValue(deviceId, out ConcurrentQueue messages); return messages; } + /// + /// The SaveMessage + /// + /// The deviceId + /// The sessionMessage public void SaveMessage(string deviceId, SessionMessage sessionMessage) { _clientsessionMessages.TryGetValue(deviceId, out ConcurrentQueue sessionMessages); @@ -24,5 +46,7 @@ public void SaveMessage(string deviceId, SessionMessage sessionMessage) return sessionMessages; }); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/Implementation/MessagePushService.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/Implementation/MessagePushService.cs index 5d6e9cccc..7d0a154df 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/Implementation/MessagePushService.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/Implementation/MessagePushService.cs @@ -13,87 +13,56 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Services.Implementation { - public class MessagePushService:IMessagePushService + /// + /// Defines the + /// + public class MessagePushService : IMessagePushService { + #region 字段 + + /// + /// Defines the _scanRunnable + /// private readonly ScanRunnable _scanRunnable; - public MessagePushService(ScanRunnable scanRunnable) - { - _scanRunnable = scanRunnable; - } - public async Task WriteWillMsg(MqttChannel mqttChannel, MqttWillMessage willMeaasge) - { - switch (willMeaasge.Qos) - { - case 0: - await SendQos0Msg(mqttChannel.Channel, willMeaasge.Topic, Encoding.Default.GetBytes(willMeaasge.WillMessage)); - break; - case 1: // qos1 - await SendQosConfirmMsg(QualityOfService.AtLeastOnce, mqttChannel, willMeaasge.Topic, Encoding.Default.GetBytes(willMeaasge.WillMessage)); - break; - case 2: // qos2 - await SendQosConfirmMsg(QualityOfService.ExactlyOnce, mqttChannel, willMeaasge.Topic, Encoding.Default.GetBytes(willMeaasge.WillMessage)); - break; - } - - } - public async Task SendQosConfirmMsg(QualityOfService qos, MqttChannel mqttChannel, string topic, byte[] bytes) - { - if (mqttChannel.IsLogin()) - { - int messageId = MessageIdGenerater.GenerateId(); - switch (qos) - { - case QualityOfService.AtLeastOnce: - mqttChannel.AddMqttMessage(messageId, await SendQos1Msg(mqttChannel.Channel, topic, false, bytes, messageId)); - break; - case QualityOfService.ExactlyOnce: - mqttChannel.AddMqttMessage(messageId,await SendQos2Msg(mqttChannel.Channel, topic, false, bytes, messageId)); - break; - } - } + #endregion 字段 - } + #region 构造函数 - private async Task SendQos1Msg(IChannel channel, String topic, bool isDup, byte[] byteBuf, int messageId) + /// + /// Initializes a new instance of the class. + /// + /// The scanRunnable + public MessagePushService(ScanRunnable scanRunnable) { - var mqttPublishMessage = new PublishPacket(QualityOfService.AtMostOnce, true, true); - mqttPublishMessage.TopicName = topic; - mqttPublishMessage.PacketId = messageId; - mqttPublishMessage.Payload = Unpooled.WrappedBuffer(byteBuf); - await channel.WriteAndFlushAsync(mqttPublishMessage); - return Enqueue(channel, messageId, topic, byteBuf, (int)QualityOfService.AtLeastOnce, ConfirmStatus.PUB); + _scanRunnable = scanRunnable; } - private async Task SendQos2Msg(IChannel channel, String topic, bool isDup, byte[] byteBuf, int messageId) - { - var mqttPublishMessage = new PublishPacket(QualityOfService.AtMostOnce, true, true); - mqttPublishMessage.TopicName = topic; - mqttPublishMessage.PacketId = messageId; - mqttPublishMessage.Payload = Unpooled.WrappedBuffer(byteBuf); - await channel.WriteAndFlushAsync(mqttPublishMessage); - return Enqueue(channel, messageId, topic, byteBuf, (int)QualityOfService.ExactlyOnce, ConfirmStatus.PUB); - } - - private async Task SendQos0Msg(IChannel channel, String topic, byte[] byteBuf, int messageId) - { - if (channel != null) - { - var mqttPublishMessage = new PublishPacket(QualityOfService.AtMostOnce, true, true); - mqttPublishMessage.TopicName = topic; - mqttPublishMessage.Payload = Unpooled.WrappedBuffer(byteBuf); - await channel.WriteAndFlushAsync(mqttPublishMessage); - } - } + #endregion 构造函数 + + #region 方法 + /// + /// The SendPubBack + /// + /// The channel + /// The messageId + /// The public async Task SendPubBack(IChannel channel, int messageId) { - var mqttPubAckMessage = new PubAckPacket() { + var mqttPubAckMessage = new PubAckPacket() + { PacketId = messageId }; - await channel.WriteAndFlushAsync(mqttPubAckMessage); + await channel.WriteAndFlushAsync(mqttPubAckMessage); } + /// + /// The SendPubRec + /// + /// The mqttChannel + /// The messageId + /// The public async Task SendPubRec(MqttChannel mqttChannel, int messageId) { var mqttPubAckMessage = new PubRecPacket() @@ -105,37 +74,115 @@ public async Task SendPubRec(MqttChannel mqttChannel, int messageId) var sendMqttMessage = Enqueue(channel, messageId, null, null, 1, ConfirmStatus.PUBREC); mqttChannel.AddMqttMessage(messageId, sendMqttMessage); } - + + /// + /// The SendPubRel + /// + /// The channel + /// The messageId + /// The public async Task SendPubRel(IChannel channel, int messageId) { var mqttPubAckMessage = new PubRelPacket() { PacketId = messageId - }; - await channel.WriteAndFlushAsync(mqttPubAckMessage); + }; + await channel.WriteAndFlushAsync(mqttPubAckMessage); } - + + /// + /// The SendQos0Msg + /// + /// The channel + /// The topic + /// The byteBuf + /// The + public async Task SendQos0Msg(IChannel channel, String topic, byte[] byteBuf) + { + if (channel != null) + { + await SendQos0Msg(channel, topic, byteBuf, 0); + } + } + + /// + /// The SendQosConfirmMsg + /// + /// The qos + /// The mqttChannel + /// The topic + /// The bytes + /// The + public async Task SendQosConfirmMsg(QualityOfService qos, MqttChannel mqttChannel, string topic, byte[] bytes) + { + if (mqttChannel.IsLogin()) + { + int messageId = MessageIdGenerater.GenerateId(); + switch (qos) + { + case QualityOfService.AtLeastOnce: + mqttChannel.AddMqttMessage(messageId, await SendQos1Msg(mqttChannel.Channel, topic, false, bytes, messageId)); + break; + + case QualityOfService.ExactlyOnce: + mqttChannel.AddMqttMessage(messageId, await SendQos2Msg(mqttChannel.Channel, topic, false, bytes, messageId)); + break; + } + } + } + + /// + /// The SendToPubComp + /// + /// The channel + /// The messageId + /// The public async Task SendToPubComp(IChannel channel, int messageId) { var mqttPubAckMessage = new PubCompPacket() { PacketId = messageId }; - await channel.WriteAndFlushAsync(mqttPubAckMessage); + await channel.WriteAndFlushAsync(mqttPubAckMessage); } - - public async Task SendQos0Msg(IChannel channel, String topic, byte[] byteBuf) + /// + /// The WriteWillMsg + /// + /// The mqttChannel + /// The willMeaasge + /// The + public async Task WriteWillMsg(MqttChannel mqttChannel, MqttWillMessage willMeaasge) { - if (channel != null) + switch (willMeaasge.Qos) { - await SendQos0Msg(channel, topic, byteBuf, 0); + case 0: + await SendQos0Msg(mqttChannel.Channel, willMeaasge.Topic, Encoding.Default.GetBytes(willMeaasge.WillMessage)); + break; + + case 1: // qos1 + await SendQosConfirmMsg(QualityOfService.AtLeastOnce, mqttChannel, willMeaasge.Topic, Encoding.Default.GetBytes(willMeaasge.WillMessage)); + break; + + case 2: // qos2 + await SendQosConfirmMsg(QualityOfService.ExactlyOnce, mqttChannel, willMeaasge.Topic, Encoding.Default.GetBytes(willMeaasge.WillMessage)); + break; } } + /// + /// The Enqueue + /// + /// The channel + /// The messageId + /// The topic + /// The datas + /// The mqttQoS + /// The confirmStatus + /// The private SendMqttMessage Enqueue(IChannel channel, int messageId, String topic, byte[] datas, int mqttQoS, ConfirmStatus confirmStatus) { - var message = new SendMqttMessage + var message = new SendMqttMessage { ByteBuf = datas, Channel = channel, @@ -149,5 +196,63 @@ private SendMqttMessage Enqueue(IChannel channel, int messageId, String topic, b return message; } + /// + /// The SendQos0Msg + /// + /// The channel + /// The topic + /// The byteBuf + /// The messageId + /// The + private async Task SendQos0Msg(IChannel channel, String topic, byte[] byteBuf, int messageId) + { + if (channel != null) + { + var mqttPublishMessage = new PublishPacket(QualityOfService.AtMostOnce, true, true); + mqttPublishMessage.TopicName = topic; + mqttPublishMessage.Payload = Unpooled.WrappedBuffer(byteBuf); + await channel.WriteAndFlushAsync(mqttPublishMessage); + } + } + + /// + /// The SendQos1Msg + /// + /// The channel + /// The topic + /// The isDup + /// The byteBuf + /// The messageId + /// The + private async Task SendQos1Msg(IChannel channel, String topic, bool isDup, byte[] byteBuf, int messageId) + { + var mqttPublishMessage = new PublishPacket(QualityOfService.AtMostOnce, true, true); + mqttPublishMessage.TopicName = topic; + mqttPublishMessage.PacketId = messageId; + mqttPublishMessage.Payload = Unpooled.WrappedBuffer(byteBuf); + await channel.WriteAndFlushAsync(mqttPublishMessage); + return Enqueue(channel, messageId, topic, byteBuf, (int)QualityOfService.AtLeastOnce, ConfirmStatus.PUB); + } + + /// + /// The SendQos2Msg + /// + /// The channel + /// The topic + /// The isDup + /// The byteBuf + /// The messageId + /// The + private async Task SendQos2Msg(IChannel channel, String topic, bool isDup, byte[] byteBuf, int messageId) + { + var mqttPublishMessage = new PublishPacket(QualityOfService.AtMostOnce, true, true); + mqttPublishMessage.TopicName = topic; + mqttPublishMessage.PacketId = messageId; + mqttPublishMessage.Payload = Unpooled.WrappedBuffer(byteBuf); + await channel.WriteAndFlushAsync(mqttPublishMessage); + return Enqueue(channel, messageId, topic, byteBuf, (int)QualityOfService.ExactlyOnce, ConfirmStatus.PUB); + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/Implementation/MqttChannelService.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/Implementation/MqttChannelService.cs index ba8dc47f5..78b6dbfa9 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/Implementation/MqttChannelService.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/Implementation/MqttChannelService.cs @@ -1,27 +1,62 @@ -using System; -using System.Collections.Generic; -using System.Text; +using DotNetty.Buffers; +using DotNetty.Codecs.Mqtt.Packets; using DotNetty.Transport.Channels; +using Microsoft.Extensions.Logging; +using Surging.Core.CPlatform.Ids; using Surging.Core.Protocol.Mqtt.Internal.Channel; using Surging.Core.Protocol.Mqtt.Internal.Enums; using Surging.Core.Protocol.Mqtt.Internal.Messages; -using System.Linq; +using Surging.Core.Protocol.Mqtt.Internal.Runtime; +using System; using System.Collections.Concurrent; -using DotNetty.Codecs.Mqtt.Packets; -using Microsoft.Extensions.Logging; -using DotNetty.Buffers; +using System.Collections.Generic; +using System.Linq; +using System.Text; using System.Threading.Tasks; -using Surging.Core.Protocol.Mqtt.Internal.Runtime; -using Surging.Core.CPlatform.Ids; namespace Surging.Core.Protocol.Mqtt.Internal.Services.Implementation { + /// + /// Defines the + /// public class MqttChannelService : AbstractChannelService { - private readonly IMessagePushService _messagePushService; + #region 字段 + + /// + /// Defines the _clientSessionService + /// private readonly IClientSessionService _clientSessionService; + + /// + /// Defines the _logger + /// private readonly ILogger _logger; + + /// + /// Defines the _messagePushService + /// + private readonly IMessagePushService _messagePushService; + + /// + /// Defines the _willService + /// private readonly IWillService _willService; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The messagePushService + /// The clientSessionService + /// The logger + /// The willService + /// The mqttBrokerEntryManger + /// The mqttRemoteInvokeService + /// The serviceIdGenerator public MqttChannelService(IMessagePushService messagePushService, IClientSessionService clientSessionService, ILogger logger, IWillService willService, IMqttBrokerEntryManger mqttBrokerEntryManger, @@ -38,6 +73,16 @@ public MqttChannelService(IMessagePushService messagePushService, IClientSession _willService = willService; } + #endregion 构造函数 + + #region 方法 + + /// + /// The Close + /// + /// The deviceId + /// The isDisconnect + /// The public override async Task Close(string deviceId, bool isDisconnect) { if (!string.IsNullOrEmpty(deviceId)) @@ -84,10 +129,15 @@ public override async Task Close(string deviceId, bool isDisconnect) await _willService.SendWillMessage(deviceId); } } - } } + /// + /// The Connect + /// + /// The deviceId + /// The channel + /// The public override async Task Connect(string deviceId, MqttChannel channel) { var mqttChannel = GetMqttChannel(deviceId); @@ -106,13 +156,19 @@ public override async Task Connect(string deviceId, MqttChannel channel) Topics.AddOrUpdate(topic, newValue, (key, value) => newValue); } } - } } MqttChannels.AddOrUpdate(deviceId, channel, (k, v) => channel); return true; } + /// + /// The Login + /// + /// The channel + /// The deviceId + /// The mqttConnectMessage + /// The public override async Task Login(IChannel channel, string deviceId, ConnectMessage mqttConnectMessage) { channel.GetAttribute(LoginAttrKey).Set("login"); @@ -120,6 +176,28 @@ public override async Task Login(IChannel channel, string deviceId, ConnectMessa await Init(channel, mqttConnectMessage); } + /// + /// The PingReq + /// + /// The channel + /// The + public override async ValueTask PingReq(IChannel channel) + { + if (MqttChannels.TryGetValue(await this.GetDeviceId(channel), out MqttChannel mqttChannel)) + { + if (mqttChannel.IsLogin()) + { + mqttChannel.PingReqTime = DateTime.Now; + } + } + } + + /// + /// The Publish + /// + /// The channel + /// The mqttPublishMessage + /// The public override async Task Publish(IChannel channel, PublishPacket mqttPublishMessage) { MqttChannel mqttChannel = GetMqttChannel(await this.GetDeviceId(channel)); @@ -135,6 +213,7 @@ public override async Task Publish(IChannel channel, PublishPacket mqttPublishMe case QualityOfService.AtLeastOnce: await _messagePushService.SendPubBack(channel, messageId); break; + case QualityOfService.ExactlyOnce: await Pubrec(mqttChannel, messageId); break; @@ -159,6 +238,12 @@ public override async Task Publish(IChannel channel, PublishPacket mqttPublishMe } } + /// + /// The Publish + /// + /// The deviceId + /// The willMessage + /// The public override async Task Publish(string deviceId, MqttWillMessage willMessage) { if (!string.IsNullOrEmpty(deviceId)) @@ -178,54 +263,23 @@ public override async Task Publish(string deviceId, MqttWillMessage willMessage) }, willMessage.Qos == 0 ? true : false); } - private async Task PushMessage(string topic, int qos, byte[] bytes, bool isRetain) - { - Topics.TryGetValue(topic, out IEnumerable mqttChannels); - if (mqttChannels != null && mqttChannels.Any()) - { - foreach (var mqttChannel in mqttChannels) - { - if (await mqttChannel.IsOnine()) - { - if (mqttChannel.IsActive()) - { - await SendMessage(mqttChannel, qos, - topic, bytes); - } - else - { - if (!mqttChannel.CleanSession && !isRetain) - { - _clientSessionService.SaveMessage(mqttChannel.ClientId, - new SessionMessage - { - Message = bytes, - QoS = qos, - Topic = topic - }); - break; - } - } - } - else - { - _clientSessionService.SaveMessage(mqttChannel.ClientId, - new SessionMessage - { - Message = bytes, - QoS = qos, - Topic = topic - }); - } - } - } - } - + /// + /// The Pubrec + /// + /// The channel + /// The messageId + /// The public override async Task Pubrec(MqttChannel channel, int messageId) { await _messagePushService.SendPubRec(channel, messageId); } + /// + /// The Pubrel + /// + /// The channel + /// The messageId + /// The public override async Task Pubrel(IChannel channel, int messageId) { if (MqttChannels.TryGetValue(await this.GetDeviceId(channel), out MqttChannel mqttChannel)) @@ -238,17 +292,48 @@ public override async Task Pubrel(IChannel channel, int messageId) } } - public override async ValueTask PingReq(IChannel channel) + /// + /// The RemoveSubTopic + /// + /// The mqttChannel + /// The + public IEnumerable RemoveSubTopic(MqttChannel mqttChannel) { - if (MqttChannels.TryGetValue(await this.GetDeviceId(channel), out MqttChannel mqttChannel)) + IEnumerable topics = mqttChannel.Topics; + foreach (var topic in topics) { - if (mqttChannel.IsLogin()) + Topics.TryGetValue(topic, out IEnumerable comparisonValue); + var newValue = comparisonValue.Where(p => p != mqttChannel); + Topics.TryUpdate(topic, newValue, comparisonValue); + } + return topics; + } + + /// + /// The SendRetain + /// + /// The topic + /// The mqttChannel + /// The + public async Task SendRetain(string topic, MqttChannel mqttChannel) + { + Retain.TryGetValue(topic, out ConcurrentQueue retainMessages); + if (retainMessages != null && !retainMessages.IsEmpty) + { + var messages = retainMessages.GetEnumerator(); + while (messages.MoveNext()) { - mqttChannel.PingReqTime = DateTime.Now; - } + var retainMessage = messages.Current; + await SendMessage(mqttChannel, retainMessage.QoS, topic, retainMessage.ByteBuf); + }; } } + /// + /// The SendWillMsg + /// + /// The willMeaasge + /// The public override async Task SendWillMsg(MqttWillMessage willMeaasge) { Topics.TryGetValue(willMeaasge.Topic, out IEnumerable mqttChannels); @@ -271,8 +356,14 @@ public override async Task SendWillMsg(MqttWillMessage willMeaasge) } } } - } + } + /// + /// The Suscribe + /// + /// The deviceId + /// The topics + /// The public override async Task Suscribe(string deviceId, params string[] topics) { if (!string.IsNullOrEmpty(deviceId)) @@ -292,6 +383,12 @@ public override async Task Suscribe(string deviceId, params string[] topics) } } + /// + /// The UnSubscribe + /// + /// The deviceId + /// The topics + /// The public override async Task UnSubscribe(string deviceId, params string[] topics) { if (MqttChannels.TryGetValue(deviceId, out MqttChannel mqttChannel)) @@ -301,63 +398,15 @@ public override async Task UnSubscribe(string deviceId, params string[] topics) RemoveChannel(topic, mqttChannel); await BrokerCancellationReg(topic); } - } - } - - public async Task SendRetain(string topic, MqttChannel mqttChannel) - { - Retain.TryGetValue(topic, out ConcurrentQueue retainMessages); - if (retainMessages != null && !retainMessages.IsEmpty) - { - var messages = retainMessages.GetEnumerator(); - while (messages.MoveNext()) - { - var retainMessage = messages.Current; - await SendMessage(mqttChannel, retainMessage.QoS, topic, retainMessage.ByteBuf); - }; - } - } - - private void SaveRetain(String topic, RetainMessage retainMessage, bool isClean) - { - Retain.TryGetValue(topic, out ConcurrentQueue retainMessages); - if (retainMessages == null) retainMessages=new ConcurrentQueue(); - if (!retainMessages.IsEmpty && isClean) - { - retainMessages.Clear(); - } - retainMessages.Enqueue(retainMessage); - Retain.AddOrUpdate(topic, retainMessages,(key,value)=> retainMessages); - } - - public IEnumerable RemoveSubTopic(MqttChannel mqttChannel) - { - IEnumerable topics = mqttChannel.Topics; - foreach (var topic in topics) - { - Topics.TryGetValue(topic, out IEnumerable comparisonValue); - var newValue = comparisonValue.Where(p => p != mqttChannel); - Topics.TryUpdate(topic, newValue, comparisonValue); - } - return topics; - } - - private async Task SendMessage(MqttChannel mqttChannel,int qos, string topic,byte [] byteBuf) - { - switch (qos) - { - case 0: - await _messagePushService.SendQos0Msg(mqttChannel.Channel, topic, byteBuf); - break; - case 1: - await _messagePushService.SendQosConfirmMsg(QualityOfService.AtLeastOnce, mqttChannel, topic, byteBuf); - break; - case 2: - await _messagePushService.SendQosConfirmMsg(QualityOfService.ExactlyOnce, mqttChannel, topic, byteBuf); - break; } } + /// + /// The Init + /// + /// The channel + /// The mqttConnectMessage + /// The private async Task Init(IChannel channel, ConnectMessage mqttConnectMessage) { String deviceId = await GetDeviceId(channel); @@ -389,7 +438,6 @@ private async Task Init(IChannel channel, ConnectMessage mqttConnectMessage) WillRetain = mqttConnectMessage.WillRetain, Topic = mqttConnectMessage.WillTopic, WillMessage = Encoding.UTF8.GetString(mqttConnectMessage.WillMessage) - }; _willService.Add(mqttConnectMessage.ClientId, willMessage); } @@ -407,7 +455,7 @@ await channel.WriteAndFlushAsync(new ConnAckPacket { ReturnCode = ConnectReturnCode.Accepted, SessionPresent = !mqttConnectMessage.CleanSession - }); + }); var sessionMessages = _clientSessionService.GetMessages(mqttConnectMessage.ClientId); if (sessionMessages != null && !sessionMessages.IsEmpty) { @@ -418,9 +466,11 @@ await channel.WriteAndFlushAsync(new ConnAckPacket case 0: await _messagePushService.SendQos0Msg(channel, sessionMessage.Topic, sessionMessage.Message); break; + case 1: await _messagePushService.SendQosConfirmMsg(QualityOfService.AtLeastOnce, GetMqttChannel(deviceId), sessionMessage.Topic, sessionMessage.Message); break; + case 2: await _messagePushService.SendQosConfirmMsg(QualityOfService.ExactlyOnce, GetMqttChannel(deviceId), sessionMessage.Topic, sessionMessage.Message); break; @@ -429,5 +479,102 @@ await channel.WriteAndFlushAsync(new ConnAckPacket } } } + + /// + /// The PushMessage + /// + /// The topic + /// The qos + /// The bytes + /// The isRetain + /// The + private async Task PushMessage(string topic, int qos, byte[] bytes, bool isRetain) + { + Topics.TryGetValue(topic, out IEnumerable mqttChannels); + if (mqttChannels != null && mqttChannels.Any()) + { + foreach (var mqttChannel in mqttChannels) + { + if (await mqttChannel.IsOnine()) + { + if (mqttChannel.IsActive()) + { + await SendMessage(mqttChannel, qos, + topic, bytes); + } + else + { + if (!mqttChannel.CleanSession && !isRetain) + { + _clientSessionService.SaveMessage(mqttChannel.ClientId, + new SessionMessage + { + Message = bytes, + QoS = qos, + Topic = topic + }); + break; + } + } + } + else + { + _clientSessionService.SaveMessage(mqttChannel.ClientId, + new SessionMessage + { + Message = bytes, + QoS = qos, + Topic = topic + }); + } + } + } + } + + /// + /// The SaveRetain + /// + /// The topic + /// The retainMessage + /// The isClean + private void SaveRetain(String topic, RetainMessage retainMessage, bool isClean) + { + Retain.TryGetValue(topic, out ConcurrentQueue retainMessages); + if (retainMessages == null) retainMessages = new ConcurrentQueue(); + if (!retainMessages.IsEmpty && isClean) + { + retainMessages.Clear(); + } + retainMessages.Enqueue(retainMessage); + Retain.AddOrUpdate(topic, retainMessages, (key, value) => retainMessages); + } + + /// + /// The SendMessage + /// + /// The mqttChannel + /// The qos + /// The topic + /// The byteBuf + /// The + private async Task SendMessage(MqttChannel mqttChannel, int qos, string topic, byte[] byteBuf) + { + switch (qos) + { + case 0: + await _messagePushService.SendQos0Msg(mqttChannel.Channel, topic, byteBuf); + break; + + case 1: + await _messagePushService.SendQosConfirmMsg(QualityOfService.AtLeastOnce, mqttChannel, topic, byteBuf); + break; + + case 2: + await _messagePushService.SendQosConfirmMsg(QualityOfService.ExactlyOnce, mqttChannel, topic, byteBuf); + break; + } + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/Implementation/WillService.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/Implementation/WillService.cs index 7764ae8e3..51ec76bed 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/Implementation/WillService.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/Implementation/WillService.cs @@ -9,23 +9,71 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Services.Implementation { - public class WillService: IWillService + /// + /// Defines the + /// + public class WillService : IWillService { - private ConcurrentDictionary willMeaasges = new ConcurrentDictionary(); + #region 字段 + + /// + /// Defines the _logger + /// private readonly ILogger _logger; + + /// + /// Defines the _serviceProvider + /// private readonly CPlatformContainer _serviceProvider; - public WillService(ILogger logger, CPlatformContainer serviceProvider) + /// + /// Defines the willMeaasges + /// + private ConcurrentDictionary willMeaasges = new ConcurrentDictionary(); + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The logger + /// The serviceProvider + public WillService(ILogger logger, CPlatformContainer serviceProvider) { _logger = logger; _serviceProvider = serviceProvider; } + #endregion 构造函数 + + #region 方法 + + /// + /// The Add + /// + /// The deviceid + /// The willMessage public void Add(string deviceid, MqttWillMessage willMessage) { - willMeaasges.AddOrUpdate(deviceid, willMessage,(id,message)=>willMessage); + willMeaasges.AddOrUpdate(deviceid, willMessage, (id, message) => willMessage); } - + + /// + /// The Remove + /// + /// The deviceid + public void Remove(string deviceid) + { + willMeaasges.TryRemove(deviceid, out MqttWillMessage message); + } + + /// + /// The SendWillMessage + /// + /// The deviceId + /// The public async Task SendWillMessage(string deviceId) { if (!string.IsNullOrEmpty(deviceId)) @@ -33,20 +81,17 @@ public async Task SendWillMessage(string deviceId) willMeaasges.TryGetValue(deviceId, out MqttWillMessage willMessage); if (willMeaasges != null) { - await _serviceProvider.GetInstances().SendWillMsg(willMessage); + await _serviceProvider.GetInstances().SendWillMsg(willMessage); if (!willMessage.WillRetain) { Remove(deviceId); if (_logger.IsEnabled(LogLevel.Information)) _logger.LogInformation($"deviceId:{deviceId} 的遗嘱消息[" + willMessage.WillMessage + "] 已经被删除"); - } } } } - - public void Remove(string deviceid) { - willMeaasges.TryRemove(deviceid,out MqttWillMessage message); - } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/MqttBehavior.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/MqttBehavior.cs index 0d62a3055..b058e8c48 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/MqttBehavior.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Internal/Services/MqttBehavior.cs @@ -13,18 +13,50 @@ namespace Surging.Core.Protocol.Mqtt.Internal.Services { - public abstract class MqttBehavior: ServiceBase + /// + /// Defines the + /// + public abstract class MqttBehavior : ServiceBase { - public async Task Publish(string deviceId, MqttWillMessage willMessage) + #region 方法 + + /// + /// The Authorized + /// + /// The username + /// The password + /// The + public abstract Task Authorized(string username, string password); + + /// + /// The GetDeviceIsOnine + /// + /// The deviceId + /// The + public async Task GetDeviceIsOnine(string deviceId) { - await GetService().Publish(deviceId, willMessage); + return await this.GetService().GetDeviceIsOnine(deviceId); } - public async Task RemotePublish(string deviceId, MqttWillMessage willMessage) + /// + /// The GetService + /// + /// + /// The + public override T GetService() { - await GetService().RemotePublishMessage(deviceId, willMessage); + if (ServiceLocator.Current.IsRegistered()) + return base.GetService(); + else + return ServiceLocator.GetService().CreateProxy(); } + /// + /// The GetService + /// + /// + /// The key + /// The public override T GetService(string key) { if (ServiceLocator.Current.IsRegisteredWithKey(key)) @@ -33,15 +65,25 @@ public override T GetService(string key) return ServiceLocator.GetService().CreateProxy(key); } - public override T GetService() + /// + /// The GetService + /// + /// The key + /// The type + /// The + public override object GetService(string key, Type type) { - if (ServiceLocator.Current.IsRegistered()) - return base.GetService(); + if (ServiceLocator.Current.IsRegisteredWithKey(key, type)) + return base.GetService(key, type); else - return ServiceLocator.GetService().CreateProxy(); - + return ServiceLocator.GetService().CreateProxy(key, type); } + /// + /// The GetService + /// + /// The type + /// The public override object GetService(Type type) { if (ServiceLocator.Current.IsRegistered(type)) @@ -50,25 +92,37 @@ public override object GetService(Type type) return ServiceLocator.GetService().CreateProxy(type); } - public override object GetService(string key, Type type) + /// + /// The Publish + /// + /// The event + public void Publish(IntegrationEvent @event) { - if (ServiceLocator.Current.IsRegisteredWithKey(key, type)) - return base.GetService(key, type); - else - return ServiceLocator.GetService().CreateProxy(key, type); - + GetService().Publish(@event); } - public void Publish(IntegrationEvent @event) + /// + /// The Publish + /// + /// The deviceId + /// The willMessage + /// The + public async Task Publish(string deviceId, MqttWillMessage willMessage) { - GetService().Publish(@event); + await GetService().Publish(deviceId, willMessage); } - public async Task GetDeviceIsOnine(string deviceId) + /// + /// The RemotePublish + /// + /// The deviceId + /// The willMessage + /// The + public async Task RemotePublish(string deviceId, MqttWillMessage willMessage) { - return await this.GetService().GetDeviceIsOnine(deviceId); + await GetService().RemotePublishMessage(deviceId, willMessage); } - public abstract Task Authorized(string username, string password); + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/MqttHandlerServiceBase.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/MqttHandlerServiceBase.cs index 34dacefb2..9f1961368 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/MqttHandlerServiceBase.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/MqttHandlerServiceBase.cs @@ -8,41 +8,133 @@ namespace Surging.Core.Protocol.Mqtt { - public abstract class MqttHandlerServiceBase + /// + /// Defines the + /// + public abstract class MqttHandlerServiceBase { + #region 字段 + + /// + /// Defines the _handler + /// protected readonly Action _handler; - public MqttHandlerServiceBase( Action handler) + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The handler + public MqttHandlerServiceBase(Action handler) { _handler = handler; } - public abstract void Login(IChannelHandlerContext context, ConnectPacket packet); + #endregion 构造函数 + + #region 方法 + /// + /// The ConnAck + /// + /// The context + /// The packet public abstract void ConnAck(IChannelHandlerContext context, ConnAckPacket packet); + /// + /// The Disconnect + /// + /// The context + /// The packet public abstract void Disconnect(IChannelHandlerContext context, DisconnectPacket packet); + /// + /// The Login + /// + /// The context + /// The packet + public abstract void Login(IChannelHandlerContext context, ConnectPacket packet); + + /// + /// The PingReq + /// + /// The context + /// The packet public abstract void PingReq(IChannelHandlerContext context, PingReqPacket packet); + /// + /// The PingResp + /// + /// The context + /// The packet public abstract void PingResp(IChannelHandlerContext context, PingRespPacket packet); + /// + /// The PubAck + /// + /// The context + /// The packet public abstract void PubAck(IChannelHandlerContext context, PubAckPacket packet); + /// + /// The PubComp + /// + /// The context + /// The packet public abstract void PubComp(IChannelHandlerContext context, PubCompPacket packet); + /// + /// The Publish + /// + /// The context + /// The packet + public abstract void Publish(IChannelHandlerContext context, PublishPacket packet); + + /// + /// The PubRec + /// + /// The context + /// The packet public abstract void PubRec(IChannelHandlerContext context, PubRecPacket packet); + /// + /// The PubRel + /// + /// The context + /// The packet public abstract void PubRel(IChannelHandlerContext context, PubRelPacket packet); - public abstract void Publish(IChannelHandlerContext context, PublishPacket packet); - + /// + /// The SubAck + /// + /// The context + /// The packet public abstract void SubAck(IChannelHandlerContext context, SubAckPacket packet); + /// + /// The Subscribe + /// + /// The context + /// The packet public abstract void Subscribe(IChannelHandlerContext context, SubscribePacket packet); + /// + /// The UnsubAck + /// + /// The context + /// The packet public abstract void UnsubAck(IChannelHandlerContext context, UnsubAckPacket packet); + /// + /// The Unsubscribe + /// + /// The context + /// The packet public abstract void Unsubscribe(IChannelHandlerContext context, UnsubscribePacket packet); + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/MqttProtocolModule.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/MqttProtocolModule.cs index 57e1a4cb1..dd8635cca 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/MqttProtocolModule.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/MqttProtocolModule.cs @@ -19,8 +19,17 @@ namespace Surging.Core.Protocol.Mqtt { + /// + /// Defines the + /// public class MqttProtocolModule : EnginePartModule { + #region 方法 + + /// + /// The Initialize + /// + /// The context public override void Initialize(AppModuleContext context) { base.Initialize(context); @@ -72,6 +81,10 @@ protected override void RegisterBuilder(ContainerBuilderWrapper builder) } } + /// + /// The RegisterDefaultProtocol + /// + /// The builder private static void RegisterDefaultProtocol(ContainerBuilderWrapper builder) { builder.Register(provider => @@ -83,20 +96,21 @@ private static void RegisterDefaultProtocol(ContainerBuilderWrapper builder) }).SingleInstance(); builder.Register(provider => { - var messageListener = provider.Resolve(); return new DefaultServiceHost(async endPoint => { await messageListener.StartAsync(endPoint); return messageListener; }, null); - }).As(); } + /// + /// The RegisterMqttProtocol + /// + /// The builder private static void RegisterMqttProtocol(ContainerBuilderWrapper builder) { - builder.Register(provider => { return new DotNettyMqttServerMessageListener(provider.Resolve>(), @@ -105,15 +119,16 @@ private static void RegisterMqttProtocol(ContainerBuilderWrapper builder) ); }).SingleInstance(); builder.Register(provider => - { + { var messageListener = provider.Resolve(); return new MqttServiceHost(async endPoint => { await messageListener.StartAsync(endPoint); return messageListener; }); - }).As(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/MqttServiceHost.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/MqttServiceHost.cs index b8d2de071..2ac475ee2 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/MqttServiceHost.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/MqttServiceHost.cs @@ -9,23 +9,43 @@ namespace Surging.Core.Protocol.Mqtt { + /// + /// Defines the + /// public class MqttServiceHost : ServiceHostAbstract { - #region Field + #region 字段 + /// + /// Defines the _messageListenerFactory + /// private readonly Func> _messageListenerFactory; + + /// + /// Defines the _serverMessageListener + /// private IMessageListener _serverMessageListener; - #endregion Field + #endregion 字段 + + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The messageListenerFactory public MqttServiceHost(Func> messageListenerFactory) : base(null) { _messageListenerFactory = messageListenerFactory; } - #region Overrides of ServiceHostAbstract + #endregion 构造函数 + + #region 方法 - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + /// The Dispose + /// public override void Dispose() { (_serverMessageListener as IDisposable)?.Dispose(); @@ -41,9 +61,14 @@ public override async Task StartAsync(EndPoint endPoint) if (_serverMessageListener != null) return; _serverMessageListener = await _messageListenerFactory(endPoint); - } + /// + /// The StartAsync + /// + /// The ip + /// The port + /// The public override async Task StartAsync(string ip, int port) { if (_serverMessageListener != null) @@ -51,6 +76,6 @@ public override async Task StartAsync(string ip, int port) _serverMessageListener = await _messageListenerFactory(new IPEndPoint(IPAddress.Parse(ip), AppConfig.ServerOptions.Ports.MQTTPort)); } - #endregion Overrides of ServiceHostAbstract + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Util/MessageIdGenerater.cs b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Util/MessageIdGenerater.cs index 5e29f6de4..2e2bb28e4 100644 --- a/src/Surging.Core/Surging.Core.Protocol.Mqtt/Util/MessageIdGenerater.cs +++ b/src/Surging.Core/Surging.Core.Protocol.Mqtt/Util/MessageIdGenerater.cs @@ -5,11 +5,31 @@ namespace Surging.Core.Protocol.Mqtt.Util { - public class MessageIdGenerater + /// + /// Defines the + /// + public class MessageIdGenerater { + #region 字段 - private static int _index; + /// + /// Defines the _index + /// + private static int _index; + + /// + /// Defines the _lock + /// private static int _lock; + + #endregion 字段 + + #region 方法 + + /// + /// The GenerateId + /// + /// The public static int GenerateId() { for (; ; ) @@ -23,10 +43,12 @@ public static int GenerateId() _index++; else _index = 0; - + Interlocked.Exchange(ref _lock, 0); return _index; } } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.WS/Attributes/BehaviorContractAttribute.cs b/src/Surging.Core/Surging.Core.Protocol.WS/Attributes/BehaviorContractAttribute.cs index ab81e0756..1f85ce83c 100644 --- a/src/Surging.Core/Surging.Core.Protocol.WS/Attributes/BehaviorContractAttribute.cs +++ b/src/Surging.Core/Surging.Core.Protocol.WS/Attributes/BehaviorContractAttribute.cs @@ -4,13 +4,28 @@ namespace Surging.Core.Protocol.WS.Attributes { + /// + /// Defines the + /// public class BehaviorContractAttribute : Attribute { + #region 属性 - public bool IgnoreExtensions { get; set; } + /// + /// Gets or sets a value indicating whether EmitOnPing + /// + public bool EmitOnPing { get; set; } - public bool EmitOnPing { get; set; } + /// + /// Gets or sets a value indicating whether IgnoreExtensions + /// + public bool IgnoreExtensions { get; set; } - public string Protocol { get; set; } + /// + /// Gets or sets the Protocol + /// + public string Protocol { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.WS/Configurations/BehaviorOption.cs b/src/Surging.Core/Surging.Core.Protocol.WS/Configurations/BehaviorOption.cs index 7bb3a65a4..70e4aa9bf 100644 --- a/src/Surging.Core/Surging.Core.Protocol.WS/Configurations/BehaviorOption.cs +++ b/src/Surging.Core/Surging.Core.Protocol.WS/Configurations/BehaviorOption.cs @@ -4,12 +4,28 @@ namespace Surging.Core.Protocol.WS.Configurations { + /// + /// Defines the + /// public class BehaviorOption { - public bool IgnoreExtensions { get; set; } + #region 属性 + /// + /// Gets or sets a value indicating whether EmitOnPing + /// public bool EmitOnPing { get; set; } + /// + /// Gets or sets a value indicating whether IgnoreExtensions + /// + public bool IgnoreExtensions { get; set; } + + /// + /// Gets or sets the Protocol + /// public string Protocol { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.WS/Configurations/WebSocketOptions.cs b/src/Surging.Core/Surging.Core.Protocol.WS/Configurations/WebSocketOptions.cs index de8add798..0d7a65009 100644 --- a/src/Surging.Core/Surging.Core.Protocol.WS/Configurations/WebSocketOptions.cs +++ b/src/Surging.Core/Surging.Core.Protocol.WS/Configurations/WebSocketOptions.cs @@ -4,36 +4,29 @@ namespace Surging.Core.Protocol.WS.Configurations { + /// + /// Defines the + /// public class WebSocketOptions { + #region 属性 + + /// + /// Gets or sets the Behavior + /// + public BehaviorOption Behavior { get; set; } + + /// + /// Gets or sets a value indicating whether KeepClean + /// + public bool KeepClean { get; set; } = false; + /// /// Gets or sets the time to wait for the response to the WebSocket Ping or /// Close. /// - /// - /// The set operation does nothing if the server has already started or - /// it is shutting down. - /// - /// - /// - /// A to wait for the response. - /// - /// - /// The default value is the same as 1 second. - /// - /// - /// - /// The value specified for a set operation is zero or less. - /// public int WaitTime { get; set; } = 1; - - public bool KeepClean - { - get; - set; - } = false; - public BehaviorOption Behavior { get; set; } + #endregion 属性 } - } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.WS/DefaultWSServerMessageListener.cs b/src/Surging.Core/Surging.Core.Protocol.WS/DefaultWSServerMessageListener.cs index 5f72b8c3e..5758714a8 100644 --- a/src/Surging.Core/Surging.Core.Protocol.WS/DefaultWSServerMessageListener.cs +++ b/src/Surging.Core/Surging.Core.Protocol.WS/DefaultWSServerMessageListener.cs @@ -1,25 +1,55 @@ -using Surging.Core.CPlatform.Messages; +using Microsoft.Extensions.Logging; +using Surging.Core.CPlatform.Messages; using Surging.Core.CPlatform.Runtime.Server; using Surging.Core.CPlatform.Transport; +using Surging.Core.Protocol.WS.Configurations; +using Surging.Core.Protocol.WS.Runtime; using System; using System.Collections.Generic; -using System.Net; using System.Linq; +using System.Net; using System.Threading.Tasks; using WebSocketCore.Server; -using Surging.Core.Protocol.WS.Runtime; -using Microsoft.Extensions.Logging; -using Surging.Core.Protocol.WS.Configurations; namespace Surging.Core.Protocol.WS { + /// + /// Defines the + /// public class DefaultWSServerMessageListener : IMessageListener, IDisposable { + #region 字段 + + /// + /// Defines the _entries + /// private readonly List _entries; - private WebSocketServer _wssv; + + /// + /// Defines the _logger + /// private readonly ILogger _logger; + + /// + /// Defines the _options + /// private readonly WebSocketOptions _options; + /// + /// Defines the _wssv + /// + private WebSocketServer _wssv; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The logger + /// The wsServiceEntryProvider + /// The options public DefaultWSServerMessageListener(ILogger logger, IWSServiceEntryProvider wsServiceEntryProvider, WebSocketOptions options) { @@ -27,6 +57,59 @@ public DefaultWSServerMessageListener(ILogger lo _entries = wsServiceEntryProvider.GetEntries().ToList(); _options = options; } + + #endregion 构造函数 + + #region 事件 + + /// + /// Defines the Received + /// + public event ReceivedDelegate Received; + + #endregion 事件 + + #region 属性 + + /// + /// Gets the Server + /// + public WebSocketServer Server + { + get + { + return _wssv; + } + } + + #endregion 属性 + + #region 方法 + + /// + /// The Dispose + /// + public void Dispose() + { + _wssv.Stop(); + } + + /// + /// The OnReceived + /// + /// The sender + /// The message + /// The + public Task OnReceived(IMessageSender sender, TransportMessage message) + { + return Task.CompletedTask; + } + + /// + /// The StartAsync + /// + /// The endPoint + /// The public async Task StartAsync(EndPoint endPoint) { var ipEndPoint = endPoint as IPEndPoint; @@ -36,9 +119,9 @@ public async Task StartAsync(EndPoint endPoint) foreach (var entry in _entries) _wssv.AddWebSocketService(entry.Path, entry.FuncBehavior); _wssv.KeepClean = _options.KeepClean; - _wssv.WaitTime = TimeSpan.FromSeconds(_options.WaitTime); + _wssv.WaitTime = TimeSpan.FromSeconds(_options.WaitTime); //允许转发请求 - _wssv.AllowForwardedRequest = true; + _wssv.AllowForwardedRequest = true; _wssv.Start(); if (_logger.IsEnabled(LogLevel.Debug)) _logger.LogDebug($"WS服务主机启动成功,监听地址:{endPoint}。"); @@ -47,27 +130,8 @@ public async Task StartAsync(EndPoint endPoint) { _logger.LogError($"WS服务主机启动失败,监听地址:{endPoint}。 "); } - - } - - public WebSocketServer Server - { - get - { - return _wssv; - } - } - - public event ReceivedDelegate Received; - - public Task OnReceived(IMessageSender sender, TransportMessage message) - { - return Task.CompletedTask; } - public void Dispose() - { - _wssv.Stop(); - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.WS/Runtime/IWSServiceEntryProvider.cs b/src/Surging.Core/Surging.Core.Protocol.WS/Runtime/IWSServiceEntryProvider.cs index 62d11dec6..1c643b5af 100644 --- a/src/Surging.Core/Surging.Core.Protocol.WS/Runtime/IWSServiceEntryProvider.cs +++ b/src/Surging.Core/Surging.Core.Protocol.WS/Runtime/IWSServiceEntryProvider.cs @@ -4,8 +4,23 @@ namespace Surging.Core.Protocol.WS.Runtime { + #region 接口 + + /// + /// Defines the + /// public interface IWSServiceEntryProvider { + #region 方法 + + /// + /// The GetEntries + /// + /// The IEnumerable GetEntries(); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.WS/Runtime/Implementation/DefaultWSServiceEntryProvider.cs b/src/Surging.Core/Surging.Core.Protocol.WS/Runtime/Implementation/DefaultWSServiceEntryProvider.cs index 45f1498c7..f6d2af4f6 100644 --- a/src/Surging.Core/Surging.Core.Protocol.WS/Runtime/Implementation/DefaultWSServiceEntryProvider.cs +++ b/src/Surging.Core/Surging.Core.Protocol.WS/Runtime/Implementation/DefaultWSServiceEntryProvider.cs @@ -15,21 +15,50 @@ namespace Surging.Core.Protocol.WS.Runtime.Implementation { + /// + /// Defines the + /// public class DefaultWSServiceEntryProvider : IWSServiceEntryProvider { - #region Field + #region 字段 - private readonly IEnumerable _types; + /// + /// Defines the _logger + /// private readonly ILogger _logger; + + /// + /// Defines the _serviceProvider + /// private readonly CPlatformContainer _serviceProvider; - private List _wSServiceEntries; + + /// + /// Defines the _types + /// + private readonly IEnumerable _types; + + /// + /// Defines the _options + /// private WebSocketOptions _options; - #endregion Field + /// + /// Defines the _wSServiceEntries + /// + private List _wSServiceEntries; + + #endregion 字段 - #region Constructor + #region 构造函数 - public DefaultWSServiceEntryProvider(IServiceEntryProvider serviceEntryProvider, + /// + /// Initializes a new instance of the class. + /// + /// The serviceEntryProvider + /// The logger + /// The serviceProvider + /// The options + public DefaultWSServiceEntryProvider(IServiceEntryProvider serviceEntryProvider, ILogger logger, CPlatformContainer serviceProvider, WebSocketOptions options) @@ -40,9 +69,38 @@ public DefaultWSServiceEntryProvider(IServiceEntryProvider serviceEntryProvider _options = options; } - #endregion Constructor + #endregion 构造函数 + + #region 方法 - #region Implementation of IServiceEntryProvider + /// + /// The CreateServiceEntry + /// + /// The service + /// The + public WSServiceEntry CreateServiceEntry(Type service) + { + WSServiceEntry result = null; + var routeTemplate = service.GetCustomAttribute(); + var behaviorContract = service.GetCustomAttribute(); + var objInstance = _serviceProvider.GetInstances(service); + var behavior = objInstance as WebSocketBehavior; + var path = RoutePatternParser.Parse(routeTemplate.RouteTemplate, service.Name); + if (path.Length > 0 && path[0] != '/') + path = $"/{path}"; + if (behavior != null) + result = new WSServiceEntry + { + Behavior = behavior, + Type = behavior.GetType(), + Path = path, + FuncBehavior = () => + { + return GetWebSocketBehavior(service, _options?.Behavior, behaviorContract); + } + }; + return result; + } /// /// 获取服务条目集合。 @@ -69,33 +127,15 @@ public IEnumerable GetEntries() } return _wSServiceEntries; } - #endregion - - public WSServiceEntry CreateServiceEntry(Type service) - { - WSServiceEntry result = null; - var routeTemplate = service.GetCustomAttribute(); - var behaviorContract = service.GetCustomAttribute(); - var objInstance = _serviceProvider.GetInstances(service); - var behavior = objInstance as WebSocketBehavior; - var path = RoutePatternParser.Parse(routeTemplate.RouteTemplate, service.Name); - if (path.Length>0 && path[0] != '/') - path = $"/{path}"; - if (behavior != null) - result = new WSServiceEntry - { - Behavior = behavior, - Type = behavior.GetType(), - Path = path, - FuncBehavior = () => - { - return GetWebSocketBehavior(service, _options?.Behavior, behaviorContract); - } - }; - return result; - } - private WebSocketBehavior GetWebSocketBehavior(Type service,BehaviorOption option, BehaviorContractAttribute contractAttribute) + /// + /// The GetWebSocketBehavior + /// + /// The service + /// The option + /// The contractAttribute + /// The + private WebSocketBehavior GetWebSocketBehavior(Type service, BehaviorOption option, BehaviorContractAttribute contractAttribute) { var wsBehavior = _serviceProvider.GetInstances(service) as WebSocketBehavior; if (option != null) @@ -109,8 +149,10 @@ private WebSocketBehavior GetWebSocketBehavior(Type service,BehaviorOption optio wsBehavior.IgnoreExtensions = contractAttribute.IgnoreExtensions; wsBehavior.Protocol = contractAttribute.Protocol; wsBehavior.EmitOnPing = contractAttribute.EmitOnPing; - } + } return wsBehavior; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.WS/Runtime/WSServiceEntry.cs b/src/Surging.Core/Surging.Core.Protocol.WS/Runtime/WSServiceEntry.cs index 574ad5005..b25428dfd 100644 --- a/src/Surging.Core/Surging.Core.Protocol.WS/Runtime/WSServiceEntry.cs +++ b/src/Surging.Core/Surging.Core.Protocol.WS/Runtime/WSServiceEntry.cs @@ -6,14 +6,33 @@ namespace Surging.Core.Protocol.WS.Runtime { - public class WSServiceEntry + /// + /// Defines the + /// + public class WSServiceEntry { + #region 属性 + + /// + /// Gets or sets the Behavior + /// + public WebSocketBehavior Behavior { get; set; } + + /// + /// Gets or sets the FuncBehavior + /// + public Func FuncBehavior { get; set; } + + /// + /// Gets or sets the Path + /// public string Path { get; set; } + /// + /// Gets or sets the Type + /// public Type Type { get; set; } - public WebSocketBehavior Behavior { get; set; } - - public Func FuncBehavior { get; set; } + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.WS/WSProtocolModule.cs b/src/Surging.Core/Surging.Core.Protocol.WS/WSProtocolModule.cs index 9293d3833..20edfa026 100644 --- a/src/Surging.Core/Surging.Core.Protocol.WS/WSProtocolModule.cs +++ b/src/Surging.Core/Surging.Core.Protocol.WS/WSProtocolModule.cs @@ -16,8 +16,17 @@ namespace Surging.Core.Protocol.WS { + /// + /// Defines the + /// public class WSProtocolModule : EnginePartModule { + #region 方法 + + /// + /// The Initialize + /// + /// The context public override void Initialize(AppModuleContext context) { base.Initialize(context); @@ -53,9 +62,13 @@ protected override void RegisterBuilder(ContainerBuilderWrapper builder) } } + /// + /// The RegisterDefaultProtocol + /// + /// The builder + /// The options private static void RegisterDefaultProtocol(ContainerBuilderWrapper builder, WebSocketOptions options) { - builder.Register(provider => { return new DefaultWSServerMessageListener( @@ -72,10 +85,14 @@ private static void RegisterDefaultProtocol(ContainerBuilderWrapper builder, Web await messageListener.StartAsync(endPoint); return messageListener; }, null); - }).As(); } + /// + /// The RegisterWSProtocol + /// + /// The builder + /// The options private static void RegisterWSProtocol(ContainerBuilderWrapper builder, WebSocketOptions options) { builder.Register(provider => @@ -93,8 +110,9 @@ private static void RegisterWSProtocol(ContainerBuilderWrapper builder, WebSocke await messageListener.StartAsync(endPoint); return messageListener; }); - }).As(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.WS/WSServiceBase.cs b/src/Surging.Core/Surging.Core.Protocol.WS/WSServiceBase.cs index b386543b8..f642b15dc 100644 --- a/src/Surging.Core/Surging.Core.Protocol.WS/WSServiceBase.cs +++ b/src/Surging.Core/Surging.Core.Protocol.WS/WSServiceBase.cs @@ -6,81 +6,138 @@ using Surging.Core.Protocol.WS.Runtime; using Surging.Core.ProxyGenerator; using System; -using WebSocketCore.Server; using System.Linq; +using WebSocketCore.Server; namespace Surging.Core.Protocol.WS { - public abstract class WSServiceBase : WebSocketBehavior, IServiceBehavior - { - public T CreateProxy(string key) where T : class + /// + /// Defines the + /// + public abstract class WSServiceBase : WebSocketBehavior, IServiceBehavior + { + #region 方法 + + /// + /// The CreateProxy + /// + /// + /// The + public T CreateProxy() where T : class { - return ServiceLocator.GetService().CreateProxy(key); + return ServiceLocator.GetService().CreateProxy(); } - - public object CreateProxy(Type type) + + /// + /// The CreateProxy + /// + /// + /// The key + /// The + public T CreateProxy(string key) where T : class { - return ServiceLocator.GetService().CreateProxy(type); + return ServiceLocator.GetService().CreateProxy(key); } - + + /// + /// The CreateProxy + /// + /// The key + /// The type + /// The public object CreateProxy(string key, Type type) { return ServiceLocator.GetService().CreateProxy(key, type); } - - public T CreateProxy() where T : class + + /// + /// The CreateProxy + /// + /// The type + /// The + public object CreateProxy(Type type) { - return ServiceLocator.GetService().CreateProxy(); + return ServiceLocator.GetService().CreateProxy(type); } - public T GetService(string key) where T : class + /// + /// The GetClient + /// + /// The + public WebSocketSessionManager GetClient() { - if (ServiceLocator.Current.IsRegisteredWithKey(key)) - return ServiceLocator.GetService(key); - else - return ServiceLocator.GetService().CreateProxy(key); + WebSocketSessionManager result = null; + var server = ServiceLocator.GetService().Server; + var entries = ServiceLocator.GetService().GetEntries(); + var entry = entries.Where(p => p.Type == this.GetType()).FirstOrDefault(); + if (server.WebSocketServices.TryGetServiceHost(entry.Path, out WebSocketServiceHostBase webSocketServiceHost)) + result = webSocketServiceHost.Sessions; + return result; } - public T GetService() where T : class + /// + /// The GetService + /// + /// + /// The + public T GetService() where T : class { if (ServiceLocator.Current.IsRegistered()) return ServiceLocator.GetService(); else return ServiceLocator.GetService().CreateProxy(); - } - public object GetService(Type type) + /// + /// The GetService + /// + /// + /// The key + /// The + public T GetService(string key) where T : class { - if (ServiceLocator.Current.IsRegistered(type)) - return ServiceLocator.GetService(type); + if (ServiceLocator.Current.IsRegisteredWithKey(key)) + return ServiceLocator.GetService(key); else - return ServiceLocator.GetService().CreateProxy(type); + return ServiceLocator.GetService().CreateProxy(key); } - public object GetService(string key, Type type) + /// + /// The GetService + /// + /// The key + /// The type + /// The + public object GetService(string key, Type type) { if (ServiceLocator.Current.IsRegisteredWithKey(key, type)) return ServiceLocator.GetService(key, type); else return ServiceLocator.GetService().CreateProxy(key, type); - } - public WebSocketSessionManager GetClient() + /// + /// The GetService + /// + /// The type + /// The + public object GetService(Type type) { - WebSocketSessionManager result = null; - var server = ServiceLocator.GetService().Server; - var entries = ServiceLocator.GetService().GetEntries(); - var entry = entries.Where(p => p.Type == this.GetType()).FirstOrDefault(); - if (server.WebSocketServices.TryGetServiceHost(entry.Path, out WebSocketServiceHostBase webSocketServiceHost)) - result = webSocketServiceHost.Sessions; - return result; + if (ServiceLocator.Current.IsRegistered(type)) + return ServiceLocator.GetService(type); + else + return ServiceLocator.GetService().CreateProxy(type); } + /// + /// The Publish + /// + /// The event public void Publish(IntegrationEvent @event) { GetService().Publish(@event); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Protocol.WS/WSServiceHost.cs b/src/Surging.Core/Surging.Core.Protocol.WS/WSServiceHost.cs index 291144b7f..ea1725275 100644 --- a/src/Surging.Core/Surging.Core.Protocol.WS/WSServiceHost.cs +++ b/src/Surging.Core/Surging.Core.Protocol.WS/WSServiceHost.cs @@ -9,23 +9,43 @@ namespace Surging.Core.Protocol.WS { + /// + /// Defines the + /// public class WSServiceHost : ServiceHostAbstract { - #region Field + #region 字段 + /// + /// Defines the _messageListenerFactory + /// private readonly Func> _messageListenerFactory; + + /// + /// Defines the _serverMessageListener + /// private IMessageListener _serverMessageListener; - #endregion Field + #endregion 字段 + + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The messageListenerFactory public WSServiceHost(Func> messageListenerFactory) : base(null) { _messageListenerFactory = messageListenerFactory; } - #region Overrides of ServiceHostAbstract + #endregion 构造函数 + + #region 方法 - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + /// The Dispose + /// public override void Dispose() { (_serverMessageListener as IDisposable)?.Dispose(); @@ -41,9 +61,14 @@ public override async Task StartAsync(EndPoint endPoint) if (_serverMessageListener != null) return; _serverMessageListener = await _messageListenerFactory(endPoint); - } + /// + /// The StartAsync + /// + /// The ip + /// The port + /// The public override async Task StartAsync(string ip, int port) { if (_serverMessageListener != null) @@ -51,6 +76,6 @@ public override async Task StartAsync(string ip, int port) _serverMessageListener = await _messageListenerFactory(new IPEndPoint(IPAddress.Parse(ip), AppConfig.ServerOptions.Ports.WSPort)); } - #endregion Overrides of ServiceHostAbstract + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/ContainerBuilderExtensions.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/ContainerBuilderExtensions.cs index 35b67ed56..15ff1b0be 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/ContainerBuilderExtensions.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/ContainerBuilderExtensions.cs @@ -1,42 +1,49 @@ -using Surging.Core.CPlatform; +using Autofac; +using Surging.Core.CPlatform; +using Surging.Core.CPlatform.Convertibles; +using Surging.Core.CPlatform.Runtime.Client; using Surging.Core.ProxyGenerator.Implementation; -using Autofac; -using System; using Surging.Core.ProxyGenerator.Interceptors; -using Surging.Core.ProxyGenerator.Interceptors.Implementation; -using Surging.Core.CPlatform.Runtime.Client; -using Surging.Core.CPlatform.Convertibles; +using Surging.Core.ProxyGenerator.Interceptors.Implementation; +using System; namespace Surging.Core.ProxyGenerator { + /// + /// Defines the + /// public static class ContainerBuilderExtensions { + #region 方法 + /// - /// 添加客户端代理 + /// The AddClient /// - /// 服务构建者 - /// 服务构建者。 - public static IServiceBuilder AddClientProxy(this IServiceBuilder builder) + /// The services + /// The + public static IServiceBuilder AddClient(this ContainerBuilder services) { - var services = builder.Services; - services.RegisterType().As().SingleInstance(); - services.RegisterType().As().SingleInstance(); - builder.Services.Register(provider =>new ServiceProxyFactory( - provider.Resolve(), - provider.Resolve(), - provider.Resolve(), - builder.GetInterfaceService(), - builder.GetDataContractName() - )).As().SingleInstance(); - return builder; + return services + .AddCoreService() + .AddClientRuntime() + .AddClientProxy(); } - public static IServiceBuilder AddClientIntercepted(this IServiceBuilder builder,params Type[] interceptorServiceTypes ) + /// + /// 添加客户端属性注入 + /// + /// 服务构建者 + /// 服务构建者 + public static IServiceBuilder AddClient(this IServiceBuilder builder) { - var services = builder.Services; - services.RegisterTypes(interceptorServiceTypes).As().SingleInstance(); - services.RegisterType().As().SingleInstance(); - return builder; + return builder + .RegisterServices() + .RegisterRepositories() + .RegisterServiceBus() + .RegisterModules() + .RegisterInstanceByConstraint() + .AddClientRuntime() + .AddClientProxy(); } /// @@ -53,39 +60,50 @@ public static IServiceBuilder AddClientIntercepted(this IServiceBuilder builder, return builder; } - public static IServiceBuilder AddClient(this ContainerBuilder services) + /// + /// The AddClientIntercepted + /// + /// The builder + /// The interceptorServiceTypes + /// The + public static IServiceBuilder AddClientIntercepted(this IServiceBuilder builder, params Type[] interceptorServiceTypes) { - return services - .AddCoreService() - .AddClientRuntime() - .AddClientProxy(); + var services = builder.Services; + services.RegisterTypes(interceptorServiceTypes).As().SingleInstance(); + services.RegisterType().As().SingleInstance(); + return builder; } /// - /// 添加关联服务 + /// 添加客户端代理 /// - /// - /// 服务构建者 - public static IServiceBuilder AddRelateService(this IServiceBuilder builder) + /// 服务构建者 + /// 服务构建者。 + public static IServiceBuilder AddClientProxy(this IServiceBuilder builder) { - return builder.AddRelateServiceRuntime().AddClientProxy(); + var services = builder.Services; + services.RegisterType().As().SingleInstance(); + services.RegisterType().As().SingleInstance(); + builder.Services.Register(provider => new ServiceProxyFactory( + provider.Resolve(), + provider.Resolve(), + provider.Resolve(), + builder.GetInterfaceService(), + builder.GetDataContractName() + )).As().SingleInstance(); + return builder; } /// - /// 添加客户端属性注入 + /// 添加关联服务 /// - /// 服务构建者 + /// /// 服务构建者 - public static IServiceBuilder AddClient(this IServiceBuilder builder) + public static IServiceBuilder AddRelateService(this IServiceBuilder builder) { - return builder - .RegisterServices() - .RegisterRepositories() - .RegisterServiceBus() - .RegisterModules() - .RegisterInstanceByConstraint() - .AddClientRuntime() - .AddClientProxy(); + return builder.AddRelateServiceRuntime().AddClientProxy(); } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/FastReflection/ConstructorInvoker.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/FastReflection/ConstructorInvoker.cs index 43428a623..4ae494da8 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/FastReflection/ConstructorInvoker.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/FastReflection/ConstructorInvoker.cs @@ -6,23 +6,81 @@ namespace Surging.Core.ProxyGenerator.FastReflection { + #region 接口 + + /// + /// Defines the + /// public interface IConstructorInvoker { + #region 方法 + + /// + /// The Invoke + /// + /// The parameters + /// The object Invoke(params object[] parameters); + + #endregion 方法 } + #endregion 接口 + + /// + /// Defines the + /// public class ConstructorInvoker : IConstructorInvoker { + #region 字段 + + /// + /// Defines the m_invoker + /// private Func m_invoker; - public ConstructorInfo ConstructorInfo { get; private set; } + #endregion 字段 + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The constructorInfo public ConstructorInvoker(ConstructorInfo constructorInfo) { this.ConstructorInfo = constructorInfo; this.m_invoker = InitializeInvoker(constructorInfo); } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the ConstructorInfo + /// + public ConstructorInfo ConstructorInfo { get; private set; } + + #endregion 属性 + + #region 方法 + + /// + /// The Invoke + /// + /// The parameters + /// The + public object Invoke(params object[] parameters) + { + return this.m_invoker(parameters); + } + + /// + /// The InitializeInvoker + /// + /// The constructorInfo + /// The private Func InitializeInvoker(ConstructorInfo constructorInfo) { // Target: (object)new T((T0)parameters[0], (T1)parameters[1], ...) @@ -53,18 +111,16 @@ private Func InitializeInvoker(ConstructorInfo constructorInfo return lambda.Compile(); } - public object Invoke(params object[] parameters) - { - return this.m_invoker(parameters); - } - - #region IConstructorInvoker Members - + /// + /// The Invoke + /// + /// The parameters + /// The object IConstructorInvoker.Invoke(params object[] parameters) { return this.Invoke(parameters); } - #endregion + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/FastReflection/MethodInvoker.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/FastReflection/MethodInvoker.cs index b43250698..7f74c0ba0 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/FastReflection/MethodInvoker.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/FastReflection/MethodInvoker.cs @@ -6,28 +6,83 @@ namespace Surging.Core.ProxyGenerator.FastReflection { + #region 接口 + + /// + /// Defines the + /// public interface IMethodInvoker { + #region 方法 + + /// + /// The Invoke + /// + /// The instance + /// The parameters + /// The object Invoke(object instance, params object[] parameters); + + #endregion 方法 } + #endregion 接口 + + /// + /// Defines the + /// public class MethodInvoker : IMethodInvoker { + #region 字段 + + /// + /// Defines the m_invoker + /// private Func m_invoker; - public MethodInfo MethodInfo { get; private set; } + #endregion 字段 + + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The methodInfo public MethodInvoker(MethodInfo methodInfo) { this.MethodInfo = methodInfo; this.m_invoker = CreateInvokeDelegate(methodInfo); } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the MethodInfo + /// + public MethodInfo MethodInfo { get; private set; } + + #endregion 属性 + + #region 方法 + + /// + /// The Invoke + /// + /// The instance + /// The parameters + /// The public object Invoke(object instance, params object[] parameters) { return this.m_invoker(instance, parameters); } + /// + /// The CreateInvokeDelegate + /// + /// The methodInfo + /// The private static Func CreateInvokeDelegate(MethodInfo methodInfo) { // Target: ((TInstance)instance).Method((T0)parameters[0], (T1)parameters[1], ...) @@ -80,14 +135,17 @@ private static Func CreateInvokeDelegate(MethodInfo me } } - #region IMethodInvoker Members - + /// + /// The Invoke + /// + /// The instance + /// The parameters + /// The object IMethodInvoker.Invoke(object instance, params object[] parameters) { return this.Invoke(instance, parameters); } - #endregion + #endregion 方法 } -} - +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/FastReflection/PropertyAccessor.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/FastReflection/PropertyAccessor.cs index 6891e38f4..68790b158 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/FastReflection/PropertyAccessor.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/FastReflection/PropertyAccessor.cs @@ -5,20 +5,59 @@ namespace Surging.Core.ProxyGenerator.FastReflection { + #region 接口 + + /// + /// Defines the + /// public interface IPropertyAccessor { + #region 方法 + + /// + /// The GetValue + /// + /// The instance + /// The object GetValue(object instance); + /// + /// The SetValue + /// + /// The instance + /// The value void SetValue(object instance, object value); + + #endregion 方法 } + #endregion 接口 + + /// + /// Defines the + /// public class PropertyAccessor : IPropertyAccessor { + #region 字段 + + /// + /// Defines the m_getter + /// private Func m_getter; + + /// + /// Defines the m_setMethodInvoker + /// private MethodInvoker m_setMethodInvoker; - public PropertyInfo PropertyInfo { get; private set; } + #endregion 字段 + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The propertyInfo public PropertyAccessor(PropertyInfo propertyInfo) { this.PropertyInfo = propertyInfo; @@ -26,6 +65,53 @@ public PropertyAccessor(PropertyInfo propertyInfo) this.InitializeSet(propertyInfo); } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the PropertyInfo + /// + public PropertyInfo PropertyInfo { get; private set; } + + #endregion 属性 + + #region 方法 + + /// + /// The GetValue + /// + /// The o + /// The + public object GetValue(object o) + { + if (this.m_getter == null) + { + throw new NotSupportedException("Get method is not defined for this property."); + } + + return this.m_getter(o); + } + + /// + /// The SetValue + /// + /// The o + /// The value + public void SetValue(object o, object value) + { + if (this.m_setMethodInvoker == null) + { + throw new NotSupportedException("Set method is not defined for this property."); + } + + this.m_setMethodInvoker.Invoke(o, new object[] { value }); + } + + /// + /// The InitializeGet + /// + /// The propertyInfo private void InitializeGet(PropertyInfo propertyInfo) { if (!propertyInfo.CanRead) return; @@ -51,45 +137,36 @@ private void InitializeGet(PropertyInfo propertyInfo) this.m_getter = lambda.Compile(); } + /// + /// The InitializeSet + /// + /// The propertyInfo private void InitializeSet(PropertyInfo propertyInfo) { if (!propertyInfo.CanWrite) return; this.m_setMethodInvoker = new MethodInvoker(propertyInfo.GetSetMethod(true)); } - public object GetValue(object o) - { - if (this.m_getter == null) - { - throw new NotSupportedException("Get method is not defined for this property."); - } - - return this.m_getter(o); - } - - public void SetValue(object o, object value) - { - if (this.m_setMethodInvoker == null) - { - throw new NotSupportedException("Set method is not defined for this property."); - } - - this.m_setMethodInvoker.Invoke(o, new object[] { value }); - } - - #region IPropertyAccessor Members - + /// + /// The GetValue + /// + /// The instance + /// The object IPropertyAccessor.GetValue(object instance) { return this.GetValue(instance); } + /// + /// The SetValue + /// + /// The instance + /// The value void IPropertyAccessor.SetValue(object instance, object value) { this.SetValue(instance, value); } - #endregion + #endregion 方法 } -} - +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/IServiceProxyFactory.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/IServiceProxyFactory.cs index b329fe2e4..6f14ebfce 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/IServiceProxyFactory.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/IServiceProxyFactory.cs @@ -2,11 +2,38 @@ namespace Surging.Core.ProxyGenerator { + #region 接口 + /// /// 一个抽象的服务代理工厂。 /// public interface IServiceProxyFactory { + #region 方法 + + /// + /// The CreateProxy + /// + /// + /// The + T CreateProxy() where T : class; + + /// + /// The CreateProxy + /// + /// + /// The key + /// The + T CreateProxy(string key) where T : class; + + /// + /// The CreateProxy + /// + /// The key + /// The type + /// The + object CreateProxy(string key, Type type); + /// /// 创建服务代理。 /// @@ -14,21 +41,25 @@ public interface IServiceProxyFactory /// 服务代理实例。 object CreateProxy(Type proxyType); - object CreateProxy(string key, Type type); - + /// + /// The RegisterProxType + /// + /// The namespaces + /// The types void RegisterProxType(string[] namespaces, params Type[] types); - - T CreateProxy() where T : class; - - T CreateProxy(string key) where T : class; + #endregion 方法 } + #endregion 接口 + /// /// 服务代理工厂扩展。 /// public static class ServiceProxyFactoryExtensions { + #region 方法 + /// /// 创建服务代理。 /// @@ -41,6 +72,6 @@ public static T CreateProxy(this IServiceProxyFactory serviceProxyFactory, Ty return (T)serviceProxyFactory.CreateProxy(proxyType); } - + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/IServiceProxyGenerater.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/IServiceProxyGenerater.cs index 6b4f4cc4b..ddd82074e 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/IServiceProxyGenerater.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/IServiceProxyGenerater.cs @@ -4,24 +4,33 @@ namespace Surging.Core.ProxyGenerator { + #region 接口 + /// /// 一个抽象的服务代理生成器。 /// - public interface IServiceProxyGenerater:IDisposable + public interface IServiceProxyGenerater : IDisposable { + #region 方法 + /// /// 生成服务代理。 /// /// 需要被代理的接口类型。 - /// 引用的命名空间。 + /// The namespaces /// 服务代理实现。 - IEnumerable GenerateProxys(IEnumerable interfacTypes,IEnumerable namespaces); + IEnumerable GenerateProxys(IEnumerable interfacTypes, IEnumerable namespaces); /// /// 生成服务代理代码树。 /// /// 需要被代理的接口类型。 + /// The namespaces /// 代码树。 SyntaxTree GenerateProxyTree(Type interfaceType, IEnumerable namespaces); + + #endregion 方法 } + + #endregion 接口 } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/IServiceProxyProvider.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/IServiceProxyProvider.cs index 3d2a66fdf..322371564 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/IServiceProxyProvider.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/IServiceProxyProvider.cs @@ -5,13 +5,36 @@ namespace Surging.Core.ProxyGenerator { + #region 接口 + /// /// 代理服务接口 /// - public interface IServiceProxyProvider + public interface IServiceProxyProvider { + #region 方法 + + /// + /// The Invoke + /// + /// + /// The parameters + /// The routePath + /// The Task Invoke(IDictionary parameters, string routePath); - Task Invoke(IDictionary parameters, string routePath,string serviceKey); + /// + /// The Invoke + /// + /// + /// The parameters + /// The routePath + /// The serviceKey + /// The + Task Invoke(IDictionary parameters, string routePath, string serviceKey); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/Implementation/RemoteServiceProxy.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/Implementation/RemoteServiceProxy.cs index 76d52f017..26e5c5190 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/Implementation/RemoteServiceProxy.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/Implementation/RemoteServiceProxy.cs @@ -9,28 +9,56 @@ namespace Surging.Core.ProxyGenerator.Implementation { - public class RemoteServiceProxy: ServiceProxyBase + /// + /// Defines the + /// + public class RemoteServiceProxy : ServiceProxyBase { - public RemoteServiceProxy(string serviceKey, CPlatformContainer serviceProvider) - :this(serviceProvider.GetInstances(), - serviceProvider.GetInstances(),serviceKey,serviceProvider, - serviceProvider.GetInstances()) - { - - } + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The remoteInvokeService + /// The typeConvertibleService + /// The serviceKey + /// The serviceProvider + /// The serviceRouteProvider public RemoteServiceProxy(IRemoteInvokeService remoteInvokeService, ITypeConvertibleService typeConvertibleService, String serviceKey, CPlatformContainer serviceProvider, IServiceRouteProvider serviceRouteProvider - ):base(remoteInvokeService, typeConvertibleService, serviceKey, serviceProvider) + ) : base(remoteInvokeService, typeConvertibleService, serviceKey, serviceProvider) { + } + /// + /// Initializes a new instance of the class. + /// + /// The serviceKey + /// The serviceProvider + public RemoteServiceProxy(string serviceKey, CPlatformContainer serviceProvider) + : this(serviceProvider.GetInstances(), + serviceProvider.GetInstances(), serviceKey, serviceProvider, + serviceProvider.GetInstances()) + { } - public new async Task Invoke(IDictionary parameters, string serviceId) + #endregion 构造函数 + + #region 方法 + + /// + /// The Invoke + /// + /// + /// The parameters + /// The serviceId + /// The + public new async Task Invoke(IDictionary parameters, string serviceId) { - return await base.Invoke(parameters, serviceId); + return await base.Invoke(parameters, serviceId); } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/Implementation/ServiceProxyBase.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/Implementation/ServiceProxyBase.cs index e2f0d9adc..8d3ffc759 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/Implementation/ServiceProxyBase.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/Implementation/ServiceProxyBase.cs @@ -1,16 +1,16 @@ -using Newtonsoft.Json.Linq; +using Autofac; +using Newtonsoft.Json.Linq; using Surging.Core.CPlatform; using Surging.Core.CPlatform.Convertibles; using Surging.Core.CPlatform.Messages; using Surging.Core.CPlatform.Runtime.Client; using Surging.Core.CPlatform.Support; +using Surging.Core.CPlatform.Utilities; using Surging.Core.ProxyGenerator.Interceptors; using System; using System.Collections.Generic; -using System.Threading.Tasks; -using Autofac; -using Surging.Core.CPlatform.Utilities; using System.Linq; +using System.Threading.Tasks; namespace Surging.Core.ProxyGenerator.Implementation { @@ -19,19 +19,59 @@ namespace Surging.Core.ProxyGenerator.Implementation /// public abstract class ServiceProxyBase { - #region Field + #region 字段 + + /// + /// Defines the _breakeRemoteInvokeService + /// + private readonly IBreakeRemoteInvokeService _breakeRemoteInvokeService; + + /// + /// Defines the _cacheInterceptor + /// + private readonly IInterceptor _cacheInterceptor; + + /// + /// Defines the _commandProvider + /// + private readonly IServiceCommandProvider _commandProvider; + + /// + /// Defines the _interceptors + /// + private readonly IEnumerable _interceptors; + + /// + /// Defines the _remoteInvokeService + /// private readonly IRemoteInvokeService _remoteInvokeService; - private readonly ITypeConvertibleService _typeConvertibleService; + + /// + /// Defines the _serviceKey + /// private readonly string _serviceKey; + + /// + /// Defines the _serviceProvider + /// private readonly CPlatformContainer _serviceProvider; - private readonly IServiceCommandProvider _commandProvider; - private readonly IBreakeRemoteInvokeService _breakeRemoteInvokeService; - private readonly IEnumerable _interceptors; - private readonly IInterceptor _cacheInterceptor; - #endregion Field - #region Constructor + /// + /// Defines the _typeConvertibleService + /// + private readonly ITypeConvertibleService _typeConvertibleService; + + #endregion 字段 + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The remoteInvokeService + /// The typeConvertibleService + /// The serviceKey + /// The serviceProvider protected ServiceProxyBase(IRemoteInvokeService remoteInvokeService, ITypeConvertibleService typeConvertibleService, String serviceKey, CPlatformContainer serviceProvider) { @@ -47,13 +87,45 @@ protected ServiceProxyBase(IRemoteInvokeService remoteInvokeService, var interceptors = serviceProvider.GetInstances>(); _interceptors = interceptors.Where(p => !typeof(CacheInterceptor).IsAssignableFrom(p.GetType())); _cacheInterceptor = interceptors.Where(p => typeof(CacheInterceptor).IsAssignableFrom(p.GetType())).FirstOrDefault(); - } + } + } + + #endregion 构造函数 + #region 方法 + /// + /// The CallInvoke + /// + /// The invocation + /// The + public async Task CallInvoke(IInvocation invocation) + { + var cacheInvocation = invocation as ICacheInvocation; + var parameters = invocation.Arguments; + var serviceId = invocation.ServiceId; + var type = invocation.ReturnType; + var message = await _breakeRemoteInvokeService.InvokeAsync(parameters, serviceId, _serviceKey, + type == typeof(Task) ? false : true); + if (message == null) + { + var vt = _commandProvider.GetCommand(serviceId); + var command = vt.IsCompletedSuccessfully ? vt.Result : await vt; + if (command.FallBackName != null && _serviceProvider.IsRegistered(command.FallBackName) && command.Strategy == StrategyType.FallBack) + { + var invoker = _serviceProvider.GetInstances(command.FallBackName); + return await invoker.Invoke(parameters, serviceId, _serviceKey); + } + else + { + var invoker = _serviceProvider.GetInstances(command.Strategy.ToString()); + return await invoker.Invoke(parameters, serviceId, _serviceKey, true); + } + } + if (type == typeof(Task)) return message; + return _typeConvertibleService.Convert(message.Result, type); } - #endregion Constructor - #region Protected Method /// /// 远程调用。 /// @@ -64,13 +136,13 @@ protected ServiceProxyBase(IRemoteInvokeService remoteInvokeService, protected async Task Invoke(IDictionary parameters, string serviceId) { object result = default(T); - var vt = _commandProvider.GetCommand(serviceId); + var vt = _commandProvider.GetCommand(serviceId); var command = vt.IsCompletedSuccessfully ? vt.Result : await vt; RemoteInvokeResultMessage message = null; var decodeJOject = typeof(T) == UtilityType.ObjectType; IInvocation invocation = null; var existsInterceptor = _interceptors.Any(); - if ((_cacheInterceptor==null || !command.RequestCacheEnabled) && !existsInterceptor) + if ((_cacheInterceptor == null || !command.RequestCacheEnabled) && !existsInterceptor) { message = await _breakeRemoteInvokeService.InvokeAsync(parameters, serviceId, _serviceKey, decodeJOject); if (message == null) @@ -87,7 +159,7 @@ protected async Task Invoke(IDictionary parameters, string } } } - if (_cacheInterceptor!=null && command.RequestCacheEnabled) + if (_cacheInterceptor != null && command.RequestCacheEnabled) { invocation = GetCacheInvocation(parameters, serviceId, typeof(T)); var interceptReuslt = await Intercept(_cacheInterceptor, invocation); @@ -107,38 +179,11 @@ protected async Task Invoke(IDictionary parameters, string if (message != null) { if (message.Result == null) result = message.Result; - else result = _typeConvertibleService.Convert(message.Result, typeof(T)); + else result = _typeConvertibleService.Convert(message.Result, typeof(T)); } return (T)result; } - public async Task CallInvoke(IInvocation invocation) - { - var cacheInvocation = invocation as ICacheInvocation; - var parameters = invocation.Arguments; - var serviceId = invocation.ServiceId; - var type = invocation.ReturnType; - var message = await _breakeRemoteInvokeService.InvokeAsync(parameters, serviceId, _serviceKey, - type == typeof(Task) ? false : true); - if (message == null) - { - var vt = _commandProvider.GetCommand(serviceId); - var command = vt.IsCompletedSuccessfully ? vt.Result : await vt; - if (command.FallBackName != null && _serviceProvider.IsRegistered(command.FallBackName) && command.Strategy == StrategyType.FallBack) - { - var invoker = _serviceProvider.GetInstances(command.FallBackName); - return await invoker.Invoke(parameters, serviceId, _serviceKey); - } - else - { - var invoker = _serviceProvider.GetInstances(command.Strategy.ToString()); - return await invoker.Invoke(parameters, serviceId, _serviceKey, true); - } - } - if (type == typeof(Task)) return message; - return _typeConvertibleService.Convert(message.Result, type); - } - /// /// 远程调用。 /// @@ -162,7 +207,7 @@ protected async Task Invoke(IDictionary parameters, string servi } if (message == null) { - var vt = _commandProvider.GetCommand(serviceId); + var vt = _commandProvider.GetCommand(serviceId); var command = vt.IsCompletedSuccessfully ? vt.Result : await vt; if (command.FallBackName != null && _serviceProvider.IsRegistered(command.FallBackName) && command.Strategy == StrategyType.FallBack) { @@ -177,26 +222,46 @@ protected async Task Invoke(IDictionary parameters, string servi } } - private async Task> Intercept(IInterceptor interceptor, IInvocation invocation) + /// + /// The GetCacheInvocation + /// + /// The parameters + /// The serviceId + /// The returnType + /// The + private IInvocation GetCacheInvocation(IDictionary parameters, string serviceId, Type returnType) { - await interceptor.Intercept(invocation); - var message = invocation.ReturnValue is RemoteInvokeResultMessage - ? invocation.ReturnValue as RemoteInvokeResultMessage : null; - return new Tuple(message, invocation.ReturnValue); + var invocation = _serviceProvider.GetInstances(); + return invocation.GetCacheInvocation(this, parameters, serviceId, returnType); } - + + /// + /// The GetInvocation + /// + /// The parameters + /// The serviceId + /// The returnType + /// The private IInvocation GetInvocation(IDictionary parameters, string serviceId, Type returnType) { var invocation = _serviceProvider.GetInstances(); return invocation.GetInvocation(this, parameters, serviceId, returnType); } - private IInvocation GetCacheInvocation(IDictionary parameters, string serviceId, Type returnType) + /// + /// The Intercept + /// + /// The interceptor + /// The invocation + /// The + private async Task> Intercept(IInterceptor interceptor, IInvocation invocation) { - var invocation = _serviceProvider.GetInstances(); - return invocation.GetCacheInvocation(this, parameters, serviceId, returnType); + await interceptor.Intercept(invocation); + var message = invocation.ReturnValue is RemoteInvokeResultMessage + ? invocation.ReturnValue as RemoteInvokeResultMessage : null; + return new Tuple(message, invocation.ReturnValue); } - #endregion Protected Method + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/Implementation/ServiceProxyFactory.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/Implementation/ServiceProxyFactory.cs index f93923335..500d3bb3e 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/Implementation/ServiceProxyFactory.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/Implementation/ServiceProxyFactory.cs @@ -1,13 +1,13 @@ -using Surging.Core.CPlatform.Convertibles; +using Microsoft.Extensions.DependencyInjection; +using Surging.Core.CPlatform; +using Surging.Core.CPlatform.Convertibles; +using Surging.Core.CPlatform.DependencyResolution; using Surging.Core.CPlatform.Runtime.Client; +using Surging.Core.CPlatform.Support; using System; +using System.Collections.Generic; using System.Linq; using System.Reflection; -using Microsoft.Extensions.DependencyInjection; -using Surging.Core.CPlatform; -using Surging.Core.CPlatform.Support; -using System.Collections.Generic; -using Surging.Core.CPlatform.DependencyResolution; using System.Runtime.CompilerServices; namespace Surging.Core.ProxyGenerator.Implementation @@ -17,22 +17,40 @@ namespace Surging.Core.ProxyGenerator.Implementation /// public class ServiceProxyFactory : IServiceProxyFactory { - #region Field + #region 字段 + + /// + /// Defines the _remoteInvokeService + /// private readonly IRemoteInvokeService _remoteInvokeService; - private readonly ITypeConvertibleService _typeConvertibleService; + + /// + /// Defines the _serviceProvider + /// private readonly IServiceProvider _serviceProvider; - private Type[] _serviceTypes=new Type[0]; - #endregion Field + /// + /// Defines the _typeConvertibleService + /// + private readonly ITypeConvertibleService _typeConvertibleService; - #region Constructor + /// + /// Defines the _serviceTypes + /// + private Type[] _serviceTypes = new Type[0]; - public ServiceProxyFactory(IRemoteInvokeService remoteInvokeService, ITypeConvertibleService typeConvertibleService, - IServiceProvider serviceProvider):this(remoteInvokeService, typeConvertibleService, serviceProvider,null,null) - { + #endregion 字段 - } + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The remoteInvokeService + /// The typeConvertibleService + /// The serviceProvider + /// The types + /// The namespaces public ServiceProxyFactory(IRemoteInvokeService remoteInvokeService, ITypeConvertibleService typeConvertibleService, IServiceProvider serviceProvider, IEnumerable types, IEnumerable namespaces) { @@ -41,71 +59,109 @@ public ServiceProxyFactory(IRemoteInvokeService remoteInvokeService, ITypeConver _serviceProvider = serviceProvider; if (types != null) { - RegisterProxType(namespaces.ToArray(),types.ToArray()); + RegisterProxType(namespaces.ToArray(), types.ToArray()); } } - #endregion Constructor + /// + /// Initializes a new instance of the class. + /// + /// The remoteInvokeService + /// The typeConvertibleService + /// The serviceProvider + public ServiceProxyFactory(IRemoteInvokeService remoteInvokeService, ITypeConvertibleService typeConvertibleService, + IServiceProvider serviceProvider) : this(remoteInvokeService, typeConvertibleService, serviceProvider, null, null) + { + } - #region Implementation of IServiceProxyFactory + #endregion 构造函数 + + #region 方法 + + /// + /// The CreateProxy + /// + /// + /// The + public T CreateProxy() where T : class + { + return CreateProxy(null); + } + /// + /// The CreateProxy + /// + /// + /// The key + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public object CreateProxy(Type type) + public T CreateProxy(string key) where T : class { - var instance = ServiceResolver.Current.GetService(type); + var instanceType = typeof(T); + var instance = ServiceResolver.Current.GetService(instanceType, key); if (instance == null) { - var proxyType = _serviceTypes.Single(type.GetTypeInfo().IsAssignableFrom); - instance = proxyType.GetTypeInfo().GetConstructors().First().Invoke(new object[] { _remoteInvokeService, _typeConvertibleService, null, - _serviceProvider.GetService()}); - ServiceResolver.Current.Register(null, instance, type); + var proxyType = _serviceTypes.Single(typeof(T).GetTypeInfo().IsAssignableFrom); + instance = proxyType.GetTypeInfo().GetConstructors().First().Invoke(new object[] { _remoteInvokeService, _typeConvertibleService,key, + _serviceProvider.GetService() }); + ServiceResolver.Current.Register(key, instance, instanceType); } - return instance; + return instance as T; } + /// + /// The CreateProxy + /// + /// The key + /// The type + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public object CreateProxy(string key,Type type) + public object CreateProxy(string key, Type type) { - var instance = ServiceResolver.Current.GetService(type,key); + var instance = ServiceResolver.Current.GetService(type, key); if (instance == null) { var proxyType = _serviceTypes.Single(type.GetTypeInfo().IsAssignableFrom); - instance = proxyType.GetTypeInfo().GetConstructors().First().Invoke(new object[] { _remoteInvokeService, _typeConvertibleService, key, + instance = proxyType.GetTypeInfo().GetConstructors().First().Invoke(new object[] { _remoteInvokeService, _typeConvertibleService, key, _serviceProvider.GetService()}); ServiceResolver.Current.Register(key, instance, type); } return instance; } + /// + /// The CreateProxy + /// + /// The type + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T CreateProxy(string key) where T:class + public object CreateProxy(Type type) { - var instanceType = typeof(T); - var instance = ServiceResolver.Current.GetService(instanceType, key); + var instance = ServiceResolver.Current.GetService(type); if (instance == null) { - var proxyType = _serviceTypes.Single(typeof(T).GetTypeInfo().IsAssignableFrom); - instance = proxyType.GetTypeInfo().GetConstructors().First().Invoke(new object[] { _remoteInvokeService, _typeConvertibleService,key, - _serviceProvider.GetService() }); - ServiceResolver.Current.Register(key, instance, instanceType); + var proxyType = _serviceTypes.Single(type.GetTypeInfo().IsAssignableFrom); + instance = proxyType.GetTypeInfo().GetConstructors().First().Invoke(new object[] { _remoteInvokeService, _typeConvertibleService, null, + _serviceProvider.GetService()}); + ServiceResolver.Current.Register(null, instance, type); } - return instance as T; - } - - public T CreateProxy() where T : class - { - return CreateProxy(null); + return instance; } + /// + /// The RegisterProxType + /// + /// The namespaces + /// The types [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RegisterProxType(string[] namespaces,params Type[] types) + public void RegisterProxType(string[] namespaces, params Type[] types) { var proxyGenerater = _serviceProvider.GetService(); var serviceTypes = proxyGenerater.GenerateProxys(types, namespaces).ToArray(); - _serviceTypes= _serviceTypes.Except(serviceTypes).Concat(serviceTypes).ToArray(); + _serviceTypes = _serviceTypes.Except(serviceTypes).Concat(serviceTypes).ToArray(); proxyGenerater.Dispose(); } - #endregion Implementation of IServiceProxyFactory + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/Implementation/ServiceProxyGenerater.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/Implementation/ServiceProxyGenerater.cs index 4380a8f94..db5cf4a29 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/Implementation/ServiceProxyGenerater.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/Implementation/ServiceProxyGenerater.cs @@ -1,54 +1,73 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.Extensions.DependencyModel; +using Microsoft.Extensions.Logging; +using Surging.Core.CPlatform; using Surging.Core.CPlatform.Convertibles; using Surging.Core.CPlatform.Ids; -using Surging.Core.ProxyGenerator.Utilitys; using Surging.Core.CPlatform.Runtime.Client; using Surging.Core.CPlatform.Serialization; +using Surging.Core.ProxyGenerator.Utilitys; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; - -#if !NET - using System.Runtime.Loader; -using Microsoft.Extensions.DependencyModel; - -#endif - using System.Threading.Tasks; -using Microsoft.Extensions.Logging; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; -using Surging.Core.CPlatform; namespace Surging.Core.ProxyGenerator.Implementation { - public class ServiceProxyGenerater : IServiceProxyGenerater,IDisposable + /// + /// Defines the + /// + public class ServiceProxyGenerater : IServiceProxyGenerater, IDisposable { - #region Field + #region 字段 - private readonly IServiceIdGenerator _serviceIdGenerator; + /// + /// Defines the _logger + /// private readonly ILogger _logger; - #endregion Field - #region Constructor + /// + /// Defines the _serviceIdGenerator + /// + private readonly IServiceIdGenerator _serviceIdGenerator; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serviceIdGenerator + /// The logger public ServiceProxyGenerater(IServiceIdGenerator serviceIdGenerator, ILogger logger) { _serviceIdGenerator = serviceIdGenerator; _logger = logger; } - #endregion Constructor + #endregion 构造函数 + + #region 方法 - #region Implementation of IServiceProxyGenerater + /// + /// The Dispose + /// + public void Dispose() + { + GC.SuppressFinalize(this); + } /// /// 生成服务代理。 /// /// 需要被代理的接口类型。 + /// The namespaces /// 服务代理实现。 public IEnumerable GenerateProxys(IEnumerable interfacTypes, IEnumerable namespaces) { @@ -64,7 +83,7 @@ public IEnumerable GenerateProxys(IEnumerable interfacTypes, IEnumer { assemblys = assemblys.Append(t.Assembly); } - var trees = interfacTypes.Select(p=>GenerateProxyTree(p,namespaces)).ToList(); + var trees = interfacTypes.Select(p => GenerateProxyTree(p, namespaces)).ToList(); var stream = CompilationUtilitys.CompileClientProxy(trees, assemblys .Select(a => MetadataReference.CreateFromFile(a.Location)) @@ -81,7 +100,7 @@ public IEnumerable GenerateProxys(IEnumerable interfacTypes, IEnumer #else var assembly = AssemblyLoadContext.Default.LoadFromStream(stream); #endif - return assembly.GetExportedTypes(); + return assembly.GetExportedTypes(); } } @@ -89,6 +108,7 @@ public IEnumerable GenerateProxys(IEnumerable interfacTypes, IEnumer /// 生成服务代理代码树。 /// /// 需要被代理的接口类型。 + /// The namespaces /// 代码树。 public SyntaxTree GenerateProxyTree(Type interfaceType, IEnumerable namespaces) { @@ -128,56 +148,11 @@ public SyntaxTree GenerateProxyTree(Type interfaceType, IEnumerable name .NormalizeWhitespace().SyntaxTree; } - #endregion Implementation of IServiceProxyGenerater - - #region Private Method - - private static QualifiedNameSyntax GetQualifiedNameSyntax(Type type) - { - var fullName = type.Namespace + "." + type.Name; - return GetQualifiedNameSyntax(fullName); - } - - private static QualifiedNameSyntax GetQualifiedNameSyntax(string fullName) - { - return GetQualifiedNameSyntax(fullName.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries)); - } - - private static QualifiedNameSyntax GetQualifiedNameSyntax(IReadOnlyCollection names) - { - var ids = names.Select(IdentifierName).ToArray(); - - var index = 0; - QualifiedNameSyntax left = null; - while (index + 1 < names.Count) - { - left = left == null ? QualifiedName(ids[index], ids[index + 1]) : QualifiedName(left, ids[index + 1]); - index++; - } - return left; - } - - private static SyntaxList GetUsings(IEnumerable namespaces) - { - var directives = new List(); - foreach(var name in namespaces) - { - directives.Add(UsingDirective(GetQualifiedNameSyntax(name))); - } - return List( - new[] - { - UsingDirective(IdentifierName("System")), - UsingDirective(GetQualifiedNameSyntax("System.Threading.Tasks")), - UsingDirective(GetQualifiedNameSyntax("System.Collections.Generic")), - UsingDirective(GetQualifiedNameSyntax(typeof(ITypeConvertibleService).Namespace)), - UsingDirective(GetQualifiedNameSyntax(typeof(IRemoteInvokeService).Namespace)), - UsingDirective(GetQualifiedNameSyntax(typeof(CPlatformContainer).Namespace)), - UsingDirective(GetQualifiedNameSyntax(typeof(ISerializer<>).Namespace)), - UsingDirective(GetQualifiedNameSyntax(typeof(ServiceProxyBase).Namespace)) - }.Concat(directives)); - } - + /// + /// The GetConstructorDeclaration + /// + /// The className + /// The private static ConstructorDeclarationSyntax GetConstructorDeclaration(string className) { return ConstructorDeclaration(Identifier(className)) @@ -230,12 +205,51 @@ private static ConstructorDeclarationSyntax GetConstructorDeclaration(string cla .WithBody(Block()); } - private IEnumerable GenerateMethodDeclarations(IEnumerable methods) + /// + /// The GetQualifiedNameSyntax + /// + /// The names + /// The + private static QualifiedNameSyntax GetQualifiedNameSyntax(IReadOnlyCollection names) { - var array = methods.ToArray(); - return array.Select(p=>GenerateMethodDeclaration(p)).ToArray(); + var ids = names.Select(IdentifierName).ToArray(); + + var index = 0; + QualifiedNameSyntax left = null; + while (index + 1 < names.Count) + { + left = left == null ? QualifiedName(ids[index], ids[index + 1]) : QualifiedName(left, ids[index + 1]); + index++; + } + return left; + } + + /// + /// The GetQualifiedNameSyntax + /// + /// The fullName + /// The + private static QualifiedNameSyntax GetQualifiedNameSyntax(string fullName) + { + return GetQualifiedNameSyntax(fullName.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries)); + } + + /// + /// The GetQualifiedNameSyntax + /// + /// The type + /// The + private static QualifiedNameSyntax GetQualifiedNameSyntax(Type type) + { + var fullName = type.Namespace + "." + type.Name; + return GetQualifiedNameSyntax(fullName); } + /// + /// The GetTypeSyntax + /// + /// The type + /// The private static TypeSyntax GetTypeSyntax(Type type) { //没有返回值。 @@ -262,6 +276,37 @@ private static TypeSyntax GetTypeSyntax(Type type) .WithTypeArgumentList(typeArgumentListSyntax); } + /// + /// The GetUsings + /// + /// The namespaces + /// The + private static SyntaxList GetUsings(IEnumerable namespaces) + { + var directives = new List(); + foreach (var name in namespaces) + { + directives.Add(UsingDirective(GetQualifiedNameSyntax(name))); + } + return List( + new[] + { + UsingDirective(IdentifierName("System")), + UsingDirective(GetQualifiedNameSyntax("System.Threading.Tasks")), + UsingDirective(GetQualifiedNameSyntax("System.Collections.Generic")), + UsingDirective(GetQualifiedNameSyntax(typeof(ITypeConvertibleService).Namespace)), + UsingDirective(GetQualifiedNameSyntax(typeof(IRemoteInvokeService).Namespace)), + UsingDirective(GetQualifiedNameSyntax(typeof(CPlatformContainer).Namespace)), + UsingDirective(GetQualifiedNameSyntax(typeof(ISerializer<>).Namespace)), + UsingDirective(GetQualifiedNameSyntax(typeof(ServiceProxyBase).Namespace)) + }.Concat(directives)); + } + + /// + /// The GenerateMethodDeclaration + /// + /// The method + /// The private MemberDeclarationSyntax GenerateMethodDeclaration(MethodInfo method) { var serviceId = _serviceIdGenerator.GenerateServiceId(method); @@ -283,10 +328,9 @@ private MemberDeclarationSyntax GenerateMethodDeclaration(MethodInfo method) parameterDeclarationList.Add(Parameter( Identifier(parameter.Name)) .WithType(GetQualifiedNameSyntax(parameter.ParameterType))); - } parameterDeclarationList.Add(Token(SyntaxKind.CommaToken)); - + parameterList.Add(InitializerExpression( SyntaxKind.ComplexElementInitializerExpression, SeparatedList( @@ -317,7 +361,6 @@ private MemberDeclarationSyntax GenerateMethodDeclaration(MethodInfo method) { expressionSyntax = GenericName( Identifier("Invoke")).WithTypeArgumentList(((GenericNameSyntax)returnDeclaration).TypeArgumentList); - } else { @@ -373,11 +416,17 @@ private MemberDeclarationSyntax GenerateMethodDeclaration(MethodInfo method) return declaration; } - public void Dispose() - { - GC.SuppressFinalize(this); + /// + /// The GenerateMethodDeclarations + /// + /// The methods + /// The + private IEnumerable GenerateMethodDeclarations(IEnumerable methods) + { + var array = methods.ToArray(); + return array.Select(p => GenerateMethodDeclaration(p)).ToArray(); } - #endregion Private Method + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/Implementation/ServiceProxyProvider.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/Implementation/ServiceProxyProvider.cs index daa7d6c95..9181009f4 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/Implementation/ServiceProxyProvider.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/Implementation/ServiceProxyProvider.cs @@ -6,20 +6,53 @@ namespace Surging.Core.ProxyGenerator.Implementation { + /// + /// Defines the + /// public class ServiceProxyProvider : IServiceProxyProvider { - private readonly IServiceRouteProvider _serviceRouteProvider; + #region 字段 + + /// + /// Defines the _serviceProvider + /// private readonly CPlatformContainer _serviceProvider; - public ServiceProxyProvider( IServiceRouteProvider serviceRouteProvider + + /// + /// Defines the _serviceRouteProvider + /// + private readonly IServiceRouteProvider _serviceRouteProvider; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serviceRouteProvider + /// The serviceProvider + public ServiceProxyProvider(IServiceRouteProvider serviceRouteProvider , CPlatformContainer serviceProvider) { _serviceRouteProvider = serviceRouteProvider; _serviceProvider = serviceProvider; } - public async Task Invoke(IDictionary parameters, string routePath) + #endregion 构造函数 + + #region 方法 + + /// + /// The Invoke + /// + /// + /// The parameters + /// The routePath + /// The + public async Task Invoke(IDictionary parameters, string routePath) { - var serviceRoute= await _serviceRouteProvider.GetRouteByPath(routePath.ToLower()); + var serviceRoute = await _serviceRouteProvider.GetRouteByPath(routePath.ToLower()); T result = default(T); if (parameters.ContainsKey("serviceKey")) { @@ -27,18 +60,17 @@ public async Task Invoke(IDictionary parameters, string r var proxy = ServiceResolver.Current.GetService(serviceKey); if (proxy == null) { - proxy = new RemoteServiceProxy(serviceKey.ToString(), _serviceProvider); + proxy = new RemoteServiceProxy(serviceKey.ToString(), _serviceProvider); ServiceResolver.Current.Register(serviceKey.ToString(), proxy); } result = await proxy.Invoke(parameters, serviceRoute.ServiceDescriptor.Id); - } else { var proxy = ServiceResolver.Current.GetService(); if (proxy == null) { - proxy = new RemoteServiceProxy(null, _serviceProvider); + proxy = new RemoteServiceProxy(null, _serviceProvider); ServiceResolver.Current.Register(null, proxy); } result = await proxy.Invoke(parameters, serviceRoute.ServiceDescriptor.Id); @@ -46,6 +78,14 @@ public async Task Invoke(IDictionary parameters, string r return result; } + /// + /// The Invoke + /// + /// + /// The parameters + /// The routePath + /// The serviceKey + /// The public async Task Invoke(IDictionary parameters, string routePath, string serviceKey) { var serviceRoute = await _serviceRouteProvider.GetRouteByPath(routePath.ToLower()); @@ -72,5 +112,7 @@ public async Task Invoke(IDictionary parameters, string ro } return result; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/CacheInterceptor.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/CacheInterceptor.cs index 6e8b03a54..ec5864876 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/CacheInterceptor.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/CacheInterceptor.cs @@ -5,13 +5,30 @@ namespace Surging.Core.ProxyGenerator.Interceptors { + /// + /// Defines the + /// public abstract class CacheInterceptor : IInterceptor { + #region 方法 + + /// + /// The Intercept + /// + /// The invocation + /// The public abstract Task Intercept(ICacheInvocation invocation); + /// + /// The Intercept + /// + /// The invocation + /// The public async Task Intercept(IInvocation invocation) { - await Intercept(invocation as ICacheInvocation); + await Intercept(invocation as ICacheInvocation); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/ICacheInvocation.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/ICacheInvocation.cs index 7402332e1..9a5dd6064 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/ICacheInvocation.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/ICacheInvocation.cs @@ -5,10 +5,27 @@ namespace Surging.Core.ProxyGenerator.Interceptors { + #region 接口 + + /// + /// Defines the + /// public interface ICacheInvocation : IInvocation { - string[] CacheKey { get; } + #region 属性 + /// + /// Gets the Attributes + /// List Attributes { get; } + + /// + /// Gets the CacheKey + /// + string[] CacheKey { get; } + + #endregion 属性 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/IInterceptor.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/IInterceptor.cs index 73c44e857..82a189fb8 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/IInterceptor.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/IInterceptor.cs @@ -5,8 +5,24 @@ namespace Surging.Core.ProxyGenerator.Interceptors { + #region 接口 + + /// + /// Defines the + /// public interface IInterceptor { + #region 方法 + + /// + /// The Intercept + /// + /// The invocation + /// The Task Intercept(IInvocation invocation); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/IInterceptorProvider.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/IInterceptorProvider.cs index 8f401f7d3..eaf03267e 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/IInterceptorProvider.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/IInterceptorProvider.cs @@ -4,10 +4,37 @@ namespace Surging.Core.ProxyGenerator.Interceptors { + #region 接口 + + /// + /// Defines the + /// public interface IInterceptorProvider { + #region 方法 + + /// + /// The GetCacheInvocation + /// + /// The proxy + /// The parameters + /// The serviceId + /// The returnType + /// The + IInvocation GetCacheInvocation(object proxy, IDictionary parameters, string serviceId, Type returnType); + + /// + /// The GetInvocation + /// + /// The proxy + /// The parameters + /// The serviceId + /// The returnType + /// The IInvocation GetInvocation(object proxy, IDictionary parameters, string serviceId, Type returnType); - IInvocation GetCacheInvocation(object proxy, IDictionary parameters,string serviceId, Type returnType); + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/IInvocation.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/IInvocation.cs index 916d3007e..3b570eff9 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/IInvocation.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/IInvocation.cs @@ -6,20 +6,59 @@ namespace Surging.Core.ProxyGenerator.Interceptors { - public interface IInvocation + #region 接口 + + /// + /// Defines the + /// + public interface IInvocation { - object Proxy { get; } - - string ServiceId { get; } - + #region 属性 + + /// + /// Gets the Arguments + /// IDictionary Arguments { get; } + /// + /// Gets the Proxy + /// + object Proxy { get; } + + /// + /// Gets the ReturnType + /// Type ReturnType { get; } - - Task Proceed(); + /// + /// Gets or sets the ReturnValue + /// object ReturnValue { get; set; } + /// + /// Gets the ServiceId + /// + string ServiceId { get; } + + #endregion 属性 + + #region 方法 + + /// + /// The Proceed + /// + /// The + Task Proceed(); + + /// + /// The SetArgumentValue + /// + /// The index + /// The value void SetArgumentValue(int index, object value); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/Implementation/AbstractInvocation.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/Implementation/AbstractInvocation.cs index 3871e2907..3c51a3c33 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/Implementation/AbstractInvocation.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/Implementation/AbstractInvocation.cs @@ -5,16 +5,61 @@ namespace Surging.Core.ProxyGenerator.Interceptors.Implementation { - public abstract class AbstractInvocation : IInvocation,ICacheInvocation + /// + /// Defines the + /// + public abstract class AbstractInvocation : IInvocation, ICacheInvocation { + #region 字段 + + /// + /// Defines the proxyObject + /// + protected readonly object proxyObject; + + /// + /// Defines the _arguments + /// private readonly IDictionary _arguments; - private readonly string _serviceId; - private readonly string[] _cacheKey; + + /// + /// Defines the _attributes + /// private readonly List _attributes; + + /// + /// Defines the _cacheKey + /// + private readonly string[] _cacheKey; + + /// + /// Defines the _returnType + /// private readonly Type _returnType; - protected readonly object proxyObject; + + /// + /// Defines the _serviceId + /// + private readonly string _serviceId; + + /// + /// Defines the _returnValue + /// protected object _returnValue; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The arguments + /// The serviceId + /// The cacheKey + /// The attributes + /// The returnType + /// The proxy protected AbstractInvocation( IDictionary arguments, string serviceId, @@ -32,25 +77,65 @@ object proxy proxyObject = proxy; } - public object Proxy => proxyObject; + #endregion 构造函数 - public string ServiceId => _serviceId; - public string[] CacheKey => _cacheKey; + #region 属性 + /// + /// Gets the Arguments + /// public IDictionary Arguments => _arguments; + /// + /// Gets the Attributes + /// public List Attributes => _attributes; - object IInvocation.ReturnValue { get => _returnValue; set => _returnValue = value; } + /// + /// Gets the CacheKey + /// + public string[] CacheKey => _cacheKey; + + /// + /// Gets the Proxy + /// + public object Proxy => proxyObject; + /// + /// Gets the ReturnType + /// public Type ReturnType => _returnType; - public abstract Task Proceed(); + /// + /// Gets the ServiceId + /// + public string ServiceId => _serviceId; + + /// + /// Gets or sets the ReturnValue + /// + object IInvocation.ReturnValue { get => _returnValue; set => _returnValue = value; } + + #endregion 属性 + #region 方法 + /// + /// The Proceed + /// + /// The + public abstract Task Proceed(); + + /// + /// The SetArgumentValue + /// + /// The index + /// The value public void SetArgumentValue(int index, object value) { throw new NotImplementedException(); } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/Implementation/ActionInvocation.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/Implementation/ActionInvocation.cs index 6894ced20..074239e38 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/Implementation/ActionInvocation.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/Implementation/ActionInvocation.cs @@ -7,8 +7,22 @@ namespace Surging.Core.ProxyGenerator.Interceptors.Implementation { + /// + /// Defines the + /// public class ActionInvocation : AbstractInvocation - { + { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The arguments + /// The serviceId + /// The cacheKey + /// The attributes + /// The returnType + /// The proxy protected ActionInvocation( IDictionary arguments, string serviceId, @@ -20,17 +34,27 @@ object proxy { } + #endregion 构造函数 + + #region 方法 + + /// + /// The Proceed + /// + /// The public override async Task Proceed() { try { - if(_returnValue ==null) - _returnValue = await (Proxy as ServiceProxyBase).CallInvoke(this); + if (_returnValue == null) + _returnValue = await (Proxy as ServiceProxyBase).CallInvoke(this); } catch (Exception ex) { throw ex; } } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/Implementation/InterceptorProvider.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/Implementation/InterceptorProvider.cs index aa9dc6d45..522941a9b 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/Implementation/InterceptorProvider.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/Implementation/InterceptorProvider.cs @@ -1,56 +1,103 @@ -using Surging.Core.CPlatform.Runtime.Server; -using System.Collections.Generic; -using System.Linq; +using Microsoft.Extensions.Logging; using Surging.Core.CPlatform; -using System.Reflection; -using System.Collections; +using Surging.Core.CPlatform.Runtime.Server; using Surging.Core.ProxyGenerator.Utilitys; -using System.Collections.Concurrent; using System; -using Microsoft.Extensions.Logging; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; namespace Surging.Core.ProxyGenerator.Interceptors.Implementation { + /// + /// Defines the + /// public class InterceptorProvider : IInterceptorProvider { + #region 字段 + + /// + /// Defines the _serviceEntryManager + /// private readonly IServiceEntryManager _serviceEntryManager; - ConcurrentDictionary,bool> _derivedTypes = new ConcurrentDictionary, bool>(); + + /// + /// Defines the _derivedTypes + /// + internal ConcurrentDictionary, bool> _derivedTypes = new ConcurrentDictionary, bool>(); + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serviceEntryManager public InterceptorProvider(IServiceEntryManager serviceEntryManager) { _serviceEntryManager = serviceEntryManager; } - public IInvocation GetInvocation(object proxy, IDictionary parameters, - string serviceId,Type returnType) + + #endregion 构造函数 + + #region 方法 + + /// + /// The GetCacheInvocation + /// + /// The proxy + /// The parameters + /// The serviceId + /// The returnType + /// The + public IInvocation GetCacheInvocation(object proxy, IDictionary parameters, + string serviceId, Type returnType) { + var entry = (from q in _serviceEntryManager.GetAllEntries() + let k = q.Attributes + where q.Descriptor.Id == serviceId + select q).FirstOrDefault(); var constructor = InvocationMethods.CompositionInvocationConstructor; return constructor.Invoke(new object[]{ parameters, serviceId, - null, - null, + GetKey(parameters), + entry.Attributes, returnType, proxy }) as IInvocation; } - public IInvocation GetCacheInvocation(object proxy, IDictionary parameters, - string serviceId, Type returnType) + /// + /// The GetInvocation + /// + /// The proxy + /// The parameters + /// The serviceId + /// The returnType + /// The + public IInvocation GetInvocation(object proxy, IDictionary parameters, + string serviceId, Type returnType) { - var entry = (from q in _serviceEntryManager.GetAllEntries() - let k = q.Attributes - where q.Descriptor.Id == serviceId - select q).FirstOrDefault(); var constructor = InvocationMethods.CompositionInvocationConstructor; return constructor.Invoke(new object[]{ parameters, serviceId, - GetKey(parameters), - entry.Attributes, + null, + null, returnType, proxy }) as IInvocation; } + /// + /// The GetKey + /// + /// The parameterValue + /// The private string[] GetKey(IDictionary parameterValue) { var param = parameterValue.Values.FirstOrDefault(); @@ -74,13 +121,19 @@ private string[] GetKey(IDictionary parameterValue) return reuslt; } - private bool IsKeyAttributeDerivedType(Type baseType,Type derivedType) + /// + /// The IsKeyAttributeDerivedType + /// + /// The baseType + /// The derivedType + /// The + private bool IsKeyAttributeDerivedType(Type baseType, Type derivedType) { bool result = false; var key = Tuple.Create(baseType, derivedType); if (!_derivedTypes.ContainsKey(key)) { - result =_derivedTypes.GetOrAdd(key, derivedType.IsSubclassOf(baseType) || derivedType == baseType); + result = _derivedTypes.GetOrAdd(key, derivedType.IsSubclassOf(baseType) || derivedType == baseType); } else { @@ -88,5 +141,7 @@ private bool IsKeyAttributeDerivedType(Type baseType,Type derivedType) } return result; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/Implementation/KeyAttribute.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/Implementation/KeyAttribute.cs index 6b4a14df5..8077bedc2 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/Implementation/KeyAttribute.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/Implementation/KeyAttribute.cs @@ -9,12 +9,28 @@ namespace Surging.Core.ProxyGenerator.Interceptors.Implementation /// [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)] - public abstract class KeyAttribute : Attribute + public abstract class KeyAttribute : Attribute { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The sortIndex protected KeyAttribute(int sortIndex) { SortIndex = sortIndex; } + + #endregion 构造函数 + + #region 属性 + + /// + /// Gets or sets the SortIndex + /// public int SortIndex { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/InvocationMethods.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/InvocationMethods.cs index ef6b188a9..4b1b4291d 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/InvocationMethods.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/Interceptors/InvocationMethods.cs @@ -7,8 +7,16 @@ namespace Surging.Core.ProxyGenerator.Interceptors { - public class InvocationMethods + /// + /// Defines the + /// + public class InvocationMethods { + #region 字段 + + /// + /// Defines the CompositionInvocationConstructor + /// public static readonly ConstructorInfo CompositionInvocationConstructor = typeof(ActionInvocation).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] @@ -22,6 +30,6 @@ public class InvocationMethods }, null); - + #endregion 字段 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/ProxyServiceBase.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/ProxyServiceBase.cs index 3be66b073..8a31cd090 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/ProxyServiceBase.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/ProxyServiceBase.cs @@ -1,82 +1,96 @@ -using Surging.Core.CPlatform.Ioc; -using Surging.Core.CPlatform.Utilities; -using System; -using Autofac; +using Autofac; +using Surging.Core.CPlatform.DependencyResolution; using Surging.Core.CPlatform.EventBus.Events; using Surging.Core.CPlatform.EventBus.Implementation; -using Surging.Core.CPlatform.DependencyResolution; +using Surging.Core.CPlatform.Ioc; +using Surging.Core.CPlatform.Utilities; +using System; namespace Surging.Core.ProxyGenerator { - public abstract class ProxyServiceBase: ServiceBase + /// + /// Defines the + /// + public abstract class ProxyServiceBase : ServiceBase { + #region 方法 + + /// + /// The CreateProxy + /// + /// + /// The [Obsolete("This method is Obsolete, use GetService")] - public T CreateProxy(string key) where T : class + public T CreateProxy() where T : class { - // return ServiceLocator.GetService().CreateProxy(key); - var result = ServiceResolver.Current.GetService(key); + var result = ServiceResolver.Current.GetService(); if (result == null) { - result = ServiceLocator.GetService().CreateProxy(key); - ServiceResolver.Current.Register(key, result); - + result = ServiceLocator.GetService().CreateProxy(); + ServiceResolver.Current.Register(null, result); } return result; } + /// + /// The CreateProxy + /// + /// + /// The key + /// The [Obsolete("This method is Obsolete, use GetService")] - public object CreateProxy(Type type) + public T CreateProxy(string key) where T : class { - var result = ServiceResolver.Current.GetService(type); + // return ServiceLocator.GetService().CreateProxy(key); + var result = ServiceResolver.Current.GetService(key); if (result == null) { - result = ServiceLocator.GetService().CreateProxy(type); - ServiceResolver.Current.Register(null, result); - + result = ServiceLocator.GetService().CreateProxy(key); + ServiceResolver.Current.Register(key, result); } return result; } + /// + /// The CreateProxy + /// + /// The key + /// The type + /// The [Obsolete("This method is Obsolete, use GetService")] public object CreateProxy(string key, Type type) { - var result = ServiceResolver.Current.GetService(type,key); + var result = ServiceResolver.Current.GetService(type, key); if (result == null) { - result = ServiceLocator.GetService().CreateProxy(key,type); + result = ServiceLocator.GetService().CreateProxy(key, type); ServiceResolver.Current.Register(key, result); } return result; } + /// + /// The CreateProxy + /// + /// The type + /// The [Obsolete("This method is Obsolete, use GetService")] - public T CreateProxy() where T : class + public object CreateProxy(Type type) { - var result = ServiceResolver.Current.GetService(); + var result = ServiceResolver.Current.GetService(type); if (result == null) { - result = ServiceLocator.GetService().CreateProxy(); - ServiceResolver.Current.Register(null,result); + result = ServiceLocator.GetService().CreateProxy(type); + ServiceResolver.Current.Register(null, result); } return result; } - public override T GetService(string key) - { - if (ServiceLocator.Current.IsRegisteredWithKey(key)) - return base.GetService(key); - else - { - var result = ServiceResolver.Current.GetService(key); - if (result == null) - { - result = ServiceLocator.GetService().CreateProxy(key); - ServiceResolver.Current.Register(key, result); - } - return result; - } - } - + /// + /// The GetService + /// + /// + /// The public override T GetService() { if (ServiceLocator.Current.IsRegistered()) @@ -91,25 +105,36 @@ public override T GetService() } return result; } - } - public override object GetService(Type type) + /// + /// The GetService + /// + /// + /// The key + /// The + public override T GetService(string key) { - if (ServiceLocator.Current.IsRegistered(type)) - return base.GetService(type); + if (ServiceLocator.Current.IsRegisteredWithKey(key)) + return base.GetService(key); else { - var result = ServiceResolver.Current.GetService(type); + var result = ServiceResolver.Current.GetService(key); if (result == null) { - result = ServiceLocator.GetService().CreateProxy(type); - ServiceResolver.Current.Register(null, result); + result = ServiceLocator.GetService().CreateProxy(key); + ServiceResolver.Current.Register(key, result); } return result; } } + /// + /// The GetService + /// + /// The key + /// The type + /// The public override object GetService(string key, Type type) { if (ServiceLocator.Current.IsRegisteredWithKey(key, type)) @@ -124,12 +149,38 @@ public override object GetService(string key, Type type) } return result; } - } + /// + /// The GetService + /// + /// The type + /// The + public override object GetService(Type type) + { + if (ServiceLocator.Current.IsRegistered(type)) + return base.GetService(type); + else + { + var result = ServiceResolver.Current.GetService(type); + if (result == null) + { + result = ServiceLocator.GetService().CreateProxy(type); + ServiceResolver.Current.Register(null, result); + } + return result; + } + } + + /// + /// The Publish + /// + /// The event public void Publish(IntegrationEvent @event) { GetService().Publish(@event); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/RegistrationExtensions.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/RegistrationExtensions.cs index b889f42a3..532ad9b8f 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/RegistrationExtensions.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/RegistrationExtensions.cs @@ -7,19 +7,35 @@ namespace Surging.Core.ProxyGenerator { - public static class RegistrationExtensions + /// + /// Defines the + /// + public static class RegistrationExtensions { - public static void AddClientIntercepted(this ContainerBuilderWrapper builder, Type interceptorServiceType) - { + #region 方法 + + /// + /// The AddClientIntercepted + /// + /// The builder + /// The interceptorServiceType + public static void AddClientIntercepted(this ContainerBuilderWrapper builder, Type interceptorServiceType) + { builder.RegisterType(interceptorServiceType).As().SingleInstance(); builder.RegisterType().As().SingleInstance(); } + /// + /// The AddClientIntercepted + /// + /// The builder + /// The interceptorServiceTypes public static void AddClientIntercepted(this ContainerBuilderWrapper builder, params Type[] interceptorServiceTypes) - { + { builder.RegisterTypes(interceptorServiceTypes).As().SingleInstance(); builder.RegisterType().As().SingleInstance(); - } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/ServiceHostBuilderExtensions.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/ServiceHostBuilderExtensions.cs index e49a34289..a96dfdb57 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/ServiceHostBuilderExtensions.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/ServiceHostBuilderExtensions.cs @@ -4,8 +4,18 @@ namespace Surging.Core.ProxyGenerator { + /// + /// Defines the + /// public static class ServiceHostBuilderExtensions { + #region 方法 + + /// + /// The UseProxy + /// + /// The hostBuilder + /// The public static IServiceHostBuilder UseProxy(this IServiceHostBuilder hostBuilder) { return hostBuilder.MapServices(mapper => @@ -13,8 +23,10 @@ public static IServiceHostBuilder UseProxy(this IServiceHostBuilder hostBuilder) mapper.Resolve().ServiceEngineStarted.Register(() => { mapper.Resolve(); - }); + }); }); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/ServiceProxyModule.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/ServiceProxyModule.cs index c8130063d..8f9585579 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/ServiceProxyModule.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/ServiceProxyModule.cs @@ -5,17 +5,26 @@ using Surging.Core.CPlatform.Module; using Surging.Core.CPlatform.Routing; using Surging.Core.CPlatform.Runtime.Client; +using Surging.Core.CPlatform.Runtime.Server; using Surging.Core.ProxyGenerator.Implementation; using System; using System.Collections.Generic; -using System.Text; using System.Linq; -using Surging.Core.CPlatform.Runtime.Server; +using System.Text; namespace Surging.Core.ProxyGenerator { - public class ServiceProxyModule: EnginePartModule + /// + /// Defines the + /// + public class ServiceProxyModule : EnginePartModule { + #region 方法 + + /// + /// The Initialize + /// + /// The context public override void Initialize(AppModuleContext context) { var serviceProvider = context.ServiceProvoider; @@ -31,8 +40,8 @@ public override void Initialize(AppModuleContext context) { builder.Update(serviceProvider.Current.ComponentRegistry); serviceProvider.GetInstances().UpdateEntries(serviceProvider.GetInstances>()); - // serviceProvider.GetInstances().RegisterProxType(result.Value.Item2.ToArray(), result.Value.Item1.ToArray()); - serviceProvider.GetInstances().RegisterRoutes(0); + // serviceProvider.GetInstances().RegisterProxType(result.Value.Item2.ToArray(), result.Value.Item1.ToArray()); + serviceProvider.GetInstances().RegisterRoutes(0); serviceProvider.GetInstances(); } }); @@ -46,8 +55,8 @@ public override void Initialize(AppModuleContext context) protected override void RegisterBuilder(ContainerBuilderWrapper builder) { base.RegisterBuilder(builder); - } - + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/Utilitys/AttributeFactory.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/Utilitys/AttributeFactory.cs index 0b532caf0..65eb09ac8 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/Utilitys/AttributeFactory.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/Utilitys/AttributeFactory.cs @@ -7,8 +7,31 @@ namespace Surging.Core.ProxyGenerator.Utilitys { - class AttributeFactory + /// + /// Defines the + /// + internal class AttributeFactory { + #region 字段 + + /// + /// Defines the m_attributeCreator + /// + private Func m_attributeCreator; + + /// + /// Defines the m_propertySetters + /// + private List> m_propertySetters; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The data public AttributeFactory(CustomAttributeData data) { this.Data = data; @@ -27,11 +50,23 @@ public AttributeFactory(CustomAttributeData data) } } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the Data + /// public CustomAttributeData Data { get; private set; } - private Func m_attributeCreator; - private List> m_propertySetters; + #endregion 属性 + + #region 方法 + /// + /// The Create + /// + /// The public Attribute Create() { var attribute = this.m_attributeCreator(); @@ -43,5 +78,7 @@ public Attribute Create() return (Attribute)attribute; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ProxyGenerator/Utilitys/CompilationUtilitys.cs b/src/Surging.Core/Surging.Core.ProxyGenerator/Utilitys/CompilationUtilitys.cs index 9b5364746..390660c50 100644 --- a/src/Surging.Core/Surging.Core.ProxyGenerator/Utilitys/CompilationUtilitys.cs +++ b/src/Surging.Core/Surging.Core.ProxyGenerator/Utilitys/CompilationUtilitys.cs @@ -1,6 +1,8 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyModel; using Microsoft.Extensions.Logging; using Surging.Core.CPlatform.Runtime.Client; using System.Collections.Generic; @@ -9,47 +11,40 @@ using System.Reflection; using System.Threading.Tasks; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; -using Microsoft.Extensions.DependencyInjection; - -#if !NET - -using Microsoft.Extensions.DependencyModel; #endif namespace Surging.Core.ProxyGenerator.Utilitys { + /// + /// Defines the + /// public static class CompilationUtilitys { - #region Public Method - - public static MemoryStream CompileClientProxy(IEnumerable trees, IEnumerable references, ILogger logger = null) - { -#if !NET - var assemblys = new[] - { - "System.Runtime", - "mscorlib", - "System.Threading.Tasks", - "System.Collections" - }; - references = assemblys.Select(i => MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName(i)).Location)).Concat(references); -#endif - references = new[] - { - MetadataReference.CreateFromFile(typeof(Task).GetTypeInfo().Assembly.Location), - MetadataReference.CreateFromFile(typeof(ServiceDescriptor).GetTypeInfo().Assembly.Location), - MetadataReference.CreateFromFile(typeof(IRemoteInvokeService).GetTypeInfo().Assembly.Location), - MetadataReference.CreateFromFile(typeof(IServiceProxyGenerater).GetTypeInfo().Assembly.Location) - }.Concat(references); - return Compile(AssemblyInfo.Create("Surging.Cores.ClientProxys"), trees, references, logger); - } + #region 方法 + /// + /// The Compile + /// + /// The assemblyInfo + /// The trees + /// The references + /// The logger + /// The public static MemoryStream Compile(AssemblyInfo assemblyInfo, IEnumerable trees, IEnumerable references, ILogger logger = null) { return Compile(assemblyInfo.Title, assemblyInfo, trees, references, logger); } + /// + /// The Compile + /// + /// The assemblyName + /// The assemblyInfo + /// The trees + /// The references + /// The logger + /// The public static MemoryStream Compile(string assemblyName, AssemblyInfo assemblyInfo, IEnumerable trees, IEnumerable references, ILogger logger = null) { trees = trees.Concat(new[] { GetAssemblyInfo(assemblyInfo) }); @@ -68,10 +63,40 @@ public static MemoryStream Compile(string assemblyName, AssemblyInfo assemblyInf return stream; } - #endregion Public Method - - #region Private Method + /// + /// The CompileClientProxy + /// + /// The trees + /// The references + /// The logger + /// The + public static MemoryStream CompileClientProxy(IEnumerable trees, IEnumerable references, ILogger logger = null) + { +#if !NET + var assemblys = new[] + { + "System.Runtime", + "mscorlib", + "System.Threading.Tasks", + "System.Collections" + }; + references = assemblys.Select(i => MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName(i)).Location)).Concat(references); +#endif + references = new[] + { + MetadataReference.CreateFromFile(typeof(Task).GetTypeInfo().Assembly.Location), + MetadataReference.CreateFromFile(typeof(ServiceDescriptor).GetTypeInfo().Assembly.Location), + MetadataReference.CreateFromFile(typeof(IRemoteInvokeService).GetTypeInfo().Assembly.Location), + MetadataReference.CreateFromFile(typeof(IServiceProxyGenerater).GetTypeInfo().Assembly.Location) + }.Concat(references); + return Compile(AssemblyInfo.Create("Surging.Cores.ClientProxys"), trees, references, logger); + } + /// + /// The GetAssemblyInfo + /// + /// The info + /// The private static SyntaxTree GetAssemblyInfo(AssemblyInfo info) { return CompilationUnit() @@ -226,20 +251,61 @@ private static SyntaxTree GetAssemblyInfo(AssemblyInfo info) .SyntaxTree; } - #endregion Private Method - - #region Help Class + #endregion 方法 + /// + /// Defines the + /// public class AssemblyInfo { - public string Title { get; set; } - public string Product { get; set; } + #region 属性 + + /// + /// Gets or sets a value indicating whether ComVisible + /// + public bool ComVisible { get; set; } + + /// + /// Gets or sets the Copyright + /// public string Copyright { get; set; } + + /// + /// Gets or sets the FileVersion + /// + public string FileVersion { get; set; } + + /// + /// Gets or sets the Guid + /// public string Guid { get; set; } + + /// + /// Gets or sets the Product + /// + public string Product { get; set; } + + /// + /// Gets or sets the Title + /// + public string Title { get; set; } + + /// + /// Gets or sets the Version + /// public string Version { get; set; } - public string FileVersion { get; set; } - public bool ComVisible { get; set; } + #endregion 属性 + + #region 方法 + + /// + /// The Create + /// + /// The name + /// The copyright + /// The version + /// The public static AssemblyInfo Create(string name, string copyright = "Copyright © Surging", string version = "0.0.0.1") { return new AssemblyInfo @@ -253,8 +319,8 @@ public static AssemblyInfo Create(string name, string copyright = "Copyright © FileVersion = version }; } - } - #endregion Help Class + #endregion 方法 + } } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ServiceHosting/Internal/IApplicationLifetime.cs b/src/Surging.Core/Surging.Core.ServiceHosting/Internal/IApplicationLifetime.cs index dd108339d..a5f7d3c90 100644 --- a/src/Surging.Core/Surging.Core.ServiceHosting/Internal/IApplicationLifetime.cs +++ b/src/Surging.Core/Surging.Core.ServiceHosting/Internal/IApplicationLifetime.cs @@ -5,19 +5,51 @@ namespace Surging.Core.ServiceHosting.Internal { - public interface IApplicationLifetime + #region 接口 + + /// + /// Defines the + /// + public interface IApplicationLifetime { + #region 属性 + + /// + /// Gets the ApplicationStarted + /// CancellationToken ApplicationStarted { get; } - - CancellationToken ApplicationStopping { get; } - + + /// + /// Gets the ApplicationStopped + /// CancellationToken ApplicationStopped { get; } - - void StopApplication(); + /// + /// Gets the ApplicationStopping + /// + CancellationToken ApplicationStopping { get; } - void NotifyStopped(); + #endregion 属性 + #region 方法 + + /// + /// The NotifyStarted + /// void NotifyStarted(); + + /// + /// The NotifyStopped + /// + void NotifyStopped(); + + /// + /// The StopApplication + /// + void StopApplication(); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ServiceHosting/Internal/IHostLifetime.cs b/src/Surging.Core/Surging.Core.ServiceHosting/Internal/IHostLifetime.cs index 20037cbfc..9c2b6546b 100644 --- a/src/Surging.Core/Surging.Core.ServiceHosting/Internal/IHostLifetime.cs +++ b/src/Surging.Core/Surging.Core.ServiceHosting/Internal/IHostLifetime.cs @@ -6,11 +6,31 @@ namespace Surging.Core.ServiceHosting.Internal { - public interface IHostLifetime + #region 接口 + + /// + /// Defines the + /// + public interface IHostLifetime { - - Task WaitForStartAsync(CancellationToken cancellationToken); - + #region 方法 + + /// + /// The StopAsync + /// + /// The cancellationToken + /// The Task StopAsync(CancellationToken cancellationToken); + + /// + /// The WaitForStartAsync + /// + /// The cancellationToken + /// The + Task WaitForStartAsync(CancellationToken cancellationToken); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ServiceHosting/Internal/IServiceHost.cs b/src/Surging.Core/Surging.Core.ServiceHosting/Internal/IServiceHost.cs index 037e86f01..f8f75cc95 100644 --- a/src/Surging.Core/Surging.Core.ServiceHosting/Internal/IServiceHost.cs +++ b/src/Surging.Core/Surging.Core.ServiceHosting/Internal/IServiceHost.cs @@ -7,10 +7,29 @@ namespace Surging.Core.ServiceHosting.Internal { - public interface IServiceHost : IDisposable + #region 接口 + + /// + /// Defines the + /// + public interface IServiceHost : IDisposable { - IDisposable Run(); + #region 方法 + /// + /// The Initialize + /// + /// The IContainer Initialize(); + + /// + /// The Run + /// + /// The + IDisposable Run(); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ServiceHosting/Internal/IServiceHostBuilder.cs b/src/Surging.Core/Surging.Core.ServiceHosting/Internal/IServiceHostBuilder.cs index cbab1d1d6..d35f61e3a 100644 --- a/src/Surging.Core/Surging.Core.ServiceHosting/Internal/IServiceHostBuilder.cs +++ b/src/Surging.Core/Surging.Core.ServiceHosting/Internal/IServiceHostBuilder.cs @@ -8,19 +8,58 @@ namespace Surging.Core.ServiceHosting.Internal { - public interface IServiceHostBuilder + #region 接口 + + /// + /// Defines the + /// + public interface IServiceHostBuilder { + #region 方法 + + /// + /// The Build + /// + /// The IServiceHost Build(); - IServiceHostBuilder RegisterServices(Action builder); + /// + /// The Configure + /// + /// The builder + /// The + IServiceHostBuilder Configure(Action builder); + /// + /// The ConfigureLogging + /// + /// The configure + /// The IServiceHostBuilder ConfigureLogging(Action configure); + /// + /// The ConfigureServices + /// + /// The configureServices + /// The IServiceHostBuilder ConfigureServices(Action configureServices); - IServiceHostBuilder Configure(Action builder); - + /// + /// The MapServices + /// + /// The mapper + /// The IServiceHostBuilder MapServices(Action mapper); - + + /// + /// The RegisterServices + /// + /// The builder + /// The + IServiceHostBuilder RegisterServices(Action builder); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ApplicationLifetime.cs b/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ApplicationLifetime.cs index 1de4df3e1..682d4e476 100644 --- a/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ApplicationLifetime.cs +++ b/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ApplicationLifetime.cs @@ -6,24 +6,72 @@ namespace Surging.Core.ServiceHosting.Internal.Implementation { + /// + /// Defines the + /// public class ApplicationLifetime : IApplicationLifetime { + #region 字段 + + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the _startedSource + /// private readonly CancellationTokenSource _startedSource = new CancellationTokenSource(); - private readonly CancellationTokenSource _stoppingSource = new CancellationTokenSource(); + + /// + /// Defines the _stoppedSource + /// private readonly CancellationTokenSource _stoppedSource = new CancellationTokenSource(); - private readonly ILogger _logger; + /// + /// Defines the _stoppingSource + /// + private readonly CancellationTokenSource _stoppingSource = new CancellationTokenSource(); + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The logger public ApplicationLifetime(ILogger logger) { _logger = logger; } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the ApplicationStarted + /// public CancellationToken ApplicationStarted => _startedSource.Token; + /// + /// Gets the ApplicationStopped + /// + public CancellationToken ApplicationStopped => _stoppedSource.Token; + + /// + /// Gets the ApplicationStopping + /// public CancellationToken ApplicationStopping => _stoppingSource.Token; - public CancellationToken ApplicationStopped => _stoppedSource.Token; + #endregion 属性 + #region 方法 + + /// + /// The NotifyStarted + /// public void NotifyStarted() { try @@ -32,12 +80,14 @@ public void NotifyStarted() } catch (Exception ex) { - _logger.LogError( "An error occurred starting the application", + _logger.LogError("An error occurred starting the application", ex); } } - + /// + /// The NotifyStopped + /// public void NotifyStopped() { try @@ -51,6 +101,9 @@ public void NotifyStopped() } } + /// + /// The StopApplication + /// public void StopApplication() { lock (_stoppingSource) @@ -67,13 +120,19 @@ public void StopApplication() } } + /// + /// The ExecuteHandlers + /// + /// The cancel private void ExecuteHandlers(CancellationTokenSource cancel) - { + { if (cancel.IsCancellationRequested) { return; - } + } cancel.Cancel(throwOnFirstException: false); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ConfigureBuilder.cs b/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ConfigureBuilder.cs index 610a2c7ba..f59beab4a 100644 --- a/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ConfigureBuilder.cs +++ b/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ConfigureBuilder.cs @@ -6,21 +6,51 @@ namespace Surging.Core.ServiceHosting.Internal.Implementation { + /// + /// Defines the + /// public class ConfigureBuilder { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The configure public ConfigureBuilder(MethodInfo configure) { MethodInfo = configure; } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the MethodInfo + /// public MethodInfo MethodInfo { get; } + #endregion 属性 + + #region 方法 + + /// + /// The Build + /// + /// The instance + /// The public Action Build(object instance) => builder => Invoke(instance, builder); + /// + /// The Invoke + /// + /// The instance + /// The builder private void Invoke(object instance, IContainer builder) { using (var scope = builder.BeginLifetimeScope()) - { + { var parameterInfos = MethodInfo.GetParameters(); var parameters = new object[parameterInfos.Length]; for (var index = 0; index < parameterInfos.Length; index++) @@ -50,5 +80,7 @@ private void Invoke(object instance, IContainer builder) MethodInfo.Invoke(instance, parameters); } } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ConfigureContainerBuilder.cs b/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ConfigureContainerBuilder.cs index 5187bfd6b..e660148d7 100644 --- a/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ConfigureContainerBuilder.cs +++ b/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ConfigureContainerBuilder.cs @@ -5,17 +5,46 @@ namespace Surging.Core.ServiceHosting.Internal.Implementation { + /// + /// Defines the + /// public class ConfigureContainerBuilder { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The configureContainerMethod public ConfigureContainerBuilder(MethodInfo configureContainerMethod) { MethodInfo = configureContainerMethod; } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the MethodInfo + /// public MethodInfo MethodInfo { get; } + #endregion 属性 + + #region 方法 + + /// + /// The Build + /// + /// The instance + /// The public Action Build(object instance) => container => Invoke(instance, container); + /// + /// The GetContainerType + /// + /// The public Type GetContainerType() { var parameters = MethodInfo.GetParameters(); @@ -26,6 +55,11 @@ public Type GetContainerType() return parameters[0].ParameterType; } + /// + /// The Invoke + /// + /// The instance + /// The container private void Invoke(object instance, object container) { if (MethodInfo == null) @@ -35,5 +69,7 @@ private void Invoke(object instance, object container) var arguments = new object[1] { container }; MethodInfo.Invoke(instance, arguments); } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ConfigureServicesBuilder.cs b/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ConfigureServicesBuilder.cs index 4062f4a2f..106c265fb 100644 --- a/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ConfigureServicesBuilder.cs +++ b/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ConfigureServicesBuilder.cs @@ -8,17 +8,48 @@ namespace Surging.Core.ServiceHosting.Internal.Implementation { - public class ConfigureServicesBuilder + /// + /// Defines the + /// + public class ConfigureServicesBuilder { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The configureServices public ConfigureServicesBuilder(MethodInfo configureServices) { MethodInfo = configureServices; } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the MethodInfo + /// public MethodInfo MethodInfo { get; } + #endregion 属性 + + #region 方法 + + /// + /// The Build + /// + /// The instance + /// The public Func Build(object instance) => services => Invoke(instance, services); + /// + /// The Invoke + /// + /// The instance + /// The services + /// The private IContainer Invoke(object instance, ContainerBuilder services) { if (MethodInfo == null) @@ -43,5 +74,7 @@ private IContainer Invoke(object instance, ContainerBuilder services) return MethodInfo.Invoke(instance, arguments) as IContainer; } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ConsoleLifetime.cs b/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ConsoleLifetime.cs index 030dca744..733100721 100644 --- a/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ConsoleLifetime.cs +++ b/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ConsoleLifetime.cs @@ -11,12 +11,62 @@ namespace Surging.Core.ServiceHosting.Internal.Implementation /// public class ConsoleLifetime : IHostLifetime { + #region 字段 + + /// + /// Defines the _shutdownBlock + /// private readonly ManualResetEvent _shutdownBlock = new ManualResetEvent(false); + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The applicationLifetime public ConsoleLifetime(IApplicationLifetime applicationLifetime) { ApplicationLifetime = applicationLifetime ?? throw new ArgumentNullException(nameof(applicationLifetime)); } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the ApplicationLifetime + /// + private IApplicationLifetime ApplicationLifetime { get; } + + #endregion 属性 + + #region 方法 + + /// + /// The Dispose + /// + public void Dispose() + { + _shutdownBlock.Set(); + } + + /// + /// The StopAsync + /// + /// The cancellationToken + /// The + public Task StopAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + + /// + /// The WaitForStartAsync + /// + /// The cancellationToken + /// The public Task WaitForStartAsync(CancellationToken cancellationToken) { ApplicationLifetime.ApplicationStarted.Register(() => @@ -40,15 +90,6 @@ public Task WaitForStartAsync(CancellationToken cancellationToken) return Task.CompletedTask; } - public Task StopAsync(CancellationToken cancellationToken) - { - return Task.CompletedTask; - } - - public void Dispose() - { - _shutdownBlock.Set(); - } - private IApplicationLifetime ApplicationLifetime { get; } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ServiceHost.cs b/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ServiceHost.cs index d2d7d5aa8..5f7a4fc54 100644 --- a/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ServiceHost.cs +++ b/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ServiceHost.cs @@ -1,24 +1,67 @@ -using System; +using Autofac; +using Microsoft.Extensions.DependencyInjection; +using Surging.Core.ServiceHosting.Startup; +using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.Threading.Tasks; -using Autofac; -using Microsoft.Extensions.DependencyInjection; -using Surging.Core.ServiceHosting.Startup; namespace Surging.Core.ServiceHosting.Internal.Implementation { + /// + /// Defines the + /// public class ServiceHost : IServiceHost { + #region 字段 + + /// + /// Defines the _builder + /// private readonly ContainerBuilder _builder; - private IStartup _startup; - private IContainer _applicationServices; - private readonly IHostLifetime _hostLifetime; + + /// + /// Defines the _hostingServiceProvider + /// private readonly IServiceProvider _hostingServiceProvider; + + /// + /// Defines the _hostLifetime + /// + private readonly IHostLifetime _hostLifetime; + + /// + /// Defines the _mapServicesDelegates + /// private readonly List> _mapServicesDelegates; - private IApplicationLifetime _applicationLifetime; + /// + /// Defines the _applicationLifetime + /// + private IApplicationLifetime _applicationLifetime; + + /// + /// Defines the _applicationServices + /// + private IContainer _applicationServices; + + /// + /// Defines the _startup + /// + private IStartup _startup; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The builder + /// The hostingServiceProvider + /// The hostLifetime + /// The mapServicesDelegate public ServiceHost(ContainerBuilder builder, IServiceProvider hostingServiceProvider, IHostLifetime hostLifetime, @@ -27,24 +70,53 @@ public ServiceHost(ContainerBuilder builder, _builder = builder; _hostingServiceProvider = hostingServiceProvider; _hostLifetime = hostLifetime; - _mapServicesDelegates = mapServicesDelegate; + _mapServicesDelegates = mapServicesDelegate; } + #endregion 构造函数 + + #region 方法 + + /// + /// The Dispose + /// public void Dispose() { (_hostingServiceProvider as IDisposable)?.Dispose(); } + /// + /// The Initialize + /// + /// The + public IContainer Initialize() + { + if (_applicationServices == null) + { + _applicationServices = BuildApplication(); + } + return _applicationServices; + } + + /// + /// The Run + /// + /// The public IDisposable Run() { RunAsync().GetAwaiter().GetResult(); return this; } + /// + /// The RunAsync + /// + /// The cancellationToken + /// The public async Task RunAsync(CancellationToken cancellationToken = default(CancellationToken)) { if (_applicationServices != null) - MapperServices(_applicationServices); + MapperServices(_applicationServices); if (_hostLifetime != null) { @@ -55,29 +127,49 @@ public IDisposable Run() } } + /// + /// The StopAsync + /// + /// The cancellationToken + /// The public async Task StopAsync(CancellationToken cancellationToken = default(CancellationToken)) - { - + { using (var cts = new CancellationTokenSource(2000)) using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, cancellationToken)) { - var token = linkedCts.Token; - _applicationLifetime?.StopApplication(); + var token = linkedCts.Token; + _applicationLifetime?.StopApplication(); token.ThrowIfCancellationRequested(); - await _hostLifetime.StopAsync(token); + await _hostLifetime.StopAsync(token); _applicationLifetime?.NotifyStopped(); } } - public IContainer Initialize() + /// + /// The BuildApplication + /// + /// The + private IContainer BuildApplication() { - if (_applicationServices == null) + try { - _applicationServices = BuildApplication(); + EnsureApplicationServices(); + Action configure = _startup.Configure; + if (_applicationServices == null) + _applicationServices = _builder.Build(); + configure(_applicationServices); + return _applicationServices; + } + catch (Exception ex) + { + Console.Out.WriteLine("应用程序启动异常: " + ex.ToString()); + throw; } - return _applicationServices; } + /// + /// The EnsureApplicationServices + /// private void EnsureApplicationServices() { if (_applicationServices == null) @@ -87,6 +179,9 @@ private void EnsureApplicationServices() } } + /// + /// The EnsureStartup + /// private void EnsureStartup() { if (_startup != null) @@ -97,30 +192,18 @@ private void EnsureStartup() _startup = _hostingServiceProvider.GetRequiredService(); } - private IContainer BuildApplication() - { - try - { - EnsureApplicationServices(); - Action configure = _startup.Configure; - if (_applicationServices == null) - _applicationServices = _builder.Build(); - configure(_applicationServices); - return _applicationServices; - } - catch (Exception ex) - { - Console.Out.WriteLine("应用程序启动异常: " + ex.ToString()); - throw; - } - } - + /// + /// The MapperServices + /// + /// The mapper private void MapperServices(IContainer mapper) { foreach (var mapServices in _mapServicesDelegates) { mapServices(mapper); } - } + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ServiceHostBuilder.cs b/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ServiceHostBuilder.cs index 9e29b0b54..2e6a74264 100644 --- a/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ServiceHostBuilder.cs +++ b/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/ServiceHostBuilder.cs @@ -8,30 +8,67 @@ namespace Surging.Core.ServiceHosting.Internal.Implementation { + /// + /// Defines the + /// public class ServiceHostBuilder : IServiceHostBuilder { - private readonly List> _configureServicesDelegates; - private readonly List> _registerServicesDelegates; + #region 字段 + + /// + /// Defines the _configureDelegates + /// private readonly List> _configureDelegates; + + /// + /// Defines the _configureServicesDelegates + /// + private readonly List> _configureServicesDelegates; + + /// + /// Defines the _mapServicesDelegates + /// private readonly List> _mapServicesDelegates; - private Action _loggingDelegate; + /// + /// Defines the _registerServicesDelegates + /// + private readonly List> _registerServicesDelegates; + + /// + /// Defines the _loggingDelegate + /// + private Action _loggingDelegate; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public ServiceHostBuilder() { _configureServicesDelegates = new List>(); _registerServicesDelegates = new List>(); _configureDelegates = new List>(); _mapServicesDelegates = new List>(); - } + #endregion 构造函数 + + #region 方法 + + /// + /// The Build + /// + /// The public IServiceHost Build() { - var services = BuildCommonServices(); var config = Configure(); - if(_loggingDelegate!=null) - services.AddLogging(_loggingDelegate); + if (_loggingDelegate != null) + services.AddLogging(_loggingDelegate); else services.AddLogging(); services.AddSingleton(typeof(IConfigurationBuilder), config); @@ -40,31 +77,46 @@ public IServiceHost Build() var hostingServiceProvider = services.BuildServiceProvider(); hostingServices.Populate(services); var hostLifetime = hostingServiceProvider.GetService(); - var host = new ServiceHost(hostingServices,hostingServiceProvider, hostLifetime,_mapServicesDelegates); - var container= host.Initialize(); + var host = new ServiceHost(hostingServices, hostingServiceProvider, hostLifetime, _mapServicesDelegates); + var container = host.Initialize(); return host; } - public IServiceHostBuilder MapServices(Action mapper) + /// + /// The Configure + /// + /// The builder + /// The + public IServiceHostBuilder Configure(Action builder) { - if (mapper == null) + if (builder == null) { - throw new ArgumentNullException(nameof(mapper)); + throw new ArgumentNullException(nameof(builder)); } - _mapServicesDelegates.Add(mapper); + _configureDelegates.Add(builder); return this; } - public IServiceHostBuilder RegisterServices(Action builder) + /// + /// The ConfigureLogging + /// + /// The configure + /// The + public IServiceHostBuilder ConfigureLogging(Action configure) { - if (builder == null) + if (configure == null) { - throw new ArgumentNullException(nameof(builder)); + throw new ArgumentNullException(nameof(configure)); } - _registerServicesDelegates.Add(builder); + _loggingDelegate = configure; return this; } - + + /// + /// The ConfigureServices + /// + /// The configureServices + /// The public IServiceHostBuilder ConfigureServices(Action configureServices) { if (configureServices == null) @@ -75,16 +127,40 @@ public IServiceHostBuilder ConfigureServices(Action configur return this; } - public IServiceHostBuilder Configure(Action builder) + /// + /// The MapServices + /// + /// The mapper + /// The + public IServiceHostBuilder MapServices(Action mapper) + { + if (mapper == null) + { + throw new ArgumentNullException(nameof(mapper)); + } + _mapServicesDelegates.Add(mapper); + return this; + } + + /// + /// The RegisterServices + /// + /// The builder + /// The + public IServiceHostBuilder RegisterServices(Action builder) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } - _configureDelegates.Add(builder); - return this; + _registerServicesDelegates.Add(builder); + return this; } + /// + /// The BuildCommonServices + /// + /// The private IServiceCollection BuildCommonServices() { var services = new ServiceCollection(); @@ -95,16 +171,24 @@ private IServiceCollection BuildCommonServices() return services; } + /// + /// The Configure + /// + /// The private IConfigurationBuilder Configure() { - var config = new ConfigurationBuilder().SetBasePath(AppContext.BaseDirectory); + var config = new ConfigurationBuilder().SetBasePath(AppContext.BaseDirectory); foreach (var configure in _configureDelegates) { configure(config); } return config; } - + + /// + /// The RegisterServices + /// + /// The private ContainerBuilder RegisterServices() { var hostingServices = new ContainerBuilder(); @@ -115,14 +199,6 @@ private ContainerBuilder RegisterServices() return hostingServices; } - public IServiceHostBuilder ConfigureLogging(Action configure) - { - if (configure == null) - { - throw new ArgumentNullException(nameof(configure)); - } - _loggingDelegate=configure; - return this; - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/StartupLoader.cs b/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/StartupLoader.cs index 448e4ac9e..6ff361eee 100644 --- a/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/StartupLoader.cs +++ b/src/Surging.Core/Surging.Core.ServiceHosting/Internal/Implementation/StartupLoader.cs @@ -12,45 +12,19 @@ namespace Surging.Core.ServiceHosting.Internal.Implementation { + /// + /// Defines the + /// public class StartupLoader { - public static StartupMethods LoadMethods(IServiceProvider hostingServiceProvider, IConfigurationBuilder config, Type startupType, string environmentName) - { - var configureMethod = FindConfigureDelegate(startupType, environmentName); - var servicesMethod = FindConfigureServicesDelegate(startupType, environmentName); - var configureContainerMethod = FindConfigureContainerDelegate(startupType, environmentName); - - object instance = null; - if (!configureMethod.MethodInfo.IsStatic || (servicesMethod != null && !servicesMethod.MethodInfo.IsStatic)) - { - instance = ActivatorUtilities.CreateInstance(hostingServiceProvider, startupType,config); - } - - var configureServicesCallback = servicesMethod.Build(instance); - var configureContainerCallback = configureContainerMethod.Build(instance); - - Func configureServices = services => - { - IContainer applicationServiceProvider = configureServicesCallback.Invoke(services); - if (applicationServiceProvider != null) - { - return applicationServiceProvider; - } - if (configureContainerMethod.MethodInfo != null) - { - var serviceProviderFactoryType = typeof(IServiceProviderFactory<>).MakeGenericType(configureContainerMethod.GetContainerType()); - var serviceProviderFactory = hostingServiceProvider.GetRequiredService(serviceProviderFactoryType); - var builder = serviceProviderFactoryType.GetMethod(nameof(DefaultServiceProviderFactory.CreateBuilder)).Invoke(serviceProviderFactory, new object[] { services }); - configureContainerCallback.Invoke(builder); - applicationServiceProvider = (IContainer)serviceProviderFactoryType.GetMethod(nameof(DefaultServiceProviderFactory.CreateServiceProvider)).Invoke(serviceProviderFactory, new object[] { builder }); - } - - return applicationServiceProvider; - }; - - return new StartupMethods(instance, configureMethod.Build(instance), configureServices); - } - + #region + + /// + /// The FindStartupType + /// + /// The startupAssemblyName + /// The environmentName + /// The public static Type FindStartupType(string startupAssemblyName, string environmentName) { if (string.IsNullOrEmpty(startupAssemblyName)) @@ -98,18 +72,81 @@ public static Type FindStartupType(string startupAssemblyName, string environmen return type; } - private static ConfigureBuilder FindConfigureDelegate(Type startupType, string environmentName) + /// + /// The LoadMethods + /// + /// The hostingServiceProvider + /// The config + /// The startupType + /// The environmentName + /// The + public static StartupMethods LoadMethods(IServiceProvider hostingServiceProvider, IConfigurationBuilder config, Type startupType, string environmentName) { - var configureMethod = FindMethod(startupType, "Configure{0}", environmentName, typeof(void), required: true); - return new ConfigureBuilder(configureMethod); + var configureMethod = FindConfigureDelegate(startupType, environmentName); + var servicesMethod = FindConfigureServicesDelegate(startupType, environmentName); + var configureContainerMethod = FindConfigureContainerDelegate(startupType, environmentName); + + object instance = null; + if (!configureMethod.MethodInfo.IsStatic || (servicesMethod != null && !servicesMethod.MethodInfo.IsStatic)) + { + instance = ActivatorUtilities.CreateInstance(hostingServiceProvider, startupType, config); + } + + var configureServicesCallback = servicesMethod.Build(instance); + var configureContainerCallback = configureContainerMethod.Build(instance); + + Func configureServices = services => + { + IContainer applicationServiceProvider = configureServicesCallback.Invoke(services); + if (applicationServiceProvider != null) + { + return applicationServiceProvider; + } + if (configureContainerMethod.MethodInfo != null) + { + var serviceProviderFactoryType = typeof(IServiceProviderFactory<>).MakeGenericType(configureContainerMethod.GetContainerType()); + var serviceProviderFactory = hostingServiceProvider.GetRequiredService(serviceProviderFactoryType); + var builder = serviceProviderFactoryType.GetMethod(nameof(DefaultServiceProviderFactory.CreateBuilder)).Invoke(serviceProviderFactory, new object[] { services }); + configureContainerCallback.Invoke(builder); + applicationServiceProvider = (IContainer)serviceProviderFactoryType.GetMethod(nameof(DefaultServiceProviderFactory.CreateServiceProvider)).Invoke(serviceProviderFactory, new object[] { builder }); + } + + return applicationServiceProvider; + }; + + return new StartupMethods(instance, configureMethod.Build(instance), configureServices); } + /// + /// The FindConfigureContainerDelegate + /// + /// The startupType + /// The environmentName + /// The private static ConfigureContainerBuilder FindConfigureContainerDelegate(Type startupType, string environmentName) { var configureMethod = FindMethod(startupType, "Configure{0}Container", environmentName, typeof(void), required: false); return new ConfigureContainerBuilder(configureMethod); } + /// + /// The FindConfigureDelegate + /// + /// The startupType + /// The environmentName + /// The + private static ConfigureBuilder FindConfigureDelegate(Type startupType, string environmentName) + { + var configureMethod = FindMethod(startupType, "Configure{0}", environmentName, typeof(void), required: true); + return new ConfigureBuilder(configureMethod); + } + + /// + /// The FindConfigureServicesDelegate + /// + /// The startupType + /// The environmentName + /// The private static ConfigureServicesBuilder FindConfigureServicesDelegate(Type startupType, string environmentName) { var servicesMethod = FindMethod(startupType, "Configure{0}Services", environmentName, typeof(IContainer), required: false) @@ -117,6 +154,15 @@ private static ConfigureServicesBuilder FindConfigureServicesDelegate(Type start return new ConfigureServicesBuilder(servicesMethod); } + /// + /// The FindMethod + /// + /// The startupType + /// The methodName + /// The environmentName + /// The returnType + /// The required + /// The private static MethodInfo FindMethod(Type startupType, string methodName, string environmentName, Type returnType = null, bool required = true) { var methodNameWithEnv = string.Format(CultureInfo.InvariantCulture, methodName, environmentName); @@ -146,7 +192,6 @@ private static MethodInfo FindMethod(Type startupType, string methodName, string methodNameWithEnv, methodNameWithNoEnv, startupType.FullName)); - } return null; } @@ -163,5 +208,7 @@ private static MethodInfo FindMethod(Type startupType, string methodName, string } return methodInfo; } + + #endregion } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ServiceHosting/ServiceCollectionExtensions.cs b/src/Surging.Core/Surging.Core.ServiceHosting/ServiceCollectionExtensions.cs index efd7b08b1..b541b2676 100644 --- a/src/Surging.Core/Surging.Core.ServiceHosting/ServiceCollectionExtensions.cs +++ b/src/Surging.Core/Surging.Core.ServiceHosting/ServiceCollectionExtensions.cs @@ -5,8 +5,18 @@ namespace Surging.Core.ServiceHosting { + /// + /// Defines the + /// internal static class ServiceCollectionExtensions { + #region 方法 + + /// + /// The Clone + /// + /// The serviceCollection + /// The public static IServiceCollection Clone(this IServiceCollection serviceCollection) { IServiceCollection clone = new ServiceCollection(); @@ -16,5 +26,7 @@ public static IServiceCollection Clone(this IServiceCollection serviceCollection } return clone; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ServiceHosting/ServiceHostBuilderExtensions.cs b/src/Surging.Core/Surging.Core.ServiceHosting/ServiceHostBuilderExtensions.cs index 44a594330..15339686b 100644 --- a/src/Surging.Core/Surging.Core.ServiceHosting/ServiceHostBuilderExtensions.cs +++ b/src/Surging.Core/Surging.Core.ServiceHosting/ServiceHostBuilderExtensions.cs @@ -1,20 +1,56 @@ -using Surging.Core.ServiceHosting.Internal; +using Autofac; +using Autofac.Extensions.DependencyInjection; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Surging.Core.ServiceHosting.Internal; +using Surging.Core.ServiceHosting.Internal.Implementation; using Surging.Core.ServiceHosting.Startup; +using Surging.Core.ServiceHosting.Startup.Implementation; using System; using System.Collections.Generic; using System.Reflection; using System.Text; -using Autofac; -using Surging.Core.ServiceHosting.Startup.Implementation; -using Autofac.Extensions.DependencyInjection; -using Surging.Core.ServiceHosting.Internal.Implementation; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Configuration; namespace Surging.Core.ServiceHosting { - public static class ServiceHostBuilderExtensions + /// + /// Defines the + /// + public static class ServiceHostBuilderExtensions { + #region 方法 + + /// + /// The UseConsoleLifetime + /// + /// The hostBuilder + /// The + public static IServiceHostBuilder UseConsoleLifetime(this IServiceHostBuilder hostBuilder) + { + return hostBuilder.ConfigureServices((collection) => + { + collection.AddSingleton(); + collection.AddSingleton(); + }); + } + + /// + /// The UseStartup + /// + /// + /// The hostBuilder + /// The + public static IServiceHostBuilder UseStartup(this IServiceHostBuilder hostBuilder) where TStartup : class + { + return hostBuilder.UseStartup(typeof(TStartup)); + } + + /// + /// The UseStartup + /// + /// The hostBuilder + /// The startupType + /// The public static IServiceHostBuilder UseStartup(this IServiceHostBuilder hostBuilder, Type startupType) { return hostBuilder @@ -28,26 +64,13 @@ public static IServiceHostBuilder UseStartup(this IServiceHostBuilder hostBuilde { services.AddSingleton(typeof(IStartup), sp => { - var config= sp.GetService(); + var config = sp.GetService(); return new ConventionBasedStartup(StartupLoader.LoadMethods(sp, config, startupType, "")); }); - } }); } - public static IServiceHostBuilder UseStartup(this IServiceHostBuilder hostBuilder) where TStartup : class - { - return hostBuilder.UseStartup(typeof(TStartup)); - } - - public static IServiceHostBuilder UseConsoleLifetime(this IServiceHostBuilder hostBuilder) - { - return hostBuilder.ConfigureServices((collection) => - { - collection.AddSingleton(); - collection.AddSingleton(); - }); - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ServiceHosting/Startup/IStartup.cs b/src/Surging.Core/Surging.Core.ServiceHosting/Startup/IStartup.cs index 34a6ce597..c2307d2f4 100644 --- a/src/Surging.Core/Surging.Core.ServiceHosting/Startup/IStartup.cs +++ b/src/Surging.Core/Surging.Core.ServiceHosting/Startup/IStartup.cs @@ -6,10 +6,30 @@ namespace Surging.Core.ServiceHosting.Startup { - public interface IStartup + #region 接口 + + /// + /// Defines the + /// + public interface IStartup { - IContainer ConfigureServices(ContainerBuilder services); + #region 方法 + /// + /// The Configure + /// + /// The app void Configure(IContainer app); + + /// + /// The ConfigureServices + /// + /// The services + /// The + IContainer ConfigureServices(ContainerBuilder services); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ServiceHosting/Startup/Implementation/ConventionBasedStartup.cs b/src/Surging.Core/Surging.Core.ServiceHosting/Startup/Implementation/ConventionBasedStartup.cs index 8e75956d8..8f6742e97 100644 --- a/src/Surging.Core/Surging.Core.ServiceHosting/Startup/Implementation/ConventionBasedStartup.cs +++ b/src/Surging.Core/Surging.Core.ServiceHosting/Startup/Implementation/ConventionBasedStartup.cs @@ -8,15 +8,39 @@ namespace Surging.Core.ServiceHosting.Startup.Implementation { + /// + /// Defines the + /// public class ConventionBasedStartup : IStartup { + #region 字段 + + /// + /// Defines the _methods + /// private readonly StartupMethods _methods; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The methods public ConventionBasedStartup(StartupMethods methods) { _methods = methods; } + #endregion 构造函数 + + #region 方法 + + /// + /// The Configure + /// + /// The app public void Configure(IContainer app) { try @@ -34,6 +58,11 @@ public void Configure(IContainer app) } } + /// + /// The ConfigureServices + /// + /// The services + /// The public IContainer ConfigureServices(ContainerBuilder services) { try @@ -50,5 +79,7 @@ public IContainer ConfigureServices(ContainerBuilder services) throw; } } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ServiceHosting/Startup/Implementation/DelegateStartup.cs b/src/Surging.Core/Surging.Core.ServiceHosting/Startup/Implementation/DelegateStartup.cs index bfce94f15..29feb3d5f 100644 --- a/src/Surging.Core/Surging.Core.ServiceHosting/Startup/Implementation/DelegateStartup.cs +++ b/src/Surging.Core/Surging.Core.ServiceHosting/Startup/Implementation/DelegateStartup.cs @@ -6,15 +6,41 @@ namespace Surging.Core.ServiceHosting.Startup.Implementation { - public class DelegateStartup : StartupBase + /// + /// Defines the + /// + public class DelegateStartup : StartupBase { + #region 字段 + + /// + /// Defines the _configureApp + /// private Action _configureApp; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The configureApp public DelegateStartup(Action configureApp) : base() { _configureApp = configureApp; } + #endregion 构造函数 + + #region 方法 + + /// + /// The Configure + /// + /// The app public override void Configure(IContainer app) => _configureApp(app); + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ServiceHosting/Startup/Implementation/StartupBase.cs b/src/Surging.Core/Surging.Core.ServiceHosting/Startup/Implementation/StartupBase.cs index 57cd3d14c..0a5468395 100644 --- a/src/Surging.Core/Surging.Core.ServiceHosting/Startup/Implementation/StartupBase.cs +++ b/src/Surging.Core/Surging.Core.ServiceHosting/Startup/Implementation/StartupBase.cs @@ -7,35 +7,77 @@ namespace Surging.Core.ServiceHosting.Startup.Implementation { + /// + /// Defines the + /// public abstract class StartupBase : IStartup { - public abstract void Configure(IContainer app); + #region 方法 - IContainer IStartup.ConfigureServices(ContainerBuilder services) - { - ConfigureServices(services); - return CreateServiceProvider(services); - } + /// + /// The Configure + /// + /// The app + public abstract void Configure(IContainer app); + /// + /// The ConfigureServices + /// + /// The services public virtual void ConfigureServices(ContainerBuilder services) { } + /// + /// The CreateServiceProvider + /// + /// The services + /// The public virtual IContainer CreateServiceProvider(ContainerBuilder services) { return services.Build(); } + + /// + /// The ConfigureServices + /// + /// The services + /// The + IContainer IStartup.ConfigureServices(ContainerBuilder services) + { + ConfigureServices(services); + return CreateServiceProvider(services); + } + + #endregion 方法 } + /// + /// Defines the + /// + /// public abstract class StartupBase : StartupBase { - public override IContainer CreateServiceProvider(ContainerBuilder services) + #region 方法 + + /// + /// The ConfigureContainer + /// + /// The builder + public virtual void ConfigureContainer(TBuilder builder) { - return services.Build(); } - public virtual void ConfigureContainer(TBuilder builder) + /// + /// The CreateServiceProvider + /// + /// The services + /// The + public override IContainer CreateServiceProvider(ContainerBuilder services) { + return services.Build(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.ServiceHosting/Startup/Implementation/StartupMethods.cs b/src/Surging.Core/Surging.Core.ServiceHosting/Startup/Implementation/StartupMethods.cs index 845a1f70f..6a87a3434 100644 --- a/src/Surging.Core/Surging.Core.ServiceHosting/Startup/Implementation/StartupMethods.cs +++ b/src/Surging.Core/Surging.Core.ServiceHosting/Startup/Implementation/StartupMethods.cs @@ -4,8 +4,19 @@ namespace Surging.Core.ServiceHosting.Startup.Implementation { + /// + /// Defines the + /// public class StartupMethods { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The instance + /// The configure + /// The configureServices public StartupMethods(object instance, Action configure, Func configureServices) { Debug.Assert(configure != null); @@ -16,9 +27,25 @@ public StartupMethods(object instance, Action configure, Func ConfigureServicesDelegate { get; } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the ConfigureDelegate + /// public Action ConfigureDelegate { get; } + /// + /// Gets the ConfigureServicesDelegate + /// + public Func ConfigureServicesDelegate { get; } + + /// + /// Gets the StartupInstance + /// + public object StartupInstance { get; } + + #endregion 属性 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Stage/AppConfig.cs b/src/Surging.Core/Surging.Core.Stage/AppConfig.cs index f3305d79a..f0996c3ce 100644 --- a/src/Surging.Core/Surging.Core.Stage/AppConfig.cs +++ b/src/Surging.Core/Surging.Core.Stage/AppConfig.cs @@ -5,8 +5,18 @@ namespace Surging.Core.Stage { + /// + /// Defines the + /// public class AppConfig { + #region 属性 + + /// + /// Gets or sets the Options + /// public static StageOption Options { get; internal set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Stage/AuthorizationFilterAttribute.cs b/src/Surging.Core/Surging.Core.Stage/AuthorizationFilterAttribute.cs index 1e5d3a8cb..8d2a97595 100644 --- a/src/Surging.Core/Surging.Core.Stage/AuthorizationFilterAttribute.cs +++ b/src/Surging.Core/Surging.Core.Stage/AuthorizationFilterAttribute.cs @@ -7,16 +7,24 @@ namespace Surging.Core.Stage { + /// + /// Defines the + /// public class AuthorizationFilterAttribute : IAuthorizationFilter { + #region 方法 + + /// + /// The OnAuthorization + /// + /// The filterContext public void OnAuthorization(AuthorizationFilterContext filterContext) { - if(filterContext.Route.ServiceDescriptor.AuthType() == AuthorizationType.JWT.ToString()) + if (filterContext.Route.ServiceDescriptor.AuthType() == AuthorizationType.JWT.ToString()) { - } - } + + #endregion 方法 } -} - +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Stage/Configurations/StageOption.cs b/src/Surging.Core/Surging.Core.Stage/Configurations/StageOption.cs index 7d900de2b..3160f6036 100644 --- a/src/Surging.Core/Surging.Core.Stage/Configurations/StageOption.cs +++ b/src/Surging.Core/Surging.Core.Stage/Configurations/StageOption.cs @@ -4,18 +4,43 @@ namespace Surging.Core.Stage.Configurations { - public class StageOption + /// + /// Defines the + /// + public class StageOption { - public bool EnableHttps { get; set; } - - public string CertificateFileName { get; set; } + #region 属性 + /// + /// Gets or sets the CertificateFileName + /// + public string CertificateFileName { get; set; } + + /// + /// Gets or sets the CertificateLocation + /// public string CertificateLocation { get; set; } - public string CertificatePassword { get; set; } + /// + /// Gets or sets the CertificatePassword + /// + public string CertificatePassword { get; set; } + + /// + /// Gets or sets a value indicating whether EnableHttps + /// + public bool EnableHttps { get; set; } + + /// + /// Gets or sets the HttpPorts + /// + public string HttpPorts { get; set; } - public string HttpsPort { get; set; } + /// + /// Gets or sets the HttpsPort + /// + public string HttpsPort { get; set; } - public string HttpPorts { get; set; } + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Stage/Internal/IWebServerListener.cs b/src/Surging.Core/Surging.Core.Stage/Internal/IWebServerListener.cs index 5c7826290..b69adf9ab 100644 --- a/src/Surging.Core/Surging.Core.Stage/Internal/IWebServerListener.cs +++ b/src/Surging.Core/Surging.Core.Stage/Internal/IWebServerListener.cs @@ -5,8 +5,23 @@ namespace Surging.Core.Stage.Internal { + #region 接口 + + /// + /// Defines the + /// public interface IWebServerListener { + #region 方法 + + /// + /// The Listen + /// + /// The context void Listen(WebHostContext context); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Stage/Internal/Implementation/WebServerListener.cs b/src/Surging.Core/Surging.Core.Stage/Internal/Implementation/WebServerListener.cs index 0a0a44a6f..b714e9581 100644 --- a/src/Surging.Core/Surging.Core.Stage/Internal/Implementation/WebServerListener.cs +++ b/src/Surging.Core/Surging.Core.Stage/Internal/Implementation/WebServerListener.cs @@ -1,29 +1,82 @@ -using System; -using System.Collections.Generic; -using System.Security.Cryptography.X509Certificates; -using System.Linq; -using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Logging; using Surging.Core.KestrelHttpServer; +using System; +using System.Collections.Generic; using System.IO; -using Microsoft.Extensions.Logging; +using System.Linq; +using System.Security.Cryptography.X509Certificates; namespace Surging.Core.Stage.Internal.Implementation { + /// + /// Defines the + /// public class WebServerListener : IWebServerListener { + #region 字段 + + /// + /// Defines the _logger + /// private readonly ILogger _logger; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The logger public WebServerListener(ILogger logger) { _logger = logger; } - void IWebServerListener.Listen(WebHostContext context) + #endregion 构造函数 + + #region 方法 + + /// + /// The GetPaths + /// + /// The virtualPaths + /// The + private string[] GetPaths(params string[] virtualPaths) { + var result = new List(); + string rootPath = string.IsNullOrEmpty(CPlatform.AppConfig.ServerOptions.RootPath) ? + AppContext.BaseDirectory : CPlatform.AppConfig.ServerOptions.RootPath; + foreach (var virtualPath in virtualPaths) + { + var path = Path.Combine(rootPath, virtualPath); + if (_logger.IsEnabled(LogLevel.Debug)) + _logger.LogDebug($"准备查找路径{path}下的证书。"); + if (Directory.Exists(path)) + { + var dirs = Directory.GetDirectories(path); + result.AddRange(dirs.Select(dir => Path.Combine(rootPath, virtualPath, new DirectoryInfo(dir).Name))); + } + else + { + if (_logger.IsEnabled(LogLevel.Debug)) + _logger.LogDebug($"未找到路径:{path}。"); + } + } + return result.ToArray(); + } + /// + /// The Listen + /// + /// The context + void IWebServerListener.Listen(WebHostContext context) + { var httpsPorts = AppConfig.Options.HttpsPort?.Split(",") ?? new string[] { "443" }; var httpPorts = AppConfig.Options.HttpPorts?.Split(","); if (AppConfig.Options.EnableHttps) - { + { foreach (var httpsPort in httpsPorts) { int.TryParse(httpsPort, out int port); @@ -48,7 +101,6 @@ void IWebServerListener.Listen(WebHostContext context) pfxFile = Path.Combine(path, AppConfig.Options.CertificateFileName); if (File.Exists(pfxFile)) certificate2 = new X509Certificate2(pfxFile, AppConfig.Options.CertificatePassword); - } } } @@ -67,31 +119,8 @@ void IWebServerListener.Listen(WebHostContext context) context.KestrelOptions.Listen(context.Address, port); } } - } - private string[] GetPaths(params string[] virtualPaths) - { - var result = new List(); - string rootPath = string.IsNullOrEmpty(CPlatform.AppConfig.ServerOptions.RootPath) ? - AppContext.BaseDirectory : CPlatform.AppConfig.ServerOptions.RootPath; - foreach (var virtualPath in virtualPaths) - { - var path = Path.Combine(rootPath, virtualPath); - if (_logger.IsEnabled(LogLevel.Debug)) - _logger.LogDebug($"准备查找路径{path}下的证书。"); - if (Directory.Exists(path)) - { - var dirs = Directory.GetDirectories(path); - result.AddRange(dirs.Select(dir => Path.Combine(rootPath,virtualPath, new DirectoryInfo(dir).Name))); - } - else - { - if (_logger.IsEnabled(LogLevel.Debug)) - _logger.LogDebug($"未找到路径:{path}。"); - } - } - return result.ToArray(); - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Stage/StageModule.cs b/src/Surging.Core/Surging.Core.Stage/StageModule.cs index 679c51e4c..1fcfc00af 100644 --- a/src/Surging.Core/Surging.Core.Stage/StageModule.cs +++ b/src/Surging.Core/Surging.Core.Stage/StageModule.cs @@ -15,28 +15,60 @@ namespace Surging.Core.Stage { + /// + /// Defines the + /// public class StageModule : KestrelHttpModule { + #region 字段 + + /// + /// Defines the _listener + /// private IWebServerListener _listener; - public override void Initialize(AppModuleContext context) + + #endregion 字段 + + #region 方法 + + /// + /// The Initialize + /// + /// The context + public override void Initialize(ApplicationInitializationContext context) { - _listener = context.ServiceProvoider.GetInstances(); } - public override void RegisterBuilder(WebHostContext context) + /// + /// The Initialize + /// + /// The context + public override void Initialize(AppModuleContext context) { - _listener.Listen(context); + _listener = context.ServiceProvoider.GetInstances(); } - public override void Initialize(ApplicationInitializationContext context) + /// + /// The RegisterBuilder + /// + /// The context + public override void RegisterBuilder(ConfigurationContext context) { } - public override void RegisterBuilder(ConfigurationContext context) + /// + /// The RegisterBuilder + /// + /// The context + public override void RegisterBuilder(WebHostContext context) { - + _listener.Listen(context); } + /// + /// The RegisterBuilder + /// + /// The builder protected override void RegisterBuilder(ContainerBuilderWrapper builder) { var section = CPlatform.AppConfig.GetSection("Stage"); @@ -47,5 +79,7 @@ protected override void RegisterBuilder(ContainerBuilderWrapper builder) builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/AppConfig.cs b/src/Surging.Core/Surging.Core.Swagger/AppConfig.cs index 6d0c759a7..8fae45684 100644 --- a/src/Surging.Core/Surging.Core.Swagger/AppConfig.cs +++ b/src/Surging.Core/Surging.Core.Swagger/AppConfig.cs @@ -4,16 +4,23 @@ namespace Surging.Core.Swagger { - public class AppConfig + /// + /// Defines the + /// + public class AppConfig { - public static Info SwaggerOptions - { - get; internal set; - } + #region 属性 - public static DocumentConfiguration SwaggerConfig - { - get; internal set; - } + /// + /// Gets or sets the SwaggerConfig + /// + public static DocumentConfiguration SwaggerConfig { get; internal set; } + + /// + /// Gets or sets the SwaggerOptions + /// + public static Info SwaggerOptions { get; internal set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/Internal/DefaultServiceSchemaProvider.cs b/src/Surging.Core/Surging.Core.Swagger/Internal/DefaultServiceSchemaProvider.cs index d62355b3e..b7e1c8d23 100644 --- a/src/Surging.Core/Surging.Core.Swagger/Internal/DefaultServiceSchemaProvider.cs +++ b/src/Surging.Core/Surging.Core.Swagger/Internal/DefaultServiceSchemaProvider.cs @@ -8,15 +8,39 @@ namespace Surging.Core.Swagger.Internal { - public class DefaultServiceSchemaProvider : IServiceSchemaProvider + /// + /// Defines the + /// + public class DefaultServiceSchemaProvider : IServiceSchemaProvider { + #region 字段 + + /// + /// Defines the _serviceEntryProvider + /// private readonly IServiceEntryProvider _serviceEntryProvider; - public DefaultServiceSchemaProvider( IServiceEntryProvider serviceEntryProvider) + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The serviceEntryProvider + public DefaultServiceSchemaProvider(IServiceEntryProvider serviceEntryProvider) { _serviceEntryProvider = serviceEntryProvider; } + #endregion 构造函数 + + #region 方法 + + /// + /// The GetSchemaFilesPath + /// + /// The public IEnumerable GetSchemaFilesPath() { var result = new List(); @@ -32,5 +56,7 @@ public IEnumerable GetSchemaFilesPath() } return result; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/Internal/IServiceSchemaProvider.cs b/src/Surging.Core/Surging.Core.Swagger/Internal/IServiceSchemaProvider.cs index 997c888db..52c9e3d15 100644 --- a/src/Surging.Core/Surging.Core.Swagger/Internal/IServiceSchemaProvider.cs +++ b/src/Surging.Core/Surging.Core.Swagger/Internal/IServiceSchemaProvider.cs @@ -4,8 +4,23 @@ namespace Surging.Core.Swagger.Internal { - public interface IServiceSchemaProvider + #region 接口 + + /// + /// Defines the + /// + public interface IServiceSchemaProvider { + #region 方法 + + /// + /// The GetSchemaFilesPath + /// + /// The IEnumerable GetSchemaFilesPath(); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/Swagger/Application/SwaggerBuilderExtensions.cs b/src/Surging.Core/Surging.Core.Swagger/Swagger/Application/SwaggerBuilderExtensions.cs index 53bd50688..d932d3593 100644 --- a/src/Surging.Core/Surging.Core.Swagger/Swagger/Application/SwaggerBuilderExtensions.cs +++ b/src/Surging.Core/Surging.Core.Swagger/Swagger/Application/SwaggerBuilderExtensions.cs @@ -1,10 +1,21 @@ -using System; -using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Builder; +using System; namespace Surging.Core.Swagger.Builder { + /// + /// Defines the + /// public static class SwaggerBuilderExtensions { + #region 方法 + + /// + /// The UseSwagger + /// + /// The app + /// The setupAction + /// The public static IApplicationBuilder UseSwagger( this IApplicationBuilder app, Action setupAction = null) @@ -25,5 +36,7 @@ public static IApplicationBuilder UseSwagger( return app; } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/Swagger/Application/SwaggerContractResolver.cs b/src/Surging.Core/Surging.Core.Swagger/Swagger/Application/SwaggerContractResolver.cs index 9f129e29e..8afd8f2af 100644 --- a/src/Surging.Core/Surging.Core.Swagger/Swagger/Application/SwaggerContractResolver.cs +++ b/src/Surging.Core/Surging.Core.Swagger/Swagger/Application/SwaggerContractResolver.cs @@ -1,20 +1,46 @@ -using System; -using System.Reflection; -using Newtonsoft.Json; +using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using System; +using System.Reflection; namespace Surging.Core.Swagger { + /// + /// Defines the + /// public class SwaggerContractResolver : DefaultContractResolver { + #region 字段 + + /// + /// Defines the _applicationTypeConverter + /// private readonly JsonConverter _applicationTypeConverter; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The applicationSerializerSettings public SwaggerContractResolver(JsonSerializerSettings applicationSerializerSettings) { NamingStrategy = new CamelCaseNamingStrategy { ProcessDictionaryKeys = false }; _applicationTypeConverter = new ApplicationTypeConverter(applicationSerializerSettings); } + #endregion 构造函数 + + #region 方法 + + /// + /// The CreateProperty + /// + /// The member + /// The memberSerialization + /// The protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { var jsonProperty = base.CreateProperty(member, memberSerialization); @@ -25,26 +51,72 @@ protected override JsonProperty CreateProperty(MemberInfo member, MemberSerializ return jsonProperty; } + #endregion 方法 + + /// + /// Defines the + /// private class ApplicationTypeConverter : JsonConverter { + #region 字段 + + /// + /// Defines the _applicationTypeSerializer + /// private JsonSerializer _applicationTypeSerializer; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The applicationSerializerSettings public ApplicationTypeConverter(JsonSerializerSettings applicationSerializerSettings) { _applicationTypeSerializer = JsonSerializer.Create(applicationSerializerSettings); } - public override bool CanConvert(Type objectType) { return true; } + #endregion 构造函数 + + #region 方法 + /// + /// The CanConvert + /// + /// The objectType + /// The + public override bool CanConvert(Type objectType) + { + return true; + } + + /// + /// The ReadJson + /// + /// The reader + /// The objectType + /// The existingValue + /// The serializer + /// The public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException(); } + /// + /// The WriteJson + /// + /// The writer + /// The value + /// The serializer public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { _applicationTypeSerializer.Serialize(writer, value); } + + #endregion 方法 } } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/Swagger/Application/SwaggerMiddleware.cs b/src/Surging.Core/Surging.Core.Swagger/Swagger/Application/SwaggerMiddleware.cs index 9ecc0af8e..26c6c8048 100644 --- a/src/Surging.Core/Surging.Core.Swagger/Swagger/Application/SwaggerMiddleware.cs +++ b/src/Surging.Core/Surging.Core.Swagger/Swagger/Application/SwaggerMiddleware.cs @@ -1,29 +1,52 @@ -using System.IO; -using System.Text; -using System.Threading.Tasks; -using Microsoft.Extensions.Options; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing.Template; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; using Newtonsoft.Json; +using System.IO; +using System.Text; +using System.Threading.Tasks; namespace Surging.Core.Swagger { + /// + /// Defines the + /// public class SwaggerMiddleware { + #region 字段 + + /// + /// Defines the _next + /// private readonly RequestDelegate _next; - private readonly JsonSerializer _swaggerSerializer; + + /// + /// Defines the _options + /// private readonly SwaggerOptions _options; + + /// + /// Defines the _requestMatcher + /// private readonly TemplateMatcher _requestMatcher; - public SwaggerMiddleware( - RequestDelegate next, - IOptions mvcJsonOptionsAccessor, - IOptions optionsAccessor) - : this(next, mvcJsonOptionsAccessor, optionsAccessor.Value) - { } + /// + /// Defines the _swaggerSerializer + /// + private readonly JsonSerializer _swaggerSerializer; + + #endregion 字段 + + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The next + /// The mvcJsonOptions + /// The options public SwaggerMiddleware( RequestDelegate next, IOptions mvcJsonOptions, @@ -35,6 +58,30 @@ public SwaggerMiddleware( _requestMatcher = new TemplateMatcher(TemplateParser.Parse(options.RouteTemplate), new RouteValueDictionary()); } + /// + /// Initializes a new instance of the class. + /// + /// The next + /// The mvcJsonOptionsAccessor + /// The optionsAccessor + public SwaggerMiddleware( + RequestDelegate next, + IOptions mvcJsonOptionsAccessor, + IOptions optionsAccessor) + : this(next, mvcJsonOptionsAccessor, optionsAccessor.Value) + { + } + + #endregion 构造函数 + + #region 方法 + + /// + /// The Invoke + /// + /// The httpContext + /// The swaggerProvider + /// The public async Task Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) { if (!RequestingSwaggerDocument(httpContext.Request, out string documentName)) @@ -65,23 +112,39 @@ public async Task Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvid } } + /// + /// The RequestingSwaggerDocument + /// + /// The request + /// The documentName + /// The private bool RequestingSwaggerDocument(HttpRequest request, out string documentName) { documentName = null; if (request.Method != "GET") return false; - var routeValues = new RouteValueDictionary(); + var routeValues = new RouteValueDictionary(); if (!_requestMatcher.TryMatch(request.Path, routeValues) || !routeValues.ContainsKey("documentName")) return false; documentName = routeValues["documentName"].ToString(); return true; } + /// + /// The RespondWithNotFound + /// + /// The response private void RespondWithNotFound(HttpResponse response) { response.StatusCode = 404; } + /// + /// The RespondWithSwaggerJson + /// + /// The response + /// The swagger + /// The private async Task RespondWithSwaggerJson(HttpResponse response, SwaggerDocument swagger) { response.StatusCode = 200; @@ -94,5 +157,7 @@ private async Task RespondWithSwaggerJson(HttpResponse response, SwaggerDocument await response.WriteAsync(jsonBuilder.ToString(), new UTF8Encoding(false)); } } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/Swagger/Application/SwaggerOptions.cs b/src/Surging.Core/Surging.Core.Swagger/Swagger/Application/SwaggerOptions.cs index 1f6065028..309b463e8 100644 --- a/src/Surging.Core/Surging.Core.Swagger/Swagger/Application/SwaggerOptions.cs +++ b/src/Surging.Core/Surging.Core.Swagger/Swagger/Application/SwaggerOptions.cs @@ -1,25 +1,41 @@ -using System; +using Microsoft.AspNetCore.Http; +using System; using System.Collections.Generic; -using Microsoft.AspNetCore.Http; namespace Surging.Core.Swagger { + /// + /// Defines the + /// public class SwaggerOptions { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public SwaggerOptions() { PreSerializeFilters = new List>(); } - /// - /// Sets a custom route for the Swagger JSON endpoint(s). Must include the {documentName} parameter - /// - public string RouteTemplate { get; set; } = "swagger/{documentName}/swagger.json"; + #endregion 构造函数 + + #region 属性 /// + /// Gets the PreSerializeFilters /// Actions that can be applied SwaggerDocument's before they're serialized to JSON. /// Useful for setting metadata that's derived from the current request /// public List> PreSerializeFilters { get; private set; } + + /// + /// Gets or sets the RouteTemplate + /// Sets a custom route for the Swagger JSON endpoint(s). Must include the {documentName} parameter + /// + public string RouteTemplate { get; set; } = "swagger/{documentName}/swagger.json"; + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/Swagger/Application/SwaggerSerializerFactory.cs b/src/Surging.Core/Surging.Core.Swagger/Swagger/Application/SwaggerSerializerFactory.cs index 5cc5ff22e..095fcd481 100644 --- a/src/Surging.Core/Surging.Core.Swagger/Swagger/Application/SwaggerSerializerFactory.cs +++ b/src/Surging.Core/Surging.Core.Swagger/Swagger/Application/SwaggerSerializerFactory.cs @@ -4,8 +4,18 @@ namespace Surging.Core.Swagger { + /// + /// Defines the + /// public class SwaggerSerializerFactory { + #region 方法 + + /// + /// The Create + /// + /// The applicationJsonOptions + /// The public static JsonSerializer Create(IOptions applicationJsonOptions) { // TODO: Should this handle case where mvcJsonOptions.Value == null? @@ -16,5 +26,7 @@ public static JsonSerializer Create(IOptions applicationJsonOpti ContractResolver = new SwaggerContractResolver(applicationJsonOptions.Value.SerializerSettings) }; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/Swagger/Model/ApiKeyScheme.cs b/src/Surging.Core/Surging.Core.Swagger/Swagger/Model/ApiKeyScheme.cs index f50f01aaf..16aac9d41 100644 --- a/src/Surging.Core/Surging.Core.Swagger/Swagger/Model/ApiKeyScheme.cs +++ b/src/Surging.Core/Surging.Core.Swagger/Swagger/Model/ApiKeyScheme.cs @@ -1,14 +1,34 @@ namespace Surging.Core.Swagger { + /// + /// Defines the + /// public class ApiKeyScheme : SecurityScheme { - public string Name { get; set; } - - public string In { get; set; } + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// public ApiKeyScheme() { Type = "apiKey"; } + + #endregion 构造函数 + + #region 属性 + + /// + /// Gets or sets the In + /// + public string In { get; set; } + + /// + /// Gets or sets the Name + /// + public string Name { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/Swagger/Model/BasicAuthScheme.cs b/src/Surging.Core/Surging.Core.Swagger/Swagger/Model/BasicAuthScheme.cs index f59338ee4..3f82d1792 100644 --- a/src/Surging.Core/Surging.Core.Swagger/Swagger/Model/BasicAuthScheme.cs +++ b/src/Surging.Core/Surging.Core.Swagger/Swagger/Model/BasicAuthScheme.cs @@ -1,10 +1,20 @@ namespace Surging.Core.Swagger { + /// + /// Defines the + /// public class BasicAuthScheme : SecurityScheme { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public BasicAuthScheme() { Type = "basic"; } + + #endregion 构造函数 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/Swagger/Model/ISwaggerProvider.cs b/src/Surging.Core/Surging.Core.Swagger/Swagger/Model/ISwaggerProvider.cs index 2b1220341..01769fee2 100644 --- a/src/Surging.Core/Surging.Core.Swagger/Swagger/Model/ISwaggerProvider.cs +++ b/src/Surging.Core/Surging.Core.Swagger/Swagger/Model/ISwaggerProvider.cs @@ -2,19 +2,50 @@ namespace Surging.Core.Swagger { + #region 接口 + + /// + /// Defines the + /// public interface ISwaggerProvider { + #region 方法 + + /// + /// The GetSwagger + /// + /// The documentName + /// The host + /// The basePath + /// The schemes + /// The SwaggerDocument GetSwagger( string documentName, string host = null, string basePath = null, string[] schemes = null); + + #endregion 方法 } + #endregion 接口 + + /// + /// Defines the + /// public class UnknownSwaggerDocument : Exception { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The documentName public UnknownSwaggerDocument(string documentName) : base(string.Format("Unknown Swagger document - {0}", documentName)) - {} + { + } + + #endregion 构造函数 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/Swagger/Model/OAuth2Scheme.cs b/src/Surging.Core/Surging.Core.Swagger/Swagger/Model/OAuth2Scheme.cs index c2eb0b55d..06cb6ee40 100644 --- a/src/Surging.Core/Surging.Core.Swagger/Swagger/Model/OAuth2Scheme.cs +++ b/src/Surging.Core/Surging.Core.Swagger/Swagger/Model/OAuth2Scheme.cs @@ -2,19 +2,45 @@ namespace Surging.Core.Swagger { + /// + /// Defines the + /// public class OAuth2Scheme : SecurityScheme { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public OAuth2Scheme() { Type = "oauth2"; } - public string Flow { get; set; } + #endregion 构造函数 + #region 属性 + + /// + /// Gets or sets the AuthorizationUrl + /// public string AuthorizationUrl { get; set; } - public string TokenUrl { get; set; } + /// + /// Gets or sets the Flow + /// + public string Flow { get; set; } + /// + /// Gets or sets the Scopes + /// public IDictionary Scopes { get; set; } + + /// + /// Gets or sets the TokenUrl + /// + public string TokenUrl { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/Swagger/Model/SecurityScheme.cs b/src/Surging.Core/Surging.Core.Swagger/Swagger/Model/SecurityScheme.cs index 6693c8cf1..b187ef5e7 100644 --- a/src/Surging.Core/Surging.Core.Swagger/Swagger/Model/SecurityScheme.cs +++ b/src/Surging.Core/Surging.Core.Swagger/Swagger/Model/SecurityScheme.cs @@ -3,18 +3,41 @@ namespace Surging.Core.Swagger { + /// + /// Defines the + /// public abstract class SecurityScheme { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public SecurityScheme() { Extensions = new Dictionary(); } - public string Type { get; set; } + #endregion 构造函数 + + #region 属性 + /// + /// Gets or sets the Description + /// public string Description { get; set; } + /// + /// Gets the Extensions + /// [JsonExtensionData] public Dictionary Extensions { get; private set; } + + /// + /// Gets or sets the Type + /// + public string Type { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/Swagger/Model/SwaggerDocument.cs b/src/Surging.Core/Surging.Core.Swagger/Swagger/Model/SwaggerDocument.cs index 07618c9e2..b0443e10d 100644 --- a/src/Surging.Core/Surging.Core.Swagger/Swagger/Model/SwaggerDocument.cs +++ b/src/Surging.Core/Surging.Core.Swagger/Swagger/Model/SwaggerDocument.cs @@ -1,393 +1,992 @@ -using System.Collections.Generic; -using Newtonsoft.Json; +using Newtonsoft.Json; +using System.Collections.Generic; namespace Surging.Core.Swagger { - public class SwaggerDocument + #region 接口 + + /// + /// Defines the + /// + public interface IParameter { - public SwaggerDocument() - { - Extensions = new Dictionary(); - } + #region 属性 - public string Swagger + /// + /// Gets or sets the Description + /// + string Description { get; set; } + + /// + /// Gets the Extensions + /// + Dictionary Extensions { get; } + + /// + /// Gets or sets the In + /// + string In { get; set; } + + /// + /// Gets or sets the Name + /// + string Name { get; set; } + + /// + /// Gets or sets a value indicating whether Required + /// + bool Required { get; set; } + + #endregion 属性 + } + + #endregion 接口 + + /// + /// Defines the + /// + public class BodyParameter : IParameter + { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + public BodyParameter() { - get { return "2.0"; } + In = "body"; + Extensions = new Dictionary(); } - public Info Info { get; set; } - - public string Host { get; set; } + #endregion 构造函数 - public string BasePath { get; set; } + #region 属性 - public IList Schemes { get; set; } + /// + /// Gets or sets the Description + /// + public string Description { get; set; } - public IList Consumes { get; set; } + /// + /// Gets the Extensions + /// + [JsonExtensionData] + public Dictionary Extensions { get; private set; } - public IList Produces { get; set; } + /// + /// Gets or sets the In + /// + public string In { get; set; } - public IDictionary Paths { get; set; } + /// + /// Gets or sets the Name + /// + public string Name { get; set; } - public IDictionary Definitions { get; set; } + /// + /// Gets or sets a value indicating whether Required + /// + public bool Required { get; set; } - public IDictionary Parameters { get; set; } + /// + /// Gets or sets the Schema + /// + public Schema Schema { get; set; } - public IDictionary Responses { get; set; } + #endregion 属性 + } - public IDictionary SecurityDefinitions { get; set; } + /// + /// Defines the + /// + public class Contact + { + #region 属性 - public IList>> Security { get; set; } + /// + /// Gets or sets the Email + /// + public string Email { get; set; } - public IList Tags { get; set; } + /// + /// Gets or sets the Name + /// + public string Name { get; set; } - public ExternalDocs ExternalDocs { get; set; } + /// + /// Gets or sets the Url + /// + public string Url { get; set; } - [JsonExtensionData] - public Dictionary Extensions { get; private set; } + #endregion 属性 } + /// + /// Defines the + /// public class DocumentConfiguration { + #region 属性 + + /// + /// Gets or sets the Info + /// public Info Info { get; set; } = null; + /// + /// Gets or sets the Options + /// public DocumentOptions Options { get; set; } = null; + + #endregion 属性 } + /// + /// Defines the + /// public class DocumentOptions { + #region 属性 + + /// + /// Gets or sets a value indicating whether IgnoreFullyQualified + /// public bool IgnoreFullyQualified { get; set; } + /// + /// Gets or sets the IngressName + /// public string IngressName { get; set; } + + #endregion 属性 } + /// + /// Defines the + /// + public class ExternalDocs + { + #region 属性 + + /// + /// Gets or sets the Description + /// + public string Description { get; set; } + + /// + /// Gets or sets the Url + /// + public string Url { get; set; } + #endregion 属性 + } + + /// + /// Defines the + /// + public class Header : PartialSchema + { + #region 属性 + + /// + /// Gets or sets the Description + /// + public string Description { get; set; } + + #endregion 属性 + } + + /// + /// Defines the + /// public class Info { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public Info() { Extensions = new Dictionary(); } - public string Version { get; set; } + #endregion 构造函数 - public string Title { get; set; } + #region 属性 + /// + /// Gets or sets the Contact + /// + public Contact Contact { get; set; } + + /// + /// Gets or sets the Description + /// public string Description { get; set; } + /// + /// Gets the Extensions + /// + [JsonExtensionData] + public Dictionary Extensions { get; private set; } + + /// + /// Gets or sets the License + /// + public License License { get; set; } + + /// + /// Gets or sets the TermsOfService + /// public string TermsOfService { get; set; } - public Contact Contact { get; set; } + /// + /// Gets or sets the Title + /// + public string Title { get; set; } - public License License { get; set; } + /// + /// Gets or sets the Version + /// + public string Version { get; set; } - [JsonExtensionData] - public Dictionary Extensions { get; private set; } + #endregion 属性 } - public class Contact + /// + /// Defines the + /// + public class License { + #region 属性 + + /// + /// Gets or sets the Name + /// public string Name { get; set; } + /// + /// Gets or sets the Url + /// public string Url { get; set; } - public string Email { get; set; } + #endregion 属性 } - public class License + /// + /// Defines the + /// + public class NonBodyParameter : PartialSchema, IParameter { + #region 属性 + + /// + /// Gets or sets the Description + /// + public string Description { get; set; } + + /// + /// Gets or sets the In + /// + public string In { get; set; } + + /// + /// Gets or sets the Name + /// public string Name { get; set; } - public string Url { get; set; } + /// + /// Gets or sets a value indicating whether Required + /// + public bool Required { get; set; } + + #endregion 属性 } - public class PathItem + /// + /// Defines the + /// + public class Operation { - public PathItem() + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + public Operation() { Extensions = new Dictionary(); } - [JsonProperty("$ref")] - public string Ref { get; set; } + #endregion 构造函数 - public Operation Get { get; set; } + #region 属性 - public Operation Put { get; set; } + /// + /// Gets or sets the Consumes + /// + public IList Consumes { get; set; } - public Operation Post { get; set; } + /// + /// Gets or sets the Deprecated + /// + public bool? Deprecated { get; set; } - public Operation Delete { get; set; } + /// + /// Gets or sets the Description + /// + public string Description { get; set; } - public Operation Options { get; set; } + /// + /// Gets the Extensions + /// + [JsonExtensionData] + public Dictionary Extensions { get; private set; } - public Operation Head { get; set; } + /// + /// Gets or sets the ExternalDocs + /// + public ExternalDocs ExternalDocs { get; set; } - public Operation Patch { get; set; } + /// + /// Gets or sets the OperationId + /// + public string OperationId { get; set; } + /// + /// Gets or sets the Parameters + /// public IList Parameters { get; set; } - [JsonExtensionData] - public Dictionary Extensions { get; private set; } + /// + /// Gets or sets the Produces + /// + public IList Produces { get; set; } + + /// + /// Gets or sets the Responses + /// + public IDictionary Responses { get; set; } + + /// + /// Gets or sets the Schemes + /// + public IList Schemes { get; set; } + + /// + /// Gets or sets the Security + /// + public IList>> Security { get; set; } + + /// + /// Gets or sets the Summary + /// + public string Summary { get; set; } + + /// + /// Gets or sets the Tags + /// + public IList Tags { get; set; } + + #endregion 属性 } - public class Operation + /// + /// Defines the + /// + public class PartialSchema { - public Operation() + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + public PartialSchema() { Extensions = new Dictionary(); } - public IList Tags { get; set; } + #endregion 构造函数 - public string Summary { get; set; } + #region 属性 - public string Description { get; set; } + /// + /// Gets or sets the CollectionFormat + /// + public string CollectionFormat { get; set; } - public ExternalDocs ExternalDocs { get; set; } + /// + /// Gets or sets the Default + /// + public object Default { get; set; } - public string OperationId { get; set; } + /// + /// Gets or sets the Enum + /// + public IList Enum { get; set; } - public IList Consumes { get; set; } + /// + /// Gets or sets the ExclusiveMaximum + /// + public bool? ExclusiveMaximum { get; set; } - public IList Produces { get; set; } + /// + /// Gets or sets the ExclusiveMinimum + /// + public bool? ExclusiveMinimum { get; set; } - public IList Parameters { get; set; } + /// + /// Gets the Extensions + /// + [JsonExtensionData] + public Dictionary Extensions { get; private set; } - public IDictionary Responses { get; set; } - - public IList Schemes { get; set; } + /// + /// Gets or sets the Format + /// + public string Format { get; set; } - public bool? Deprecated { get; set; } + /// + /// Gets or sets the Items + /// + public PartialSchema Items { get; set; } - public IList>> Security { get; set; } + /// + /// Gets or sets the Maximum + /// + public double? Maximum { get; set; } - [JsonExtensionData] - public Dictionary Extensions { get; private set; } + /// + /// Gets or sets the MaxItems + /// + public int? MaxItems { get; set; } + + /// + /// Gets or sets the MaxLength + /// + public int? MaxLength { get; set; } + + /// + /// Gets or sets the Minimum + /// + public double? Minimum { get; set; } + + /// + /// Gets or sets the MinItems + /// + public int? MinItems { get; set; } + + /// + /// Gets or sets the MinLength + /// + public int? MinLength { get; set; } + + /// + /// Gets or sets the MultipleOf + /// + public int? MultipleOf { get; set; } + + /// + /// Gets or sets the Pattern + /// + public string Pattern { get; set; } + + /// + /// Gets or sets the Type + /// + public string Type { get; set; } + + /// + /// Gets or sets the UniqueItems + /// + public bool? UniqueItems { get; set; } + + #endregion 属性 } - public class Tag + /// + /// Defines the + /// + public class PathItem { - public Tag() + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + public PathItem() { Extensions = new Dictionary(); } - public string Name { get; set; } + #endregion 构造函数 - public string Description { get; set; } + #region 属性 - public ExternalDocs ExternalDocs { get; set; } + /// + /// Gets or sets the Delete + /// + public Operation Delete { get; set; } + /// + /// Gets the Extensions + /// [JsonExtensionData] public Dictionary Extensions { get; private set; } - } - public class ExternalDocs - { - public string Description { get; set; } + /// + /// Gets or sets the Get + /// + public Operation Get { get; set; } - public string Url { get; set; } - } + /// + /// Gets or sets the Head + /// + public Operation Head { get; set; } + /// + /// Gets or sets the Options + /// + public Operation Options { get; set; } - public interface IParameter - { - string Name { get; set; } + /// + /// Gets or sets the Parameters + /// + public IList Parameters { get; set; } - string In { get; set; } + /// + /// Gets or sets the Patch + /// + public Operation Patch { get; set; } - string Description { get; set; } + /// + /// Gets or sets the Post + /// + public Operation Post { get; set; } - bool Required { get; set; } + /// + /// Gets or sets the Put + /// + public Operation Put { get; set; } - Dictionary Extensions { get; } + /// + /// Gets or sets the Ref + /// + [JsonProperty("$ref")] + public string Ref { get; set; } + + #endregion 属性 } - public class BodyParameter : IParameter + /// + /// Defines the + /// + public class Response { - public BodyParameter() + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + public Response() { - In = "body"; Extensions = new Dictionary(); } - public string Name { get; set; } + #endregion 构造函数 - public string In { get; set; } + #region 属性 + /// + /// Gets or sets the Description + /// public string Description { get; set; } - public bool Required { get; set; } + /// + /// Gets or sets the Examples + /// + public object Examples { get; set; } + /// + /// Gets the Extensions + /// [JsonExtensionData] public Dictionary Extensions { get; private set; } - public Schema Schema { get; set; } - } - - public class NonBodyParameter : PartialSchema, IParameter - { - public string Name { get; set; } - - public string In { get; set; } + /// + /// Gets or sets the Headers + /// + public IDictionary Headers { get; set; } - public string Description { get; set; } + /// + /// Gets or sets the Schema + /// + public Schema Schema { get; set; } - public bool Required { get; set; } + #endregion 属性 } + /// + /// Defines the + /// public class Schema { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public Schema() { Extensions = new Dictionary(); } - [JsonProperty("$ref")] - public string Ref { get; set; } + #endregion 构造函数 - public string Format { get; set; } + #region 属性 - public string Title { get; set; } + /// + /// Gets or sets the AdditionalProperties + /// + public Schema AdditionalProperties { get; set; } - public string Description { get; set; } + /// + /// Gets or sets the AllOf + /// + public IList AllOf { get; set; } + /// + /// Gets or sets the Default + /// public object Default { get; set; } - public int? MultipleOf { get; set; } + /// + /// Gets or sets the Description + /// + public string Description { get; set; } - public double? Maximum { get; set; } + /// + /// Gets or sets the Discriminator + /// + public string Discriminator { get; set; } - public bool? ExclusiveMaximum { get; set; } + /// + /// Gets or sets the Enum + /// + public IList Enum { get; set; } - public double? Minimum { get; set; } + /// + /// Gets or sets the Example + /// + public object Example { get; set; } + /// + /// Gets or sets the ExclusiveMaximum + /// + public bool? ExclusiveMaximum { get; set; } + + /// + /// Gets or sets the ExclusiveMinimum + /// public bool? ExclusiveMinimum { get; set; } - public int? MaxLength { get; set; } + /// + /// Gets the Extensions + /// + [JsonExtensionData] + public Dictionary Extensions { get; private set; } - public int? MinLength { get; set; } + /// + /// Gets or sets the ExternalDocs + /// + public ExternalDocs ExternalDocs { get; set; } - public string Pattern { get; set; } + /// + /// Gets or sets the Format + /// + public string Format { get; set; } - public int? MaxItems { get; set; } + /// + /// Gets or sets the Items + /// + public Schema Items { get; set; } - public int? MinItems { get; set; } + /// + /// Gets or sets the Maximum + /// + public double? Maximum { get; set; } - public bool? UniqueItems { get; set; } + /// + /// Gets or sets the MaxItems + /// + public int? MaxItems { get; set; } + /// + /// Gets or sets the MaxLength + /// + public int? MaxLength { get; set; } + + /// + /// Gets or sets the MaxProperties + /// public int? MaxProperties { get; set; } - public int? MinProperties { get; set; } + /// + /// Gets or sets the Minimum + /// + public double? Minimum { get; set; } - public IList Required { get; set; } + /// + /// Gets or sets the MinItems + /// + public int? MinItems { get; set; } - public IList Enum { get; set; } + /// + /// Gets or sets the MinLength + /// + public int? MinLength { get; set; } - public string Type { get; set; } + /// + /// Gets or sets the MinProperties + /// + public int? MinProperties { get; set; } - public Schema Items { get; set; } + /// + /// Gets or sets the MultipleOf + /// + public int? MultipleOf { get; set; } - public IList AllOf { get; set; } + /// + /// Gets or sets the Pattern + /// + public string Pattern { get; set; } + /// + /// Gets or sets the Properties + /// public IDictionary Properties { get; set; } - public Schema AdditionalProperties { get; set; } + /// + /// Gets or sets the ReadOnly + /// + public bool? ReadOnly { get; set; } - public string Discriminator { get; set; } + /// + /// Gets or sets the Ref + /// + [JsonProperty("$ref")] + public string Ref { get; set; } - public bool? ReadOnly { get; set; } + /// + /// Gets or sets the Required + /// + public IList Required { get; set; } - public Xml Xml { get; set; } + /// + /// Gets or sets the Title + /// + public string Title { get; set; } - public ExternalDocs ExternalDocs { get; set; } + /// + /// Gets or sets the Type + /// + public string Type { get; set; } - public object Example { get; set; } + /// + /// Gets or sets the UniqueItems + /// + public bool? UniqueItems { get; set; } - [JsonExtensionData] - public Dictionary Extensions { get; private set; } + /// + /// Gets or sets the Xml + /// + public Xml Xml { get; set; } + + #endregion 属性 } - public class PartialSchema + /// + /// Defines the + /// + public class SwaggerDocument { - public PartialSchema() + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + public SwaggerDocument() { Extensions = new Dictionary(); } - public string Type { get; set; } + #endregion 构造函数 - public string Format { get; set; } + #region 属性 - public PartialSchema Items { get; set; } + /// + /// Gets or sets the BasePath + /// + public string BasePath { get; set; } - public string CollectionFormat { get; set; } + /// + /// Gets or sets the Consumes + /// + public IList Consumes { get; set; } - public object Default { get; set; } + /// + /// Gets or sets the Definitions + /// + public IDictionary Definitions { get; set; } - public double? Maximum { get; set; } + /// + /// Gets the Extensions + /// + [JsonExtensionData] + public Dictionary Extensions { get; private set; } - public bool? ExclusiveMaximum { get; set; } + /// + /// Gets or sets the ExternalDocs + /// + public ExternalDocs ExternalDocs { get; set; } - public double? Minimum { get; set; } + /// + /// Gets or sets the Host + /// + public string Host { get; set; } - public bool? ExclusiveMinimum { get; set; } + /// + /// Gets or sets the Info + /// + public Info Info { get; set; } - public int? MaxLength { get; set; } + /// + /// Gets or sets the Parameters + /// + public IDictionary Parameters { get; set; } - public int? MinLength { get; set; } + /// + /// Gets or sets the Paths + /// + public IDictionary Paths { get; set; } - public string Pattern { get; set; } + /// + /// Gets or sets the Produces + /// + public IList Produces { get; set; } - public int? MaxItems { get; set; } + /// + /// Gets or sets the Responses + /// + public IDictionary Responses { get; set; } - public int? MinItems { get; set; } + /// + /// Gets or sets the Schemes + /// + public IList Schemes { get; set; } - public bool? UniqueItems { get; set; } + /// + /// Gets or sets the Security + /// + public IList>> Security { get; set; } - public IList Enum { get; set; } + /// + /// Gets or sets the SecurityDefinitions + /// + public IDictionary SecurityDefinitions { get; set; } - public int? MultipleOf { get; set; } + /// + /// Gets the Swagger + /// + public string Swagger + { + get { return "2.0"; } + } - [JsonExtensionData] - public Dictionary Extensions { get; private set; } + /// + /// Gets or sets the Tags + /// + public IList Tags { get; set; } + + #endregion 属性 } - public class Response + /// + /// Defines the + /// + public class Tag { - public Response() + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + public Tag() { Extensions = new Dictionary(); } - public string Description { get; set; } - - public Schema Schema { get; set; } + #endregion 构造函数 - public IDictionary Headers { get; set; } + #region 属性 - public object Examples { get; set; } + /// + /// Gets or sets the Description + /// + public string Description { get; set; } + /// + /// Gets the Extensions + /// [JsonExtensionData] public Dictionary Extensions { get; private set; } - } - public class Header : PartialSchema - { - public string Description { get; set; } + /// + /// Gets or sets the ExternalDocs + /// + public ExternalDocs ExternalDocs { get; set; } + + /// + /// Gets or sets the Name + /// + public string Name { get; set; } + + #endregion 属性 } + /// + /// Defines the + /// public class Xml { + #region 属性 + + /// + /// Gets or sets the Attribute + /// + public bool? Attribute { get; set; } + + /// + /// Gets or sets the Name + /// public string Name { get; set; } + /// + /// Gets or sets the Namespace + /// public string Namespace { get; set; } + /// + /// Gets or sets the Prefix + /// public string Prefix { get; set; } - public bool? Attribute { get; set; } - + /// + /// Gets or sets the Wrapped + /// public bool? Wrapped { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Application/ConfigureSchemaRegistryOptions.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Application/ConfigureSchemaRegistryOptions.cs index b48175a49..c59aa1e94 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Application/ConfigureSchemaRegistryOptions.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Application/ConfigureSchemaRegistryOptions.cs @@ -1,16 +1,37 @@ -using System; -using System.Collections.Generic; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Surging.Core.Swagger; +using System; +using System.Collections.Generic; namespace Surging.Core.SwaggerGen { + /// + /// Defines the + /// internal class ConfigureSchemaRegistryOptions : IConfigureOptions { + #region ֶ + + /// + /// Defines the _serviceProvider + /// private readonly IServiceProvider _serviceProvider; + + /// + /// Defines the _swaggerGenOptions + /// private readonly SwaggerGenOptions _swaggerGenOptions; + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The serviceProvider + /// The swaggerGenOptionsAccessor public ConfigureSchemaRegistryOptions( IServiceProvider serviceProvider, IOptions swaggerGenOptionsAccessor) @@ -19,6 +40,14 @@ public ConfigureSchemaRegistryOptions( _swaggerGenOptions = swaggerGenOptionsAccessor.Value; } + #endregion 캯 + + #region + + /// + /// The Configure + /// + /// The options public void Configure(SchemaRegistryOptions options) { DeepCopy(_swaggerGenOptions.SchemaRegistryOptions, options); @@ -28,6 +57,23 @@ public void Configure(SchemaRegistryOptions options) filterDescriptor => options.SchemaFilters.Add(CreateFilter(filterDescriptor))); } + /// + /// The CreateFilter + /// + /// + /// The filterDescriptor + /// The + private TFilter CreateFilter(FilterDescriptor filterDescriptor) + { + return (TFilter)ActivatorUtilities + .CreateInstance(_serviceProvider, filterDescriptor.Type, filterDescriptor.Arguments); + } + + /// + /// The DeepCopy + /// + /// The source + /// The target private void DeepCopy(SchemaRegistryOptions source, SchemaRegistryOptions target) { target.CustomTypeMappings = new Dictionary>(source.CustomTypeMappings); @@ -40,10 +86,6 @@ private void DeepCopy(SchemaRegistryOptions source, SchemaRegistryOptions target target.SchemaFilters = new List(source.SchemaFilters); } - private TFilter CreateFilter(FilterDescriptor filterDescriptor) - { - return (TFilter)ActivatorUtilities - .CreateInstance(_serviceProvider, filterDescriptor.Type, filterDescriptor.Arguments); - } + #endregion } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Application/ConfigureSwaggerGeneratorOptions.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Application/ConfigureSwaggerGeneratorOptions.cs index 6e1674ac9..358939ac0 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Application/ConfigureSwaggerGeneratorOptions.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Application/ConfigureSwaggerGeneratorOptions.cs @@ -1,16 +1,37 @@ -using System; -using System.Collections.Generic; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Surging.Core.Swagger; +using System; +using System.Collections.Generic; namespace Surging.Core.SwaggerGen { + /// + /// Defines the + /// internal class ConfigureSwaggerGeneratorOptions : IConfigureOptions { + #region ֶ + + /// + /// Defines the _serviceProvider + /// private readonly IServiceProvider _serviceProvider; + + /// + /// Defines the _swaggerGenOptions + /// private readonly SwaggerGenOptions _swaggerGenOptions; + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The serviceProvider + /// The swaggerGenOptionsAccessor public ConfigureSwaggerGeneratorOptions( IServiceProvider serviceProvider, IOptions swaggerGenOptionsAccessor) @@ -19,6 +40,14 @@ public ConfigureSwaggerGeneratorOptions( _swaggerGenOptions = swaggerGenOptionsAccessor.Value; } + #endregion 캯 + + #region + + /// + /// The Configure + /// + /// The options public void Configure(SwaggerGeneratorOptions options) { DeepCopy(_swaggerGenOptions.SwaggerGeneratorOptions, options); @@ -35,6 +64,11 @@ public void Configure(SwaggerGeneratorOptions options) filterDescriptor => options.DocumentFilters.Add(CreateFilter(filterDescriptor))); } + /// + /// The DeepCopy + /// + /// The source + /// The target public void DeepCopy(SwaggerGeneratorOptions source, SwaggerGeneratorOptions target) { target.SwaggerDocs = new Dictionary(source.SwaggerDocs); @@ -53,10 +87,18 @@ public void DeepCopy(SwaggerGeneratorOptions source, SwaggerGeneratorOptions tar target.DocumentFilters = new List(source.DocumentFilters); } + /// + /// The CreateFilter + /// + /// + /// The filterDescriptor + /// The private TFilter CreateFilter(FilterDescriptor filterDescriptor) { return (TFilter)ActivatorUtilities .CreateInstance(_serviceProvider, filterDescriptor.Type, filterDescriptor.Arguments); } + + #endregion } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Application/SwaggerApplicationConvention.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Application/SwaggerApplicationConvention.cs index 9aa4b0664..2cf01acac 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Application/SwaggerApplicationConvention.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Application/SwaggerApplicationConvention.cs @@ -2,11 +2,22 @@ namespace Surging.Core.SwaggerGen { + /// + /// Defines the + /// public class SwaggerApplicationConvention : IApplicationModelConvention { + #region 方法 + + /// + /// The Apply + /// + /// The application public void Apply(ApplicationModel application) { application.ApiExplorer.IsVisible = true; } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Application/SwaggerGenOptions.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Application/SwaggerGenOptions.cs index df1ab8beb..1c7ad5e0a 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Application/SwaggerGenOptions.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Application/SwaggerGenOptions.cs @@ -3,30 +3,67 @@ namespace Surging.Core.SwaggerGen { + /// + /// Defines the + /// + public class FilterDescriptor + { + #region 属性 + + /// + /// Gets or sets the Arguments + /// + public object[] Arguments { get; set; } + + /// + /// Gets or sets the Type + /// + public Type Type { get; set; } + + #endregion 属性 + } + + /// + /// Defines the + /// public class SwaggerGenOptions { - public SwaggerGeneratorOptions SwaggerGeneratorOptions { get; set; } = new SwaggerGeneratorOptions(); + #region 属性 - public SchemaRegistryOptions SchemaRegistryOptions { get; set; } = new SchemaRegistryOptions(); + /// + /// Gets or sets the DocumentFilterDescriptors + /// + public List DocumentFilterDescriptors { get; set; } = new List(); + + /// + /// Gets or sets the OperationFilterDescriptors + /// + public List OperationFilterDescriptors { get; set; } = new List(); // NOTE: Filter instances can be added directly to the options exposed above OR they can be specified in // the following lists. In the latter case, they will be instantiated and added when options are injected // into their target services. This "deferred instantiation" allows the filters to be created from the // DI container, thus supporting contructor injection of services within filters. - + /// + /// Gets or sets the ParameterFilterDescriptors + /// public List ParameterFilterDescriptors { get; set; } = new List(); - public List OperationFilterDescriptors { get; set; } = new List(); - - public List DocumentFilterDescriptors { get; set; } = new List(); - + /// + /// Gets or sets the SchemaFilterDescriptors + /// public List SchemaFilterDescriptors { get; set; } = new List(); - } - public class FilterDescriptor - { - public Type Type { get; set; } + /// + /// Gets or sets the SchemaRegistryOptions + /// + public SchemaRegistryOptions SchemaRegistryOptions { get; set; } = new SchemaRegistryOptions(); - public object[] Arguments { get; set; } + /// + /// Gets or sets the SwaggerGeneratorOptions + /// + public SwaggerGeneratorOptions SwaggerGeneratorOptions { get; set; } = new SwaggerGeneratorOptions(); + + #endregion 属性 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Application/SwaggerGenOptionsExtensions.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Application/SwaggerGenOptionsExtensions.cs index 441b3195b..796739be1 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Application/SwaggerGenOptionsExtensions.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Application/SwaggerGenOptionsExtensions.cs @@ -1,38 +1,104 @@ +using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Surging.Core.CPlatform.Runtime.Server; +using Surging.Core.Swagger; +using Surging.Core.SwaggerGen; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Xml.XPath; -using Microsoft.AspNetCore.Mvc.ApiExplorer; -using Surging.Core.CPlatform.Runtime.Server; -using Surging.Core.Swagger; -using Surging.Core.SwaggerGen; namespace Microsoft.Extensions.DependencyInjection { + /// + /// Defines the + /// public static class SwaggerGenOptionsExtensions { + #region + /// - /// Define one or more documents to be created by the Swagger generator + /// Add one or more "securityDefinitions", describing how your API is protected, to the generated Swagger /// /// - /// A URI-friendly name that uniquely identifies the document - /// Global metadata to be included in the Swagger output - public static void SwaggerDoc( + /// A unique name for the scheme, as per the Swagger spec. + /// The securityScheme + public static void AddSecurityDefinition( this SwaggerGenOptions swaggerGenOptions, string name, - Info info) + SecurityScheme securityScheme) { - swaggerGenOptions.SwaggerGeneratorOptions.SwaggerDocs.Add(name, info); + swaggerGenOptions.SwaggerGeneratorOptions.SecurityDefinitions.Add(name, securityScheme); + } + + /// + /// Adds a global security requirement + /// + /// + /// The requirement + public static void AddSecurityRequirement( + this SwaggerGenOptions swaggerGenOptions, + IDictionary> requirement) + { + swaggerGenOptions.SwaggerGeneratorOptions.SecurityRequirements.Add(requirement); + } + + /// + /// Provide a custom strategy for assigning "operationId" to operations + /// + /// The swaggerGenOptions + /// The operationIdSelector + public static void CustomOperationIds( + this SwaggerGenOptions swaggerGenOptions, + Func operationIdSelector) + { + swaggerGenOptions.SwaggerGeneratorOptions.OperationIdSelector = operationIdSelector; + } + + /// + /// Provide a custom strategy for generating the unique Id's that are used to reference object Schema's + /// + /// + /// The schemaIdSelector + public static void CustomSchemaIds( + this SwaggerGenOptions swaggerGenOptions, + Func schemaIdSelector) + { + swaggerGenOptions.SchemaRegistryOptions.SchemaIdSelector = schemaIdSelector; + } + + /// + /// Use the enum names, as opposed to their integer values, when describing enum types + /// + /// The swaggerGenOptions + public static void DescribeAllEnumsAsStrings(this SwaggerGenOptions swaggerGenOptions) + { + swaggerGenOptions.SchemaRegistryOptions.DescribeAllEnumsAsStrings = true; + } + + /// + /// Describe all parameters, regardless of how they appear in code, in camelCase + /// + /// The swaggerGenOptions + public static void DescribeAllParametersInCamelCase(this SwaggerGenOptions swaggerGenOptions) + { + swaggerGenOptions.SwaggerGeneratorOptions.DescribeAllParametersInCamelCase = true; + } + + /// + /// If applicable, describe all enum names, regardless of how they appear in code, in camelCase. + /// + /// The swaggerGenOptions + public static void DescribeStringEnumsInCamelCase(this SwaggerGenOptions swaggerGenOptions) + { + swaggerGenOptions.SchemaRegistryOptions.DescribeStringEnumsInCamelCase = true; } /// /// Provide a custom strategy for selecting actions. /// /// - /// - /// A lambda that returns true/false based on document name and ApiDescription - /// + /// The predicate public static void DocInclusionPredicate( this SwaggerGenOptions swaggerGenOptions, Func predicate) @@ -40,6 +106,11 @@ public static void DocInclusionPredicate( swaggerGenOptions.SwaggerGeneratorOptions.DocInclusionPredicate = predicate; } + /// + /// The DocInclusionPredicateV2 + /// + /// The swaggerGenOptions + /// The predicate public static void DocInclusionPredicateV2( this SwaggerGenOptions swaggerGenOptions, Func predicate) @@ -47,10 +118,32 @@ public static void DocInclusionPredicateV2( swaggerGenOptions.SwaggerGeneratorOptions.DocInclusionPredicateV2 = predicate; } + /// + /// Extend the Swagger Generator with "filters" that can modify SwaggerDocuments after they're initially generated + /// + /// A type that derives from IDocumentFilter + /// + /// Optionally inject parameters through filter constructors + public static void DocumentFilter( + this SwaggerGenOptions swaggerGenOptions, + params object[] arguments) + where TFilter : IDocumentFilter + { + swaggerGenOptions.DocumentFilterDescriptors.Add(new FilterDescriptor + { + Type = typeof(TFilter), + Arguments = arguments + }); + } + + /// + /// The GenerateSwaggerDoc + /// + /// The swaggerGenOptions + /// The entries public static void GenerateSwaggerDoc( this SwaggerGenOptions swaggerGenOptions, IEnumerable entries) { - var result = new Dictionary(); var assemblies = entries.Select(p => p.Type.Assembly).Distinct(); foreach (var assembly in assemblies) @@ -74,115 +167,81 @@ public static void GenerateSwaggerDoc( Title = title.Title, Version = version.Version, Description = des?.Description, - }); } } /// - /// Ignore any actions that are decorated with the ObsoleteAttribute + /// The IgnoreFullyQualified /// - public static void IgnoreObsoleteActions(this SwaggerGenOptions swaggerGenOptions) - { - swaggerGenOptions.SwaggerGeneratorOptions.IgnoreObsoleteActions = true; - } - - /// - /// Merge actions that have conflicting HTTP methods and paths (must be unique for Swagger 2.0) - /// - /// - /// - public static void ResolveConflictingActions( - this SwaggerGenOptions swaggerGenOptions, - Func, ApiDescription> resolver) - { - swaggerGenOptions.SwaggerGeneratorOptions.ConflictingActionsResolver = resolver; - } - - /// - /// Provide a custom strategy for assigning "operationId" to operations - /// - public static void CustomOperationIds( - this SwaggerGenOptions swaggerGenOptions, - Func operationIdSelector) + /// The swaggerGenOptions + public static void IgnoreFullyQualified(this SwaggerGenOptions swaggerGenOptions) { - swaggerGenOptions.SwaggerGeneratorOptions.OperationIdSelector = operationIdSelector; + swaggerGenOptions.SchemaRegistryOptions.IgnoreFullyQualified = true; } /// - /// Provide a custom strategy for assigning a default "tag" to operations + /// Ignore any actions that are decorated with the ObsoleteAttribute /// - /// - /// - [Obsolete("Deprecated: Use the overload that accepts a Func that returns a list of tags")] - public static void TagActionsBy( - this SwaggerGenOptions swaggerGenOptions, - Func tagSelector) + /// The swaggerGenOptions + public static void IgnoreObsoleteActions(this SwaggerGenOptions swaggerGenOptions) { - swaggerGenOptions.SwaggerGeneratorOptions.TagsSelector = (apiDesc) => new[] { tagSelector(apiDesc) }; + swaggerGenOptions.SwaggerGeneratorOptions.IgnoreObsoleteActions = true; } /// - /// Provide a custom strategy for assigning "tags" to actions + /// Ignore any properties that are decorated with the ObsoleteAttribute /// - /// - /// - public static void TagActionsBy( - this SwaggerGenOptions swaggerGenOptions, - Func> tagsSelector) + /// The swaggerGenOptions + public static void IgnoreObsoleteProperties(this SwaggerGenOptions swaggerGenOptions) { - swaggerGenOptions.SwaggerGeneratorOptions.TagsSelector = tagsSelector; + swaggerGenOptions.SchemaRegistryOptions.IgnoreObsoleteProperties = true; } /// - /// Provide a custom strategy for sorting actions before they're transformed into the Swagger format + /// Inject human-friendly descriptions for Operations, Parameters and Schemas based on XML Comment files /// /// - /// - public static void OrderActionsBy( + /// A factory method that returns XML Comments as an XPathDocument + /// The includeControllerXmlComments + public static void IncludeXmlComments( this SwaggerGenOptions swaggerGenOptions, - Func sortKeySelector) + Func xmlDocFactory, + bool includeControllerXmlComments = false) { - swaggerGenOptions.SwaggerGeneratorOptions.SortKeySelector = sortKeySelector; - } + var xmlDoc = xmlDocFactory(); + swaggerGenOptions.OperationFilter(xmlDoc); + swaggerGenOptions.SchemaFilter(xmlDoc); - /// - /// Describe all parameters, regardless of how they appear in code, in camelCase - /// - public static void DescribeAllParametersInCamelCase(this SwaggerGenOptions swaggerGenOptions) - { - swaggerGenOptions.SwaggerGeneratorOptions.DescribeAllParametersInCamelCase = true; + if (includeControllerXmlComments) + swaggerGenOptions.DocumentFilter(xmlDoc); } /// - /// Add one or more "securityDefinitions", describing how your API is protected, to the generated Swagger + /// Inject human-friendly descriptions for Operations, Parameters and Schemas based on XML Comment files /// /// - /// A unique name for the scheme, as per the Swagger spec. - /// - /// A description of the scheme - can be an instance of BasicAuthScheme, ApiKeyScheme or OAuth2Scheme - /// - public static void AddSecurityDefinition( + /// An abolsute path to the file that contains XML Comments + /// The includeControllerXmlComments + public static void IncludeXmlComments( this SwaggerGenOptions swaggerGenOptions, - string name, - SecurityScheme securityScheme) + string filePath, + bool includeControllerXmlComments = false) { - swaggerGenOptions.SwaggerGeneratorOptions.SecurityDefinitions.Add(name, securityScheme); + swaggerGenOptions.IncludeXmlComments(() => new XPathDocument(filePath), includeControllerXmlComments); } /// - /// Adds a global security requirement + /// Provide a custom mapping, for a given type, to the Swagger-flavored JSONSchema /// + /// System type /// - /// - /// A dictionary of required schemes (logical AND). Keys must correspond to schemes defined through AddSecurityDefinition - /// If the scheme is of type "oauth2", then the value is a list of scopes, otherwise it MUST be an empty array - /// - public static void AddSecurityRequirement( + /// A factory method that generates Schema's for the provided type + public static void MapType( this SwaggerGenOptions swaggerGenOptions, - IDictionary> requirement) + Func schemaFactory) { - swaggerGenOptions.SwaggerGeneratorOptions.SecurityRequirements.Add(requirement); + swaggerGenOptions.MapType(typeof(T), schemaFactory); } /// @@ -200,67 +259,63 @@ public static void MapType( } /// - /// Provide a custom mapping, for a given type, to the Swagger-flavored JSONSchema + /// Extend the Swagger Generator with "filters" that can modify Operations after they're initially generated /// - /// System type + /// A type that derives from IOperationFilter /// - /// A factory method that generates Schema's for the provided type - public static void MapType( + /// Optionally inject parameters through filter constructors + public static void OperationFilter( this SwaggerGenOptions swaggerGenOptions, - Func schemaFactory) - { - swaggerGenOptions.MapType(typeof(T), schemaFactory); - } - - /// - /// Use the enum names, as opposed to their integer values, when describing enum types - /// - public static void DescribeAllEnumsAsStrings(this SwaggerGenOptions swaggerGenOptions) - { - swaggerGenOptions.SchemaRegistryOptions.DescribeAllEnumsAsStrings = true; - } - - /// - /// If applicable, describe all enum names, regardless of how they appear in code, in camelCase. - /// - public static void DescribeStringEnumsInCamelCase(this SwaggerGenOptions swaggerGenOptions) + params object[] arguments) + where TFilter : IOperationFilter { - swaggerGenOptions.SchemaRegistryOptions.DescribeStringEnumsInCamelCase = true; + swaggerGenOptions.OperationFilterDescriptors.Add(new FilterDescriptor + { + Type = typeof(TFilter), + Arguments = arguments + }); } /// - /// Use referenced definitions for enum types within body parameter and response schemas + /// Provide a custom strategy for sorting actions before they're transformed into the Swagger format /// - public static void UseReferencedDefinitionsForEnums(this SwaggerGenOptions swaggerGenOptions) + /// + /// + public static void OrderActionsBy( + this SwaggerGenOptions swaggerGenOptions, + Func sortKeySelector) { - swaggerGenOptions.SchemaRegistryOptions.UseReferencedDefinitionsForEnums = true; + swaggerGenOptions.SwaggerGeneratorOptions.SortKeySelector = sortKeySelector; } /// - /// Provide a custom strategy for generating the unique Id's that are used to reference object Schema's + /// Extend the Swagger Generator with "filters" that can modify Parameters after they're initially generated /// + /// A type that derives from IParameterFilter /// - /// - /// A lambda that returns a unique identifier for the provided system type - /// - public static void CustomSchemaIds( + /// Optionally inject parameters through filter constructors + public static void ParameterFilter( this SwaggerGenOptions swaggerGenOptions, - Func schemaIdSelector) + params object[] arguments) + where TFilter : IParameterFilter { - swaggerGenOptions.SchemaRegistryOptions.SchemaIdSelector = schemaIdSelector; + swaggerGenOptions.ParameterFilterDescriptors.Add(new FilterDescriptor + { + Type = typeof(TFilter), + Arguments = arguments + }); } /// - /// Ignore any properties that are decorated with the ObsoleteAttribute + /// Merge actions that have conflicting HTTP methods and paths (must be unique for Swagger 2.0) /// - public static void IgnoreObsoleteProperties(this SwaggerGenOptions swaggerGenOptions) - { - swaggerGenOptions.SchemaRegistryOptions.IgnoreObsoleteProperties = true; - } - - public static void IgnoreFullyQualified(this SwaggerGenOptions swaggerGenOptions) + /// + /// + public static void ResolveConflictingActions( + this SwaggerGenOptions swaggerGenOptions, + Func, ApiDescription> resolver) { - swaggerGenOptions.SchemaRegistryOptions.IgnoreFullyQualified = true; + swaggerGenOptions.SwaggerGeneratorOptions.ConflictingActionsResolver = resolver; } /// @@ -282,96 +337,53 @@ public static void SchemaFilter( } /// - /// Extend the Swagger Generator with "filters" that can modify Parameters after they're initially generated + /// Define one or more documents to be created by the Swagger generator /// - /// A type that derives from IParameterFilter /// - /// Optionally inject parameters through filter constructors - public static void ParameterFilter( + /// A URI-friendly name that uniquely identifies the document + /// Global metadata to be included in the Swagger output + public static void SwaggerDoc( this SwaggerGenOptions swaggerGenOptions, - params object[] arguments) - where TFilter : IParameterFilter + string name, + Info info) { - swaggerGenOptions.ParameterFilterDescriptors.Add(new FilterDescriptor - { - Type = typeof(TFilter), - Arguments = arguments - }); + swaggerGenOptions.SwaggerGeneratorOptions.SwaggerDocs.Add(name, info); } /// - /// Extend the Swagger Generator with "filters" that can modify Operations after they're initially generated + /// Provide a custom strategy for assigning "tags" to actions /// - /// A type that derives from IOperationFilter /// - /// Optionally inject parameters through filter constructors - public static void OperationFilter( + /// + public static void TagActionsBy( this SwaggerGenOptions swaggerGenOptions, - params object[] arguments) - where TFilter : IOperationFilter + Func> tagsSelector) { - swaggerGenOptions.OperationFilterDescriptors.Add(new FilterDescriptor - { - Type = typeof(TFilter), - Arguments = arguments - }); + swaggerGenOptions.SwaggerGeneratorOptions.TagsSelector = tagsSelector; } /// - /// Extend the Swagger Generator with "filters" that can modify SwaggerDocuments after they're initially generated + /// Provide a custom strategy for assigning a default "tag" to operations /// - /// A type that derives from IDocumentFilter /// - /// Optionally inject parameters through filter constructors - public static void DocumentFilter( + /// + [Obsolete("Deprecated: Use the overload that accepts a Func that returns a list of tags")] + public static void TagActionsBy( this SwaggerGenOptions swaggerGenOptions, - params object[] arguments) - where TFilter : IDocumentFilter + Func tagSelector) { - swaggerGenOptions.DocumentFilterDescriptors.Add(new FilterDescriptor - { - Type = typeof(TFilter), - Arguments = arguments - }); + swaggerGenOptions.SwaggerGeneratorOptions.TagsSelector = (apiDesc) => new[] { tagSelector(apiDesc) }; } /// - /// Inject human-friendly descriptions for Operations, Parameters and Schemas based on XML Comment files + /// Use referenced definitions for enum types within body parameter and response schemas /// - /// - /// A factory method that returns XML Comments as an XPathDocument - /// - /// Flag to indicate if controller XML comments (i.e. summary) should be used to assign Tag descriptions. - /// Don't set this flag if you're customizing the default tag for operations via TagActionsBy. - /// - public static void IncludeXmlComments( - this SwaggerGenOptions swaggerGenOptions, - Func xmlDocFactory, - bool includeControllerXmlComments = false) + /// The swaggerGenOptions + public static void UseReferencedDefinitionsForEnums(this SwaggerGenOptions swaggerGenOptions) { - var xmlDoc = xmlDocFactory(); - swaggerGenOptions.OperationFilter(xmlDoc); - swaggerGenOptions.SchemaFilter(xmlDoc); - - if (includeControllerXmlComments) - swaggerGenOptions.DocumentFilter(xmlDoc); + swaggerGenOptions.SchemaRegistryOptions.UseReferencedDefinitionsForEnums = true; } - /// - /// Inject human-friendly descriptions for Operations, Parameters and Schemas based on XML Comment files - /// - /// - /// An abolsute path to the file that contains XML Comments - /// - /// Flag to indicate if controller XML comments (i.e. summary) should be used to assign Tag descriptions. - /// Don't set this flag if you're customizing the default tag for operations via TagActionsBy. - /// - public static void IncludeXmlComments( - this SwaggerGenOptions swaggerGenOptions, - string filePath, - bool includeControllerXmlComments = false) - { - swaggerGenOptions.IncludeXmlComments(() => new XPathDocument(filePath), includeControllerXmlComments); - } + #endregion } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Application/SwaggerGenServiceCollectionExtensions.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Application/SwaggerGenServiceCollectionExtensions.cs index d09527145..fc340d5d9 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Application/SwaggerGenServiceCollectionExtensions.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Application/SwaggerGenServiceCollectionExtensions.cs @@ -1,13 +1,24 @@ -using System; -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Surging.Core.Swagger; using Surging.Core.SwaggerGen; +using System; namespace Microsoft.Extensions.DependencyInjection { + /// + /// Defines the + /// public static class SwaggerGenServiceCollectionExtensions { + #region 方法 + + /// + /// The AddSwaggerGen + /// + /// The services + /// The setupAction + /// The public static IServiceCollection AddSwaggerGen( this IServiceCollection services, Action setupAction = null) @@ -30,11 +41,18 @@ public static IServiceCollection AddSwaggerGen( return services; } + /// + /// The ConfigureSwaggerGen + /// + /// The services + /// The setupAction public static void ConfigureSwaggerGen( this IServiceCollection services, Action setupAction) { services.Configure(setupAction); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/ApiDescriptionExtensions.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/ApiDescriptionExtensions.cs index f7b8403fb..58602a937 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/ApiDescriptionExtensions.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/ApiDescriptionExtensions.cs @@ -7,26 +7,47 @@ namespace Surging.Core.SwaggerGen { - public static class ApiDescriptionExtensions + /// + /// Defines the + /// + public static class ApiDescriptionExtensions { + #region 方法 + + /// + /// The ActionAttributes + /// + /// The apiDescription + /// The [Obsolete("Deprecated: Use TryGetMethodInfo")] - public static IEnumerable ControllerAttributes(this ApiDescription apiDescription) + public static IEnumerable ActionAttributes(this ApiDescription apiDescription) { var controllerActionDescriptor = apiDescription.ActionDescriptor as ControllerActionDescriptor; return (controllerActionDescriptor == null) ? Enumerable.Empty() - : controllerActionDescriptor.ControllerTypeInfo.GetCustomAttributes(true); + : controllerActionDescriptor.MethodInfo.GetCustomAttributes(true); } + /// + /// The ControllerAttributes + /// + /// The apiDescription + /// The [Obsolete("Deprecated: Use TryGetMethodInfo")] - public static IEnumerable ActionAttributes(this ApiDescription apiDescription) + public static IEnumerable ControllerAttributes(this ApiDescription apiDescription) { var controllerActionDescriptor = apiDescription.ActionDescriptor as ControllerActionDescriptor; return (controllerActionDescriptor == null) ? Enumerable.Empty() - : controllerActionDescriptor.MethodInfo.GetCustomAttributes(true); + : controllerActionDescriptor.ControllerTypeInfo.GetCustomAttributes(true); } + /// + /// The TryGetMethodInfo + /// + /// The apiDescription + /// The methodInfo + /// The public static bool TryGetMethodInfo(this ApiDescription apiDescription, out MethodInfo methodInfo) { var controllerActionDescriptor = apiDescription.ActionDescriptor as ControllerActionDescriptor; @@ -36,11 +57,11 @@ public static bool TryGetMethodInfo(this ApiDescription apiDescription, out Meth return (methodInfo != null); } - internal static string RelativePathSansQueryString(this ApiDescription apiDescription) - { - return apiDescription.RelativePath.Split('?').First(); - } - + /// + /// The IsObsolete + /// + /// The apiDescription + /// The internal static bool IsObsolete(this ApiDescription apiDescription) { if (!apiDescription.TryGetMethodInfo(out MethodInfo methodInfo)) @@ -50,5 +71,17 @@ internal static bool IsObsolete(this ApiDescription apiDescription) .Union(methodInfo.DeclaringType.GetTypeInfo().GetCustomAttributes(true)) .Any(attr => attr.GetType() == typeof(ObsoleteAttribute)); } + + /// + /// The RelativePathSansQueryString + /// + /// The apiDescription + /// The + internal static string RelativePathSansQueryString(this ApiDescription apiDescription) + { + return apiDescription.RelativePath.Split('?').First(); + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/ApiParameterDescriptionExtensions.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/ApiParameterDescriptionExtensions.cs index e99cca2e2..de76927ba 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/ApiParameterDescriptionExtensions.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/ApiParameterDescriptionExtensions.cs @@ -5,9 +5,21 @@ namespace Surging.Core.SwaggerGen { + /// + /// Defines the + /// public static class ApiParameterDescriptionExtensions { - internal static bool TryGetParameterInfo( + #region 方法 + + /// + /// The TryGetParameterInfo + /// + /// The apiParameterDescription + /// The apiDescription + /// The parameterInfo + /// The + internal static bool TryGetParameterInfo( this ApiParameterDescription apiParameterDescription, ApiDescription apiDescription, out ParameterInfo parameterInfo) @@ -25,6 +37,12 @@ internal static bool TryGetParameterInfo( return (parameterInfo != null); } + /// + /// The TryGetPropertyInfo + /// + /// The apiParameterDescription + /// The propertyInfo + /// The internal static bool TryGetPropertyInfo( this ApiParameterDescription apiParameterDescription, out PropertyInfo propertyInfo) @@ -37,5 +55,7 @@ internal static bool TryGetPropertyInfo( return (propertyInfo != null); } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/ApiResponseTypeExtensions.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/ApiResponseTypeExtensions.cs index 3bdf54071..200a1d923 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/ApiResponseTypeExtensions.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/ApiResponseTypeExtensions.cs @@ -4,8 +4,18 @@ namespace Surging.Core.SwaggerGen { + /// + /// Defines the + /// public static class ApiResponseTypeExtensions { + #region 方法 + + /// + /// The IsDefaultResponse + /// + /// The apiResponseType + /// The internal static bool IsDefaultResponse(this ApiResponseType apiResponseType) { var propertyInfo = apiResponseType.GetType().GetProperty("IsDefaultResponse"); @@ -17,5 +27,7 @@ internal static bool IsDefaultResponse(this ApiResponseType apiResponseType) // ApiExplorer < 2.1.0 does not support default response. return false; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/IDocumentFilter.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/IDocumentFilter.cs index 6c3305aca..3ab079605 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/IDocumentFilter.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/IDocumentFilter.cs @@ -1,18 +1,44 @@ - -using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.AspNetCore.Mvc.ApiExplorer; using Surging.Core.Swagger; using System; using System.Collections.Generic; namespace Surging.Core.SwaggerGen { + #region 接口 + + /// + /// Defines the + /// public interface IDocumentFilter { + #region 方法 + + /// + /// The Apply + /// + /// The swaggerDoc + /// The context void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context); + + #endregion 方法 } + #endregion 接口 + + /// + /// Defines the + /// public class DocumentFilterContext { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The apiDescriptionsGroups + /// The apiDescriptions + /// The schemaRegistry public DocumentFilterContext( ApiDescriptionGroupCollection apiDescriptionsGroups, IEnumerable apiDescriptions, @@ -23,11 +49,26 @@ public DocumentFilterContext( SchemaRegistry = schemaRegistry; } - [Obsolete("Deprecated: Use ApiDescriptions")] - public ApiDescriptionGroupCollection ApiDescriptionsGroups { get; private set; } + #endregion 构造函数 + + #region 属性 + /// + /// Gets the ApiDescriptions + /// public IEnumerable ApiDescriptions { get; private set; } + /// + /// Gets the ApiDescriptionsGroups + /// + [Obsolete("Deprecated: Use ApiDescriptions")] + public ApiDescriptionGroupCollection ApiDescriptionsGroups { get; private set; } + + /// + /// Gets the SchemaRegistry + /// public ISchemaRegistry SchemaRegistry { get; private set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/IOperationFilter.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/IOperationFilter.cs index f01368e9f..3e6aa720f 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/IOperationFilter.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/IOperationFilter.cs @@ -1,29 +1,62 @@ -using System.Reflection; -using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.AspNetCore.Mvc.ApiExplorer; using Surging.Core.CPlatform.Runtime.Server; using Surging.Core.Swagger; +using System.Reflection; namespace Surging.Core.SwaggerGen { + #region 接口 + + /// + /// Defines the + /// public interface IOperationFilter { + #region 方法 + + /// + /// The Apply + /// + /// The operation + /// The context void Apply(Operation operation, OperationFilterContext context); + + #endregion 方法 } + #endregion 接口 + + /// + /// Defines the + /// public class OperationFilterContext { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The apiDescription + /// The schemaRegistry + /// The methodInfo public OperationFilterContext( ApiDescription apiDescription, ISchemaRegistry schemaRegistry, - MethodInfo methodInfo):this(apiDescription,schemaRegistry,methodInfo,null) + MethodInfo methodInfo) : this(apiDescription, schemaRegistry, methodInfo, null) { - } + /// + /// Initializes a new instance of the class. + /// + /// The apiDescription + /// The schemaRegistry + /// The methodInfo + /// The serviceEntry public OperationFilterContext( ApiDescription apiDescription, ISchemaRegistry schemaRegistry, - MethodInfo methodInfo,ServiceEntry serviceEntry) + MethodInfo methodInfo, ServiceEntry serviceEntry) { ApiDescription = apiDescription; SchemaRegistry = schemaRegistry; @@ -31,12 +64,30 @@ public OperationFilterContext( ServiceEntry = serviceEntry; } - public ServiceEntry ServiceEntry { get; set; } + #endregion 构造函数 + #region 属性 + + /// + /// Gets the ApiDescription + /// public ApiDescription ApiDescription { get; private set; } + /// + /// Gets the MethodInfo + /// + public MethodInfo MethodInfo { get; } + + /// + /// Gets the SchemaRegistry + /// public ISchemaRegistry SchemaRegistry { get; private set; } - public MethodInfo MethodInfo { get; } + /// + /// Gets or sets the ServiceEntry + /// + public ServiceEntry ServiceEntry { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/IParameterFilter.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/IParameterFilter.cs index 752f67e7f..0a7e74bc1 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/IParameterFilter.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/IParameterFilter.cs @@ -1,16 +1,44 @@ -using System.Reflection; -using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.AspNetCore.Mvc.ApiExplorer; using Surging.Core.Swagger; +using System.Reflection; namespace Surging.Core.SwaggerGen { + #region 接口 + + /// + /// Defines the + /// public interface IParameterFilter { + #region 方法 + + /// + /// The Apply + /// + /// The parameter + /// The context void Apply(IParameter parameter, ParameterFilterContext context); + + #endregion 方法 } + #endregion 接口 + + /// + /// Defines the + /// public class ParameterFilterContext { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The apiParameterDescription + /// The schemaRegistry + /// The parameterInfo + /// The propertyInfo public ParameterFilterContext( ApiParameterDescription apiParameterDescription, ISchemaRegistry schemaRegistry, @@ -23,12 +51,30 @@ public ParameterFilterContext( PropertyInfo = propertyInfo; } - public ApiParameterDescription ApiParameterDescription { get; } + #endregion 构造函数 - public ISchemaRegistry SchemaRegistry { get; } + #region 属性 + + /// + /// Gets the ApiParameterDescription + /// + public ApiParameterDescription ApiParameterDescription { get; } + /// + /// Gets the ParameterInfo + /// public ParameterInfo ParameterInfo { get; } + /// + /// Gets the PropertyInfo + /// public PropertyInfo PropertyInfo { get; } + + /// + /// Gets the SchemaRegistry + /// + public ISchemaRegistry SchemaRegistry { get; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/ISchemaFilter.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/ISchemaFilter.cs index 0fa18ed5e..ebfec715f 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/ISchemaFilter.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/ISchemaFilter.cs @@ -1,16 +1,43 @@ -using System; -using Newtonsoft.Json.Serialization; +using Newtonsoft.Json.Serialization; using Surging.Core.Swagger; +using System; namespace Surging.Core.SwaggerGen { + #region 接口 + + /// + /// Defines the + /// public interface ISchemaFilter { + #region 方法 + + /// + /// The Apply + /// + /// The schema + /// The context void Apply(Schema schema, SchemaFilterContext context); + + #endregion 方法 } + #endregion 接口 + + /// + /// Defines the + /// public class SchemaFilterContext { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The systemType + /// The jsonContract + /// The schemaRegistry public SchemaFilterContext( Type systemType, JsonContract jsonContract, @@ -21,10 +48,25 @@ public SchemaFilterContext( SchemaRegistry = schemaRegistry; } - public Type SystemType { get; private set; } + #endregion 构造函数 + + #region 属性 + /// + /// Gets the JsonContract + /// public JsonContract JsonContract { get; private set; } + /// + /// Gets the SchemaRegistry + /// public ISchemaRegistry SchemaRegistry { get; private set; } + + /// + /// Gets the SystemType + /// + public Type SystemType { get; private set; } + + #endregion 属性 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/ISchemaRegistry.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/ISchemaRegistry.cs index 867203486..f9f648bac 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/ISchemaRegistry.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/ISchemaRegistry.cs @@ -1,14 +1,44 @@ using Surging.Core.Swagger; using System; using System.Collections.Generic; + namespace Surging.Core.SwaggerGen { + #region 接口 + + /// + /// Defines the + /// public interface ISchemaRegistry { - Schema GetOrRegister(Type type); + #region 属性 + + /// + /// Gets the Definitions + /// + IDictionary Definitions { get; } + + #endregion 属性 + #region 方法 + + /// + /// The GetOrRegister + /// + /// The parmName + /// The type + /// The Schema GetOrRegister(string parmName, Type type); - IDictionary Definitions { get; } + /// + /// The GetOrRegister + /// + /// The type + /// The + Schema GetOrRegister(Type type); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/ISchemaRegistryFactory.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/ISchemaRegistryFactory.cs index 530554a43..c96bfd71b 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/ISchemaRegistryFactory.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/ISchemaRegistryFactory.cs @@ -1,7 +1,22 @@ namespace Surging.Core.SwaggerGen { + #region 接口 + + /// + /// Defines the + /// public interface ISchemaRegistryFactory { + #region 方法 + + /// + /// The Create + /// + /// The ISchemaRegistry Create(); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/JsonContractExtensions.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/JsonContractExtensions.cs index a08d7f8fe..0e309a7da 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/JsonContractExtensions.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/JsonContractExtensions.cs @@ -2,8 +2,18 @@ namespace Surging.Core.SwaggerGen { + /// + /// Defines the + /// internal static class JsonContractExtensions { + #region 方法 + + /// + /// The IsSelfReferencingArrayOrDictionary + /// + /// The jsonContract + /// The internal static bool IsSelfReferencingArrayOrDictionary(this JsonContract jsonContract) { if (jsonContract is JsonArrayContract arrayContract) @@ -14,5 +24,7 @@ internal static bool IsSelfReferencingArrayOrDictionary(this JsonContract jsonCo return false; } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/JsonPropertyExtensions.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/JsonPropertyExtensions.cs index 9dd5b82fb..1b72b0013 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/JsonPropertyExtensions.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/JsonPropertyExtensions.cs @@ -1,14 +1,49 @@ -using System; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json.Serialization; +using System; using System.ComponentModel.DataAnnotations; -using System.Reflection; using System.Linq; -using Microsoft.AspNetCore.Mvc; -using Newtonsoft.Json.Serialization; +using System.Reflection; namespace Surging.Core.SwaggerGen { + /// + /// Defines the + /// internal static class JsonPropertyExtensions { + #region 方法 + + /// + /// The HasAttribute + /// + /// + /// The jsonProperty + /// The + internal static bool HasAttribute(this JsonProperty jsonProperty) + where T : Attribute + { + if (!jsonProperty.TryGetMemberInfo(out MemberInfo memberInfo)) + return false; + + return memberInfo.GetCustomAttribute() != null; + } + + /// + /// The IsObsolete + /// + /// The jsonProperty + /// The + internal static bool IsObsolete(this JsonProperty jsonProperty) + { + return jsonProperty.HasAttribute(); + } + + /// + /// The IsRequired + /// + /// The jsonProperty + /// The internal static bool IsRequired(this JsonProperty jsonProperty) { if (jsonProperty.Required == Newtonsoft.Json.Required.AllowNull) @@ -23,20 +58,12 @@ internal static bool IsRequired(this JsonProperty jsonProperty) return false; } - internal static bool IsObsolete(this JsonProperty jsonProperty) - { - return jsonProperty.HasAttribute(); - } - - internal static bool HasAttribute(this JsonProperty jsonProperty) - where T : Attribute - { - if (!jsonProperty.TryGetMemberInfo(out MemberInfo memberInfo)) - return false; - - return memberInfo.GetCustomAttribute() != null; - } - + /// + /// The TryGetMemberInfo + /// + /// The jsonProperty + /// The memberInfo + /// The internal static bool TryGetMemberInfo(this JsonProperty jsonProperty, out MemberInfo memberInfo) { if (jsonProperty.UnderlyingName == null) @@ -57,5 +84,7 @@ internal static bool TryGetMemberInfo(this JsonProperty jsonProperty, out Member return (memberInfo != null); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SchemaExtensions.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SchemaExtensions.cs index ad1c5c9e1..a61954cdc 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SchemaExtensions.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SchemaExtensions.cs @@ -1,13 +1,38 @@ -using System; +using Surging.Core.Swagger; +using System; +using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; -using System.Collections.Generic; -using Surging.Core.Swagger; namespace Surging.Core.SwaggerGen { + /// + /// Defines the + /// internal static class SchemaExtensions { + #region 字段 + + /// + /// Defines the DataTypeFormatMap + /// + private static readonly Dictionary DataTypeFormatMap = new Dictionary + { + { DataType.Date, "date" }, + { DataType.DateTime, "date-time" }, + { DataType.Password, "password" } + }; + + #endregion 字段 + + #region 方法 + + /// + /// The AssignAttributeMetadata + /// + /// The schema + /// The attributes + /// The internal static Schema AssignAttributeMetadata(this Schema schema, IEnumerable attributes) { foreach (var attribute in attributes) @@ -49,6 +74,11 @@ internal static Schema AssignAttributeMetadata(this Schema schema, IEnumerable + /// The PopulateFrom + /// + /// The partialSchema + /// The schema internal static void PopulateFrom(this PartialSchema partialSchema, Schema schema) { if (schema == null) return; @@ -79,11 +109,6 @@ internal static void PopulateFrom(this PartialSchema partialSchema, Schema schem partialSchema.MultipleOf = schema.MultipleOf; } - private static readonly Dictionary DataTypeFormatMap = new Dictionary - { - { DataType.Date, "date" }, - { DataType.DateTime, "date-time" }, - { DataType.Password, "password" } - }; + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SchemaIdManager.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SchemaIdManager.cs index f04bb3b13..2eed64f5b 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SchemaIdManager.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SchemaIdManager.cs @@ -4,17 +4,46 @@ namespace Surging.Core.SwaggerGen { + /// + /// Defines the + /// public class SchemaIdManager { - private readonly Func _schemaIdSelector; + #region 字段 + + /// + /// Defines the _schemaIdMap + /// private readonly IDictionary _schemaIdMap; + /// + /// Defines the _schemaIdSelector + /// + private readonly Func _schemaIdSelector; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The schemaIdSelector public SchemaIdManager(Func schemaIdSelector) { _schemaIdSelector = schemaIdSelector; _schemaIdMap = new Dictionary(); } + #endregion 构造函数 + + #region 方法 + + /// + /// The IdFor + /// + /// The type + /// The public string IdFor(Type type) { if (!_schemaIdMap.TryGetValue(type, out string schemaId)) @@ -33,5 +62,7 @@ public string IdFor(Type type) return schemaId; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SchemaRegistry.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SchemaRegistry.cs index 35534bf81..325fd9ccf 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SchemaRegistry.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SchemaRegistry.cs @@ -1,22 +1,75 @@ -using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; +using Surging.Core.Swagger; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.Serialization; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using Newtonsoft.Json.Converters; -using Surging.Core.Swagger; namespace Surging.Core.SwaggerGen { + /// + /// Defines the + /// public class SchemaRegistry : ISchemaRegistry { - private readonly JsonSerializerSettings _jsonSerializerSettings; + #region 字段 + + /// + /// Defines the PrimitiveTypeMap + /// + private static readonly Dictionary> PrimitiveTypeMap = new Dictionary> + { + { typeof(short), () => new Schema { Type = "integer", Format = "int32" } }, + { typeof(ushort), () => new Schema { Type = "integer", Format = "int32" } }, + { typeof(int), () => new Schema { Type = "integer", Format = "int32" } }, + { typeof(uint), () => new Schema { Type = "integer", Format = "int32" } }, + { typeof(long), () => new Schema { Type = "integer", Format = "int64" } }, + { typeof(ulong), () => new Schema { Type = "integer", Format = "int64" } }, + { typeof(float), () => new Schema { Type = "number", Format = "float" } }, + { typeof(double), () => new Schema { Type = "number", Format = "double" } }, + { typeof(decimal), () => new Schema { Type = "number", Format = "double" } }, + { typeof(byte), () => new Schema { Type = "integer", Format = "int32" } }, + { typeof(sbyte), () => new Schema { Type = "integer", Format = "int32" } }, + { typeof(byte[]), () => new Schema { Type = "string", Format = "byte" } }, + { typeof(sbyte[]), () => new Schema { Type = "string", Format = "byte" } }, + { typeof(bool), () => new Schema { Type = "boolean" } }, + { typeof(DateTime), () => new Schema { Type = "string", Format = "date-time" } }, + { typeof(DateTimeOffset), () => new Schema { Type = "string", Format = "date-time" } }, + { typeof(Guid), () => new Schema { Type = "string", Format = "uuid" } } + }; + + /// + /// Defines the _jsonContractResolver + /// private readonly IContractResolver _jsonContractResolver; + + /// + /// Defines the _jsonSerializerSettings + /// + private readonly JsonSerializerSettings _jsonSerializerSettings; + + /// + /// Defines the _options + /// private readonly SchemaRegistryOptions _options; + + /// + /// Defines the _schemaIdManager + /// private readonly SchemaIdManager _schemaIdManager; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The jsonSerializerSettings + /// The options public SchemaRegistry( JsonSerializerSettings jsonSerializerSettings, SchemaRegistryOptions options = null) @@ -28,15 +81,29 @@ public SchemaRegistry( Definitions = new Dictionary(); } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the Definitions + /// public IDictionary Definitions { get; private set; } - public Schema GetOrRegister(Type type) - => GetOrRegister(null, type); + #endregion 属性 + #region 方法 + + /// + /// The GetOrRegister + /// + /// The paramName + /// The type + /// The public Schema GetOrRegister(string paramName, Type type) { var referencedTypes = new Queue(); - var schema = CreateSchema(paramName,type, referencedTypes); + var schema = CreateSchema(paramName, type, referencedTypes); // Ensure all referenced types have a corresponding definition while (referencedTypes.Any()) @@ -55,90 +122,84 @@ public Schema GetOrRegister(string paramName, Type type) return schema; } - private Schema CreateSchema(Type type, Queue referencedTypes) => - CreateSchema(null, type, referencedTypes); - + /// + /// The GetOrRegister + /// + /// The type + /// The + public Schema GetOrRegister(Type type) + => GetOrRegister(null, type); - private Schema CreateSchema(string paramName, Type type, Queue referencedTypes) + /// + /// The CreateArraySchema + /// + /// The arrayContract + /// The referencedTypes + /// The + private Schema CreateArraySchema(JsonArrayContract arrayContract, Queue referencedTypes) { - // If Option (F#), use the type argument - if (type.IsFSharpOption()) - type = type.GetGenericArguments()[0]; - - var jsonContract = _jsonContractResolver.ResolveContract(type); - - var createReference = !_options.CustomTypeMappings.ContainsKey(type) - && type != typeof(object) - && (// Type describes an object - jsonContract is JsonObjectContract || - // Type is self-referencing - jsonContract.IsSelfReferencingArrayOrDictionary() || - // Type is enum and opt-in flag set - (type.GetTypeInfo().IsEnum && _options.UseReferencedDefinitionsForEnums)); + var type = arrayContract.UnderlyingType; + var itemType = arrayContract.CollectionItemType ?? typeof(object); - return createReference - ? CreateReferenceSchema(type, referencedTypes) - : CreateInlineSchema(paramName, type, referencedTypes); - } + var isASet = (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(ISet<>) + || type.GetInterfaces().Any(i => i.GetTypeInfo().IsGenericType && i.GetGenericTypeDefinition() == typeof(ISet<>))); - private Schema CreateReferenceSchema(Type type, Queue referencedTypes) - { - referencedTypes.Enqueue(type); - return new Schema { Ref = "#/definitions/" + _schemaIdManager.IdFor(type) }; + return new Schema + { + Type = "array", + Items = CreateSchema(itemType, referencedTypes), + UniqueItems = isASet + }; } - private Schema CreateInlineSchema(string paramName, Type type, Queue referencedTypes) + /// + /// The CreateDictionarySchema + /// + /// The paramName + /// The dictionaryContract + /// The referencedTypes + /// The + private Schema CreateDictionarySchema(string paramName, JsonDictionaryContract dictionaryContract, Queue referencedTypes) { - Schema schema; - - var jsonContract = _jsonContractResolver.ResolveContract(type); + var keyType = dictionaryContract.DictionaryKeyType ?? typeof(object); + var valueType = dictionaryContract.DictionaryValueType ?? typeof(object); - if (_options.CustomTypeMappings.ContainsKey(type)) + if (keyType.GetTypeInfo().IsEnum) { - schema = _options.CustomTypeMappings[type](); + return new Schema + { + Type = "object", + Properties = Enum.GetNames(keyType).ToDictionary( + (name) => dictionaryContract.DictionaryKeyResolver(name), + (name) => CreateSchema(valueType, referencedTypes) + ) + }; } - else + else if (!string.IsNullOrEmpty(paramName)) { - // TODO: Perhaps a "Chain of Responsibility" would clean this up a little? - if (jsonContract is JsonPrimitiveContract) - schema = CreatePrimitiveSchema((JsonPrimitiveContract)jsonContract); - else if (jsonContract is JsonDictionaryContract) - schema = CreateDictionarySchema(paramName,(JsonDictionaryContract)jsonContract, referencedTypes); - else if (jsonContract is JsonArrayContract) - schema = CreateArraySchema((JsonArrayContract)jsonContract, referencedTypes); - else if (jsonContract is JsonObjectContract && type != typeof(object)) - schema = CreateObjectSchema((JsonObjectContract)jsonContract, referencedTypes); - else - // None of the above, fallback to abstract "object" - schema = new Schema { Type = "object" }; + return new Schema + { + Type = "object", + Properties = new Dictionary { {paramName, + CreateSchema(valueType, referencedTypes) } } + }; } - - var filterContext = new SchemaFilterContext(type, jsonContract, this); - foreach (var filter in _options.SchemaFilters) + else { - filter.Apply(schema, filterContext); + return new Schema + { + Type = "object", + AdditionalProperties = CreateSchema(valueType, referencedTypes) + }; } - - return schema; - } - - private Schema CreatePrimitiveSchema(JsonPrimitiveContract primitiveContract) - { - // If Nullable, use the type argument - var type = primitiveContract.UnderlyingType.IsNullable() - ? Nullable.GetUnderlyingType(primitiveContract.UnderlyingType) - : primitiveContract.UnderlyingType; - - if (type.GetTypeInfo().IsEnum) - return CreateEnumSchema(primitiveContract, type); - - if (PrimitiveTypeMap.ContainsKey(type)) - return PrimitiveTypeMap[type](); - - // None of the above, fallback to string - return new Schema { Type = "string" }; } + /// + /// The CreateEnumSchema + /// + /// The primitiveContract + /// The type + /// The private Schema CreateEnumSchema(JsonPrimitiveContract primitiveContract, Type type) { var stringEnumConverter = primitiveContract.Converter as StringEnumConverter @@ -178,58 +239,54 @@ private Schema CreateEnumSchema(JsonPrimitiveContract primitiveContract, Type ty }; } - private Schema CreateDictionarySchema(string paramName, JsonDictionaryContract dictionaryContract, Queue referencedTypes) + /// + /// The CreateInlineSchema + /// + /// The paramName + /// The type + /// The referencedTypes + /// The + private Schema CreateInlineSchema(string paramName, Type type, Queue referencedTypes) { - var keyType = dictionaryContract.DictionaryKeyType ?? typeof(object); - var valueType = dictionaryContract.DictionaryValueType ?? typeof(object); + Schema schema; - if (keyType.GetTypeInfo().IsEnum) - { - return new Schema - { - Type = "object", - Properties = Enum.GetNames(keyType).ToDictionary( - (name) => dictionaryContract.DictionaryKeyResolver(name), - (name) => CreateSchema(valueType, referencedTypes) - ) - }; - } - else if(!string.IsNullOrEmpty(paramName)) + var jsonContract = _jsonContractResolver.ResolveContract(type); + + if (_options.CustomTypeMappings.ContainsKey(type)) { - return new Schema - { - Type = "object", - Properties = new Dictionary { {paramName, - CreateSchema(valueType, referencedTypes) } } - - }; + schema = _options.CustomTypeMappings[type](); } else { - return new Schema - { - Type = "object", - AdditionalProperties = CreateSchema(valueType, referencedTypes) - }; + // TODO: Perhaps a "Chain of Responsibility" would clean this up a little? + if (jsonContract is JsonPrimitiveContract) + schema = CreatePrimitiveSchema((JsonPrimitiveContract)jsonContract); + else if (jsonContract is JsonDictionaryContract) + schema = CreateDictionarySchema(paramName, (JsonDictionaryContract)jsonContract, referencedTypes); + else if (jsonContract is JsonArrayContract) + schema = CreateArraySchema((JsonArrayContract)jsonContract, referencedTypes); + else if (jsonContract is JsonObjectContract && type != typeof(object)) + schema = CreateObjectSchema((JsonObjectContract)jsonContract, referencedTypes); + else + // None of the above, fallback to abstract "object" + schema = new Schema { Type = "object" }; } - } - - private Schema CreateArraySchema(JsonArrayContract arrayContract, Queue referencedTypes) - { - var type = arrayContract.UnderlyingType; - var itemType = arrayContract.CollectionItemType ?? typeof(object); - var isASet = (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(ISet<>) - || type.GetInterfaces().Any(i => i.GetTypeInfo().IsGenericType && i.GetGenericTypeDefinition() == typeof(ISet<>))); - - return new Schema + var filterContext = new SchemaFilterContext(type, jsonContract, this); + foreach (var filter in _options.SchemaFilters) { - Type = "array", - Items = CreateSchema(itemType, referencedTypes), - UniqueItems = isASet - }; + filter.Apply(schema, filterContext); + } + + return schema; } + /// + /// The CreateObjectSchema + /// + /// The jsonContract + /// The referencedTypes + /// The private Schema CreateObjectSchema(JsonObjectContract jsonContract, Queue referencedTypes) { var applicableJsonProperties = jsonContract.Properties @@ -260,6 +317,34 @@ private Schema CreateObjectSchema(JsonObjectContract jsonContract, Queue r return schema; } + /// + /// The CreatePrimitiveSchema + /// + /// The primitiveContract + /// The + private Schema CreatePrimitiveSchema(JsonPrimitiveContract primitiveContract) + { + // If Nullable, use the type argument + var type = primitiveContract.UnderlyingType.IsNullable() + ? Nullable.GetUnderlyingType(primitiveContract.UnderlyingType) + : primitiveContract.UnderlyingType; + + if (type.GetTypeInfo().IsEnum) + return CreateEnumSchema(primitiveContract, type); + + if (PrimitiveTypeMap.ContainsKey(type)) + return PrimitiveTypeMap[type](); + + // None of the above, fallback to string + return new Schema { Type = "string" }; + } + + /// + /// The CreatePropertySchema + /// + /// The jsonProperty + /// The referencedTypes + /// The private Schema CreatePropertySchema(JsonProperty jsonProperty, Queue referencedTypes) { var schema = CreateSchema(jsonProperty.PropertyType, referencedTypes); @@ -273,25 +358,56 @@ private Schema CreatePropertySchema(JsonProperty jsonProperty, Queue refer return schema; } - private static readonly Dictionary> PrimitiveTypeMap = new Dictionary> + /// + /// The CreateReferenceSchema + /// + /// The type + /// The referencedTypes + /// The + private Schema CreateReferenceSchema(Type type, Queue referencedTypes) { - { typeof(short), () => new Schema { Type = "integer", Format = "int32" } }, - { typeof(ushort), () => new Schema { Type = "integer", Format = "int32" } }, - { typeof(int), () => new Schema { Type = "integer", Format = "int32" } }, - { typeof(uint), () => new Schema { Type = "integer", Format = "int32" } }, - { typeof(long), () => new Schema { Type = "integer", Format = "int64" } }, - { typeof(ulong), () => new Schema { Type = "integer", Format = "int64" } }, - { typeof(float), () => new Schema { Type = "number", Format = "float" } }, - { typeof(double), () => new Schema { Type = "number", Format = "double" } }, - { typeof(decimal), () => new Schema { Type = "number", Format = "double" } }, - { typeof(byte), () => new Schema { Type = "integer", Format = "int32" } }, - { typeof(sbyte), () => new Schema { Type = "integer", Format = "int32" } }, - { typeof(byte[]), () => new Schema { Type = "string", Format = "byte" } }, - { typeof(sbyte[]), () => new Schema { Type = "string", Format = "byte" } }, - { typeof(bool), () => new Schema { Type = "boolean" } }, - { typeof(DateTime), () => new Schema { Type = "string", Format = "date-time" } }, - { typeof(DateTimeOffset), () => new Schema { Type = "string", Format = "date-time" } }, - { typeof(Guid), () => new Schema { Type = "string", Format = "uuid" } } - }; + referencedTypes.Enqueue(type); + return new Schema { Ref = "#/definitions/" + _schemaIdManager.IdFor(type) }; + } + + /// + /// The CreateSchema + /// + /// The paramName + /// The type + /// The referencedTypes + /// The + private Schema CreateSchema(string paramName, Type type, Queue referencedTypes) + { + // If Option (F#), use the type argument + if (type.IsFSharpOption()) + type = type.GetGenericArguments()[0]; + + var jsonContract = _jsonContractResolver.ResolveContract(type); + + var createReference = !_options.CustomTypeMappings.ContainsKey(type) + && type != typeof(object) + && (// Type describes an object + jsonContract is JsonObjectContract || + // Type is self-referencing + jsonContract.IsSelfReferencingArrayOrDictionary() || + // Type is enum and opt-in flag set + (type.GetTypeInfo().IsEnum && _options.UseReferencedDefinitionsForEnums)); + + return createReference + ? CreateReferenceSchema(type, referencedTypes) + : CreateInlineSchema(paramName, type, referencedTypes); + } + + /// + /// The CreateSchema + /// + /// The type + /// The referencedTypes + /// The + private Schema CreateSchema(Type type, Queue referencedTypes) => + CreateSchema(null, type, referencedTypes); + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SchemaRegistryFactory.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SchemaRegistryFactory.cs index 2e3803854..b6cf4a08e 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SchemaRegistryFactory.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SchemaRegistryFactory.cs @@ -4,17 +4,44 @@ namespace Surging.Core.SwaggerGen { + /// + /// Defines the + /// public class SchemaRegistryFactory : ISchemaRegistryFactory { + #region 字段 + + /// + /// Defines the _jsonSerializerSettings + /// private readonly JsonSerializerSettings _jsonSerializerSettings; + + /// + /// Defines the _schemaRegistryOptions + /// private readonly SchemaRegistryOptions _schemaRegistryOptions; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The mvcJsonOptionsAccessor + /// The schemaRegistryOptionsAccessor public SchemaRegistryFactory( IOptions mvcJsonOptionsAccessor, IOptions schemaRegistryOptionsAccessor) : this(mvcJsonOptionsAccessor.Value.SerializerSettings, schemaRegistryOptionsAccessor.Value) - { } + { + } + /// + /// Initializes a new instance of the class. + /// + /// The jsonSerializerSettings + /// The schemaRegistryOptions public SchemaRegistryFactory( JsonSerializerSettings jsonSerializerSettings, SchemaRegistryOptions schemaRegistryOptions) @@ -23,9 +50,19 @@ public SchemaRegistryFactory( _schemaRegistryOptions = schemaRegistryOptions; } + #endregion 构造函数 + + #region 方法 + + /// + /// The Create + /// + /// The public ISchemaRegistry Create() { return new SchemaRegistry(_jsonSerializerSettings, _schemaRegistryOptions); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SchemaRegistryOptions.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SchemaRegistryOptions.cs index ed5fdd7d6..c12909834 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SchemaRegistryOptions.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SchemaRegistryOptions.cs @@ -1,11 +1,19 @@ -using System; +using Surging.Core.Swagger; +using System; using System.Collections.Generic; -using Surging.Core.Swagger; namespace Surging.Core.SwaggerGen { + /// + /// Defines the + /// public class SchemaRegistryOptions { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public SchemaRegistryOptions() { CustomTypeMappings = new Dictionary>(); @@ -13,20 +21,50 @@ public SchemaRegistryOptions() SchemaFilters = new List(); } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets or sets the CustomTypeMappings + /// public IDictionary> CustomTypeMappings { get; set; } + /// + /// Gets or sets a value indicating whether DescribeAllEnumsAsStrings + /// public bool DescribeAllEnumsAsStrings { get; set; } + /// + /// Gets or sets a value indicating whether DescribeStringEnumsInCamelCase + /// public bool DescribeStringEnumsInCamelCase { get; set; } - public bool UseReferencedDefinitionsForEnums { get; set; } - - public Func SchemaIdSelector { get; set; } - + /// + /// Gets or sets a value indicating whether IgnoreFullyQualified + /// public bool IgnoreFullyQualified { get; set; } + /// + /// Gets or sets a value indicating whether IgnoreObsoleteProperties + /// public bool IgnoreObsoleteProperties { get; set; } + /// + /// Gets or sets the SchemaFilters + /// public IList SchemaFilters { get; set; } + + /// + /// Gets or sets the SchemaIdSelector + /// + public Func SchemaIdSelector { get; set; } + + /// + /// Gets or sets a value indicating whether UseReferencedDefinitionsForEnums + /// + public bool UseReferencedDefinitionsForEnums { get; set; } + + #endregion 属性 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/StringExtensions.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/StringExtensions.cs index 53991476e..3fdb2f4ab 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/StringExtensions.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/StringExtensions.cs @@ -1,17 +1,34 @@ namespace Surging.Core.SwaggerGen { + /// + /// Defines the + /// internal static class StringExtensions { + #region 方法 + + /// + /// The ToCamelCase + /// + /// The value + /// The internal static string ToCamelCase(this string value) { if (string.IsNullOrEmpty(value)) return value; return char.ToLowerInvariant(value[0]) + value.Substring(1); } + /// + /// The ToTitleCase + /// + /// The value + /// The internal static string ToTitleCase(this string value) { if (string.IsNullOrEmpty(value)) return value; return char.ToUpperInvariant(value[0]) + value.Substring(1); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SwaggerGenerator.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SwaggerGenerator.cs index 96382d2cc..d40ce33f4 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SwaggerGenerator.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SwaggerGenerator.cs @@ -1,40 +1,110 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using System.Reflection; -using System.ComponentModel.DataAnnotations; +using Autofac; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.Extensions.Options; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Http; -using Surging.Core.Swagger; -using Surging.Core.CPlatform.Runtime.Server; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Microsoft.Extensions.Primitives; using Surging.Core.CPlatform; +using Surging.Core.CPlatform.Messages; +using Surging.Core.CPlatform.Runtime.Server; using Surging.Core.CPlatform.Utilities; -using Autofac; -using Microsoft.Extensions.Primitives; +using Surging.Core.Swagger; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Reflection; +using System.Text.RegularExpressions; using System.Threading.Tasks; -using Surging.Core.CPlatform.Messages; namespace Surging.Core.SwaggerGen { + /// + /// Defines the + /// public class SwaggerGenerator : ISwaggerProvider { + #region 字段 + + /// + /// Defines the ResponseDescriptionMap + /// + private static readonly Dictionary ResponseDescriptionMap = new Dictionary + { + { "1\\d{2}", "Information" }, + { "2\\d{2}", "Success" }, + { "3\\d{2}", "Redirect" }, + { "400", "Bad Request" }, + { "401", "Unauthorized" }, + { "403", "Forbidden" }, + { "404", "Not Found" }, + { "405", "Method Not Allowed" }, + { "406", "Not Acceptable" }, + { "408", "Request Timeout" }, + { "409", "Conflict" }, + { "4\\d{2}", "Client Error" }, + { "5\\d{2}", "Server Error" } + }; + + /// + /// Defines the _apiDescriptionsProvider + /// private readonly IApiDescriptionGroupCollectionProvider _apiDescriptionsProvider; - private readonly ISchemaRegistryFactory _schemaRegistryFactory; + + /// + /// Defines the _options + /// private readonly SwaggerGeneratorOptions _options; + + /// + /// Defines the _schemaRegistryFactory + /// + private readonly ISchemaRegistryFactory _schemaRegistryFactory; + + /// + /// Defines the _serviceEntryProvider + /// private readonly IServiceEntryProvider _serviceEntryProvider; + /// + /// Defines the ParameterLocationMap + /// + private static Dictionary ParameterLocationMap = new Dictionary + { + { BindingSource.Form, "formData" }, + { BindingSource.FormFile, "formData" }, + { BindingSource.Body, "body" }, + { BindingSource.Header, "header" }, + { BindingSource.Path, "path" }, + { BindingSource.Query, "query" } + }; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The apiDescriptionsProvider + /// The schemaRegistryFactory + /// The optionsAccessor public SwaggerGenerator( IApiDescriptionGroupCollectionProvider apiDescriptionsProvider, ISchemaRegistryFactory schemaRegistryFactory, IOptions optionsAccessor) - : this (apiDescriptionsProvider, schemaRegistryFactory, optionsAccessor.Value) - { } + : this(apiDescriptionsProvider, schemaRegistryFactory, optionsAccessor.Value) + { + } + /// + /// Initializes a new instance of the class. + /// + /// The apiDescriptionsProvider + /// The schemaRegistryFactory + /// The options public SwaggerGenerator( IApiDescriptionGroupCollectionProvider apiDescriptionsProvider, ISchemaRegistryFactory schemaRegistryFactory, @@ -46,6 +116,18 @@ public SwaggerGenerator( _serviceEntryProvider = ServiceLocator.Current.Resolve(); } + #endregion 构造函数 + + #region 方法 + + /// + /// The GetSwagger + /// + /// The documentName + /// The host + /// The basePath + /// The schemes + /// The public SwaggerDocument GetSwagger( string documentName, string host = null, @@ -55,12 +137,10 @@ public SwaggerDocument GetSwagger( if (!_options.SwaggerDocs.TryGetValue(documentName, out Info info)) throw new UnknownSwaggerDocument(documentName); - - var entry = _serviceEntryProvider.GetALLEntries(); - entry = entry - .Where(apiDesc => _options.DocInclusionPredicateV2(documentName, apiDesc)); + entry = entry + .Where(apiDesc => _options.DocInclusionPredicateV2(documentName, apiDesc)); var schemaRegistry = _schemaRegistryFactory.Create(); @@ -79,139 +159,182 @@ public SwaggerDocument GetSwagger( return swaggerDoc; } - private Dictionary CreatePathItems( - IEnumerable apiDescriptions, + /// + /// The CreateBodyParameter + /// + /// The apiParameterDescription + /// The name + /// The isRequired + /// The schemaRegistry + /// The + private IParameter CreateBodyParameter( + ApiParameterDescription apiParameterDescription, + string name, + bool isRequired, ISchemaRegistry schemaRegistry) { + var schema = schemaRegistry.GetOrRegister(apiParameterDescription.Type); - return apiDescriptions - .OrderBy(p => p.RoutePath) - .GroupBy(apiDesc => apiDesc.Descriptor.RoutePath) - .ToDictionary(entry => - entry.Key.IndexOf("/") == 0 ? entry.Key : $"/{entry.Key}" - , entry => CreatePathItem(entry, schemaRegistry)); + return new BodyParameter { Name = name, Schema = schema, Required = isRequired }; } - private Dictionary CreatePathItems( - IEnumerable apiDescriptions, - ISchemaRegistry schemaRegistry) + /// + /// The CreateBodyParameter + /// + /// The parameterInfo + /// The schemaRegistry + /// The + private IParameter CreateBodyParameter(ParameterInfo parameterInfo, ISchemaRegistry schemaRegistry) { - return apiDescriptions - .OrderBy(_options.SortKeySelector) - .GroupBy(apiDesc => apiDesc.RelativePathSansQueryString()) - .ToDictionary(group => "/" + group.Key, group => CreatePathItem(group, schemaRegistry)); + var schema = schemaRegistry.GetOrRegister(parameterInfo.Name, typeof(IDictionary<,>).MakeGenericType(typeof(string), parameterInfo.ParameterType)); + return new BodyParameter { Name = parameterInfo.Name, Schema = schema, Required = true }; } - private PathItem CreatePathItem( - IEnumerable apiDescriptions, - ISchemaRegistry schemaRegistry) + /// + /// The CreateConsumes + /// + /// The apiDescription + /// The customAttributes + /// The + private IList CreateConsumes(ApiDescription apiDescription, IEnumerable customAttributes) { - var pathItem = new PathItem(); + var consumesAttribute = customAttributes.OfType().FirstOrDefault(); - // Group further by http method - var perMethodGrouping = apiDescriptions - .GroupBy(apiDesc => apiDesc.HttpMethod); + var mediaTypes = (consumesAttribute != null) + ? consumesAttribute.ContentTypes + : apiDescription.SupportedRequestFormats + .Select(apiRequestFormat => apiRequestFormat.MediaType); - foreach (var group in perMethodGrouping) - { - var httpMethod = group.Key; + return mediaTypes.ToList(); + } - if (httpMethod == null) - throw new NotSupportedException(string.Format( - "Ambiguous HTTP method for action - {0}. " + - "Actions require an explicit HttpMethod binding for Swagger 2.0", - group.First().ActionDescriptor.DisplayName)); + /// + /// The CreateNonBodyParameter + /// + /// The apiParameterDescription + /// The parameterInfo + /// The customAttributes + /// The name + /// The isRequired + /// The schemaRegistry + /// The + private IParameter CreateNonBodyParameter( + ApiParameterDescription apiParameterDescription, + ParameterInfo parameterInfo, + IEnumerable customAttributes, + string name, + bool isRequired, + ISchemaRegistry schemaRegistry) + { + var location = ParameterLocationMap.ContainsKey(apiParameterDescription.Source) + ? ParameterLocationMap[apiParameterDescription.Source] + : "query"; - if (group.Count() > 1 && _options.ConflictingActionsResolver == null) - throw new NotSupportedException(string.Format( - "HTTP method \"{0}\" & path \"{1}\" overloaded by actions - {2}. " + - "Actions require unique method/path combination for Swagger 2.0. Use ConflictingActionsResolver as a workaround", - httpMethod, - group.First().RelativePathSansQueryString(), - string.Join(",", group.Select(apiDesc => apiDesc.ActionDescriptor.DisplayName)))); + var nonBodyParam = new NonBodyParameter + { + Name = name, + In = location, + Required = (location == "path") ? true : isRequired, + }; - var apiDescription = (group.Count() > 1) ? _options.ConflictingActionsResolver(group) : group.Single(); + if (apiParameterDescription.Type == null) + { + nonBodyParam.Type = "string"; + } + else if (typeof(IFormFile).IsAssignableFrom(apiParameterDescription.Type)) + { + nonBodyParam.Type = "file"; + } + else + { + // Retrieve a Schema object for the type and copy common fields onto the parameter + var schema = schemaRegistry.GetOrRegister(apiParameterDescription.Type); - switch (httpMethod) + // NOTE: While this approach enables re-use of SchemaRegistry logic, it introduces complexity + // and constraints elsewhere (see below) and needs to be refactored! + + if (schema.Ref != null) { - case "GET": - pathItem.Get = CreateOperation(apiDescription, schemaRegistry); - break; - case "PUT": - pathItem.Put = CreateOperation(apiDescription, schemaRegistry); - break; - case "POST": - pathItem.Post = CreateOperation(apiDescription, schemaRegistry); - break; - case "DELETE": - pathItem.Delete = CreateOperation(apiDescription, schemaRegistry); - break; - case "OPTIONS": - pathItem.Options = CreateOperation(apiDescription, schemaRegistry); - break; - case "HEAD": - pathItem.Head = CreateOperation(apiDescription, schemaRegistry); - break; - case "PATCH": - pathItem.Patch = CreateOperation(apiDescription, schemaRegistry); - break; + // The registry created a referenced Schema that needs to be located. This means it's not neccessarily + // exclusive to this parameter and so, we can't assign any parameter specific attributes or metadata. + schema = schemaRegistry.Definitions[schema.Ref.Replace("#/definitions/", string.Empty)]; + } + else + { + // It's a value Schema. This means it's exclusive to this parameter and so, we can assign + // parameter specific attributes and metadata. Yep - it's hacky! + schema.AssignAttributeMetadata(customAttributes); + schema.Default = (parameterInfo != null && parameterInfo.IsOptional) + ? parameterInfo.DefaultValue + : null; } + + nonBodyParam.PopulateFrom(schema); } - return pathItem; + return nonBodyParam; } - private PathItem CreatePathItem( - IEnumerable serviceEntries, ISchemaRegistry schemaRegistry) + /// + /// The CreateNonBodyParameter + /// + /// The parameterInfo + /// The schemaRegistry + /// The + private IParameter CreateNonBodyParameter(ParameterInfo parameterInfo, ISchemaRegistry schemaRegistry) { - var pathItem = new PathItem(); - foreach (var entry in serviceEntries) + var nonBodyParam = new NonBodyParameter { - var methodInfo = entry.Type.GetTypeInfo().DeclaredMethods.Where(p => p.Name == entry.MethodName).FirstOrDefault(); - var parameterInfo = methodInfo.GetParameters(); - if (parameterInfo != null && parameterInfo.Any(p => - !UtilityType.ConvertibleType.GetTypeInfo().IsAssignableFrom(p.ParameterType))) - pathItem.Post = CreateOperation(entry, methodInfo,schemaRegistry); - else - pathItem.Get = CreateOperation(entry, methodInfo, schemaRegistry); + Name = parameterInfo.Name, + In = "query", + Required = true, + }; + if (parameterInfo.ParameterType == null) + { + nonBodyParam.Type = "string"; } - return pathItem; - } - - private Operation CreateOperation(ServiceEntry serviceEntry, MethodInfo methodInfo, ISchemaRegistry schemaRegistry) - { - var customAttributes = Enumerable.Empty(); - if (methodInfo !=null) + else if (typeof(IEnumerable>).IsAssignableFrom(parameterInfo.ParameterType) && + parameterInfo.ParameterType.Name == "HttpFormCollection") { - customAttributes = methodInfo.GetCustomAttributes(true) - .Union(methodInfo.DeclaringType.GetTypeInfo().GetCustomAttributes(true)); + nonBodyParam.Type = "file"; + nonBodyParam.In = "formData"; } - var isDeprecated = customAttributes.Any(attr => attr.GetType() == typeof(ObsoleteAttribute)); - - var operation = new Operation + else { - Tags = new[] { serviceEntry.Type.Name }, - OperationId = serviceEntry.Descriptor.Id, - Parameters= CreateParameters(serviceEntry, methodInfo,schemaRegistry), - Deprecated = isDeprecated ? true : (bool?)null, - Responses = CreateResponses(serviceEntry,methodInfo, schemaRegistry), + // Retrieve a Schema object for the type and copy common fields onto the parameter + var schema = schemaRegistry.GetOrRegister(parameterInfo.ParameterType); - }; + // NOTE: While this approach enables re-use of SchemaRegistry logic, it introduces complexity + // and constraints elsewhere (see below) and needs to be refactored! - var filterContext = new OperationFilterContext( - null, - schemaRegistry, - methodInfo,serviceEntry); + if (schema.Ref != null) + { + // The registry created a referenced Schema that needs to be located. This means it's not neccessarily + // exclusive to this parameter and so, we can't assign any parameter specific attributes or metadata. + schema = schemaRegistry.Definitions[schema.Ref.Replace("#/definitions/", string.Empty)]; + } + else + { + // It's a value Schema. This means it's exclusive to this parameter and so, we can assign + // parameter specific attributes and metadata. Yep - it's hacky! + schema.Default = (parameterInfo != null && parameterInfo.IsOptional) + ? parameterInfo.DefaultValue + : null; + } - foreach (var filter in _options.OperationFilters) - { - filter.Apply(operation, filterContext); + nonBodyParam.PopulateFrom(schema); } - return operation; + return nonBodyParam; } - private Operation CreateOperation( + /// + /// The CreateOperation + /// + /// The apiDescription + /// The schemaRegistry + /// The + private Operation CreateOperation( ApiDescription apiDescription, ISchemaRegistry schemaRegistry) { @@ -257,130 +380,51 @@ private Operation CreateOperation( return operation; } - private IList CreateConsumes(ApiDescription apiDescription, IEnumerable customAttributes) + /// + /// The CreateOperation + /// + /// The serviceEntry + /// The methodInfo + /// The schemaRegistry + /// The + private Operation CreateOperation(ServiceEntry serviceEntry, MethodInfo methodInfo, ISchemaRegistry schemaRegistry) { - var consumesAttribute = customAttributes.OfType().FirstOrDefault(); - - var mediaTypes = (consumesAttribute != null) - ? consumesAttribute.ContentTypes - : apiDescription.SupportedRequestFormats - .Select(apiRequestFormat => apiRequestFormat.MediaType); - - return mediaTypes.ToList(); - } - - private IList CreateProduces(ApiDescription apiDescription, IEnumerable customAttributes) - { - var producesAttribute = customAttributes.OfType().FirstOrDefault(); - - var mediaTypes = (producesAttribute != null) - ? producesAttribute.ContentTypes - : apiDescription.SupportedResponseTypes - .SelectMany(apiResponseType => apiResponseType.ApiResponseFormats) - .Select(apiResponseFormat => apiResponseFormat.MediaType) - .Distinct(); - - return mediaTypes.ToList(); - } - - private IList CreateParameters( - ApiDescription apiDescription, - ISchemaRegistry schemaRegistry) - { - var applicableParamDescriptions = apiDescription.ParameterDescriptions - .Where(paramDesc => - { - return paramDesc.Source.IsFromRequest - && (paramDesc.ModelMetadata == null || paramDesc.ModelMetadata.IsBindingAllowed); - }); - - return applicableParamDescriptions - .Select(paramDesc => CreateParameter(apiDescription, paramDesc, schemaRegistry)) - .ToList(); - } - - private IList CreateParameters( ServiceEntry serviceEntry, MethodInfo methodInfo, ISchemaRegistry schemaRegistry) - { - ParameterInfo [] parameterInfo = null; + var customAttributes = Enumerable.Empty(); if (methodInfo != null) { - parameterInfo = methodInfo.GetParameters(); - - }; - return parameterInfo !=null && parameterInfo.Any(p => - ! UtilityType.ConvertibleType.GetTypeInfo().IsAssignableFrom(p.ParameterType) && p.ParameterType.Name != "HttpFormCollection") - ? new List { CreateServiceKeyParameter() }.Union(parameterInfo.Select(p=> CreateBodyParameter(p,schemaRegistry))).ToList(): - new List { CreateServiceKeyParameter() }.Union(parameterInfo.Select(p => CreateNonBodyParameter(p, schemaRegistry))).ToList(); - } - - private IParameter CreateBodyParameter(ParameterInfo parameterInfo, ISchemaRegistry schemaRegistry) - { - - var schema = schemaRegistry.GetOrRegister(parameterInfo.Name,typeof(IDictionary<,>).MakeGenericType(typeof(string), parameterInfo.ParameterType)); - return new BodyParameter { Name = parameterInfo.Name,Schema=schema, Required = true }; - } + customAttributes = methodInfo.GetCustomAttributes(true) + .Union(methodInfo.DeclaringType.GetTypeInfo().GetCustomAttributes(true)); + } + var isDeprecated = customAttributes.Any(attr => attr.GetType() == typeof(ObsoleteAttribute)); - private IParameter CreateServiceKeyParameter() - { - var nonBodyParam = new NonBodyParameter + var operation = new Operation { - Name = "servicekey", - In = "query", - Required = false, + Tags = new[] { serviceEntry.Type.Name }, + OperationId = serviceEntry.Descriptor.Id, + Parameters = CreateParameters(serviceEntry, methodInfo, schemaRegistry), + Deprecated = isDeprecated ? true : (bool?)null, + Responses = CreateResponses(serviceEntry, methodInfo, schemaRegistry), }; - var schema = new Schema(); - schema.Description = "ServiceKey"; - nonBodyParam.PopulateFrom(schema); - return nonBodyParam; - } - private IParameter CreateNonBodyParameter(ParameterInfo parameterInfo, ISchemaRegistry schemaRegistry) - { - var nonBodyParam = new NonBodyParameter - { - Name = parameterInfo.Name, - In= "query", - Required = true, - }; + var filterContext = new OperationFilterContext( + null, + schemaRegistry, + methodInfo, serviceEntry); - if (parameterInfo.ParameterType == null) - { - nonBodyParam.Type = "string"; - } - else if (typeof(IEnumerable>).IsAssignableFrom(parameterInfo.ParameterType) && - parameterInfo.ParameterType.Name== "HttpFormCollection") - { - nonBodyParam.Type = "file"; - nonBodyParam.In = "formData"; - } - else + foreach (var filter in _options.OperationFilters) { - // Retrieve a Schema object for the type and copy common fields onto the parameter - var schema = schemaRegistry.GetOrRegister(parameterInfo.ParameterType); - - // NOTE: While this approach enables re-use of SchemaRegistry logic, it introduces complexity - // and constraints elsewhere (see below) and needs to be refactored! - - if (schema.Ref != null) - { - // The registry created a referenced Schema that needs to be located. This means it's not neccessarily - // exclusive to this parameter and so, we can't assign any parameter specific attributes or metadata. - schema = schemaRegistry.Definitions[schema.Ref.Replace("#/definitions/", string.Empty)]; - } - else - { - // It's a value Schema. This means it's exclusive to this parameter and so, we can assign - // parameter specific attributes and metadata. Yep - it's hacky! - schema.Default = (parameterInfo != null && parameterInfo.IsOptional) - ? parameterInfo.DefaultValue - : null; - } - - nonBodyParam.PopulateFrom(schema); + filter.Apply(operation, filterContext); } - return nonBodyParam; + return operation; } + /// + /// The CreateParameter + /// + /// The apiDescription + /// The apiParameterDescription + /// The schemaRegistry + /// The private IParameter CreateParameter( ApiDescription apiDescription, ApiParameterDescription apiParameterDescription, @@ -431,152 +475,293 @@ private IParameter CreateParameter( return parameter; } - private IParameter CreateBodyParameter( - ApiParameterDescription apiParameterDescription, - string name, - bool isRequired, + /// + /// The CreateParameters + /// + /// The serviceEntry + /// The methodInfo + /// The schemaRegistry + /// The + private IList CreateParameters(ServiceEntry serviceEntry, MethodInfo methodInfo, ISchemaRegistry schemaRegistry) + { + ParameterInfo[] parameterInfo = null; + if (methodInfo != null) + { + parameterInfo = methodInfo.GetParameters(); + }; + return parameterInfo != null && parameterInfo.Any(p => + !UtilityType.ConvertibleType.GetTypeInfo().IsAssignableFrom(p.ParameterType) && p.ParameterType.Name != "HttpFormCollection") + ? new List { CreateServiceKeyParameter() }.Union(parameterInfo.Select(p => CreateBodyParameter(p, schemaRegistry))).ToList() : + new List { CreateServiceKeyParameter() }.Union(parameterInfo.Select(p => CreateNonBodyParameter(p, schemaRegistry))).ToList(); + } + + /// + /// The CreateParameters + /// + /// The apiDescription + /// The schemaRegistry + /// The + private IList CreateParameters( + ApiDescription apiDescription, ISchemaRegistry schemaRegistry) { - var schema = schemaRegistry.GetOrRegister(apiParameterDescription.Type); + var applicableParamDescriptions = apiDescription.ParameterDescriptions + .Where(paramDesc => + { + return paramDesc.Source.IsFromRequest + && (paramDesc.ModelMetadata == null || paramDesc.ModelMetadata.IsBindingAllowed); + }); - return new BodyParameter { Name = name, Schema = schema, Required = isRequired }; + return applicableParamDescriptions + .Select(paramDesc => CreateParameter(apiDescription, paramDesc, schemaRegistry)) + .ToList(); } - private IParameter CreateNonBodyParameter( - ApiParameterDescription apiParameterDescription, - ParameterInfo parameterInfo, - IEnumerable customAttributes, - string name, - bool isRequired, + /// + /// The CreatePathItem + /// + /// The apiDescriptions + /// The schemaRegistry + /// The + private PathItem CreatePathItem( + IEnumerable apiDescriptions, ISchemaRegistry schemaRegistry) { - var location = ParameterLocationMap.ContainsKey(apiParameterDescription.Source) - ? ParameterLocationMap[apiParameterDescription.Source] - : "query"; + var pathItem = new PathItem(); - var nonBodyParam = new NonBodyParameter - { - Name = name, - In = location, - Required = (location == "path") ? true : isRequired, - }; + // Group further by http method + var perMethodGrouping = apiDescriptions + .GroupBy(apiDesc => apiDesc.HttpMethod); - if (apiParameterDescription.Type == null) - { - nonBodyParam.Type = "string"; - } - else if (typeof(IFormFile).IsAssignableFrom(apiParameterDescription.Type)) - { - nonBodyParam.Type = "file"; - } - else + foreach (var group in perMethodGrouping) { - // Retrieve a Schema object for the type and copy common fields onto the parameter - var schema = schemaRegistry.GetOrRegister(apiParameterDescription.Type); + var httpMethod = group.Key; - // NOTE: While this approach enables re-use of SchemaRegistry logic, it introduces complexity - // and constraints elsewhere (see below) and needs to be refactored! + if (httpMethod == null) + throw new NotSupportedException(string.Format( + "Ambiguous HTTP method for action - {0}. " + + "Actions require an explicit HttpMethod binding for Swagger 2.0", + group.First().ActionDescriptor.DisplayName)); - if (schema.Ref != null) - { - // The registry created a referenced Schema that needs to be located. This means it's not neccessarily - // exclusive to this parameter and so, we can't assign any parameter specific attributes or metadata. - schema = schemaRegistry.Definitions[schema.Ref.Replace("#/definitions/", string.Empty)]; - } - else + if (group.Count() > 1 && _options.ConflictingActionsResolver == null) + throw new NotSupportedException(string.Format( + "HTTP method \"{0}\" & path \"{1}\" overloaded by actions - {2}. " + + "Actions require unique method/path combination for Swagger 2.0. Use ConflictingActionsResolver as a workaround", + httpMethod, + group.First().RelativePathSansQueryString(), + string.Join(",", group.Select(apiDesc => apiDesc.ActionDescriptor.DisplayName)))); + + var apiDescription = (group.Count() > 1) ? _options.ConflictingActionsResolver(group) : group.Single(); + + switch (httpMethod) { - // It's a value Schema. This means it's exclusive to this parameter and so, we can assign - // parameter specific attributes and metadata. Yep - it's hacky! - schema.AssignAttributeMetadata(customAttributes); - schema.Default = (parameterInfo != null && parameterInfo.IsOptional) - ? parameterInfo.DefaultValue - : null; - } + case "GET": + pathItem.Get = CreateOperation(apiDescription, schemaRegistry); + break; - nonBodyParam.PopulateFrom(schema); + case "PUT": + pathItem.Put = CreateOperation(apiDescription, schemaRegistry); + break; + + case "POST": + pathItem.Post = CreateOperation(apiDescription, schemaRegistry); + break; + + case "DELETE": + pathItem.Delete = CreateOperation(apiDescription, schemaRegistry); + break; + + case "OPTIONS": + pathItem.Options = CreateOperation(apiDescription, schemaRegistry); + break; + + case "HEAD": + pathItem.Head = CreateOperation(apiDescription, schemaRegistry); + break; + + case "PATCH": + pathItem.Patch = CreateOperation(apiDescription, schemaRegistry); + break; + } } - return nonBodyParam; + return pathItem; } - private IDictionary CreateResponses( - ApiDescription apiDescription, + /// + /// The CreatePathItem + /// + /// The serviceEntries + /// The schemaRegistry + /// The + private PathItem CreatePathItem( + IEnumerable serviceEntries, ISchemaRegistry schemaRegistry) + { + var pathItem = new PathItem(); + foreach (var entry in serviceEntries) + { + var methodInfo = entry.Type.GetTypeInfo().DeclaredMethods.Where(p => p.Name == entry.MethodName).FirstOrDefault(); + var parameterInfo = methodInfo.GetParameters(); + if (parameterInfo != null && parameterInfo.Any(p => + !UtilityType.ConvertibleType.GetTypeInfo().IsAssignableFrom(p.ParameterType))) + pathItem.Post = CreateOperation(entry, methodInfo, schemaRegistry); + else + pathItem.Get = CreateOperation(entry, methodInfo, schemaRegistry); + } + return pathItem; + } + + /// + /// The CreatePathItems + /// + /// The apiDescriptions + /// The schemaRegistry + /// The + private Dictionary CreatePathItems( + IEnumerable apiDescriptions, ISchemaRegistry schemaRegistry) { - var supportedApiResponseTypes = apiDescription.SupportedResponseTypes - .DefaultIfEmpty(new ApiResponseType { StatusCode = 200 }); + return apiDescriptions + .OrderBy(p => p.RoutePath) + .GroupBy(apiDesc => apiDesc.Descriptor.RoutePath) + .ToDictionary(entry => + entry.Key.IndexOf("/") == 0 ? entry.Key : $"/{entry.Key}" + , entry => CreatePathItem(entry, schemaRegistry)); + } - return supportedApiResponseTypes - .ToDictionary( - apiResponseType => apiResponseType.IsDefaultResponse() ? "default" : apiResponseType.StatusCode.ToString(), - apiResponseType => CreateResponse(apiResponseType, schemaRegistry)); + /// + /// The CreatePathItems + /// + /// The apiDescriptions + /// The schemaRegistry + /// The + private Dictionary CreatePathItems( + IEnumerable apiDescriptions, + ISchemaRegistry schemaRegistry) + { + return apiDescriptions + .OrderBy(_options.SortKeySelector) + .GroupBy(apiDesc => apiDesc.RelativePathSansQueryString()) + .ToDictionary(group => "/" + group.Key, group => CreatePathItem(group, schemaRegistry)); } - private IDictionary CreateResponses( - ServiceEntry apiDescription, - MethodInfo methodInfo, - ISchemaRegistry schemaRegistry) + /// + /// The CreateProduces + /// + /// The apiDescription + /// The customAttributes + /// The + private IList CreateProduces(ApiDescription apiDescription, IEnumerable customAttributes) { - return new Dictionary { - { "200", CreateResponse(apiDescription,methodInfo, schemaRegistry) } - }; + var producesAttribute = customAttributes.OfType().FirstOrDefault(); + + var mediaTypes = (producesAttribute != null) + ? producesAttribute.ContentTypes + : apiDescription.SupportedResponseTypes + .SelectMany(apiResponseType => apiResponseType.ApiResponseFormats) + .Select(apiResponseFormat => apiResponseFormat.MediaType) + .Distinct(); + + return mediaTypes.ToList(); } - private Response CreateResponse(ServiceEntry apiResponseType,MethodInfo methodInfo, ISchemaRegistry schemaRegistry) + /// + /// The CreateResponse + /// + /// The apiResponseType + /// The schemaRegistry + /// The + private Response CreateResponse(ApiResponseType apiResponseType, ISchemaRegistry schemaRegistry) { var description = ResponseDescriptionMap - .FirstOrDefault((entry) => Regex.IsMatch("200", entry.Key)) + .FirstOrDefault((entry) => Regex.IsMatch(apiResponseType.StatusCode.ToString(), entry.Key)) .Value; return new Response { Description = description, - Schema = (methodInfo.ReturnType != typeof(Task) && methodInfo.ReturnType != typeof(void)) - ? schemaRegistry.GetOrRegister(typeof(HttpResultMessage<>).MakeGenericType(methodInfo.ReturnType.GenericTypeArguments)) + Schema = (apiResponseType.Type != null && apiResponseType.Type != typeof(void)) + ? schemaRegistry.GetOrRegister(apiResponseType.Type) : null }; } - private Response CreateResponse(ApiResponseType apiResponseType, ISchemaRegistry schemaRegistry) + /// + /// The CreateResponse + /// + /// The apiResponseType + /// The methodInfo + /// The schemaRegistry + /// The + private Response CreateResponse(ServiceEntry apiResponseType, MethodInfo methodInfo, ISchemaRegistry schemaRegistry) { var description = ResponseDescriptionMap - .FirstOrDefault((entry) => Regex.IsMatch(apiResponseType.StatusCode.ToString(), entry.Key)) + .FirstOrDefault((entry) => Regex.IsMatch("200", entry.Key)) .Value; return new Response { Description = description, - Schema = (apiResponseType.Type != null && apiResponseType.Type != typeof(void)) - ? schemaRegistry.GetOrRegister(apiResponseType.Type) + Schema = (methodInfo.ReturnType != typeof(Task) && methodInfo.ReturnType != typeof(void)) + ? schemaRegistry.GetOrRegister(typeof(HttpResultMessage<>).MakeGenericType(methodInfo.ReturnType.GenericTypeArguments)) : null }; } - private static Dictionary ParameterLocationMap = new Dictionary + /// + /// The CreateResponses + /// + /// The apiDescription + /// The schemaRegistry + /// The + private IDictionary CreateResponses( + ApiDescription apiDescription, + ISchemaRegistry schemaRegistry) { - { BindingSource.Form, "formData" }, - { BindingSource.FormFile, "formData" }, - { BindingSource.Body, "body" }, - { BindingSource.Header, "header" }, - { BindingSource.Path, "path" }, - { BindingSource.Query, "query" } - }; + var supportedApiResponseTypes = apiDescription.SupportedResponseTypes + .DefaultIfEmpty(new ApiResponseType { StatusCode = 200 }); - private static readonly Dictionary ResponseDescriptionMap = new Dictionary + return supportedApiResponseTypes + .ToDictionary( + apiResponseType => apiResponseType.IsDefaultResponse() ? "default" : apiResponseType.StatusCode.ToString(), + apiResponseType => CreateResponse(apiResponseType, schemaRegistry)); + } + + /// + /// The CreateResponses + /// + /// The apiDescription + /// The methodInfo + /// The schemaRegistry + /// The + private IDictionary CreateResponses( + ServiceEntry apiDescription, + MethodInfo methodInfo, + ISchemaRegistry schemaRegistry) { - { "1\\d{2}", "Information" }, - { "2\\d{2}", "Success" }, - { "3\\d{2}", "Redirect" }, - { "400", "Bad Request" }, - { "401", "Unauthorized" }, - { "403", "Forbidden" }, - { "404", "Not Found" }, - { "405", "Method Not Allowed" }, - { "406", "Not Acceptable" }, - { "408", "Request Timeout" }, - { "409", "Conflict" }, - { "4\\d{2}", "Client Error" }, - { "5\\d{2}", "Server Error" } - }; + return new Dictionary { + { "200", CreateResponse(apiDescription,methodInfo, schemaRegistry) } + }; + } + + /// + /// The CreateServiceKeyParameter + /// + /// The + private IParameter CreateServiceKeyParameter() + { + var nonBodyParam = new NonBodyParameter + { + Name = "servicekey", + In = "query", + Required = false, + }; + var schema = new Schema(); + schema.Description = "ServiceKey"; + nonBodyParam.PopulateFrom(schema); + return nonBodyParam; + } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SwaggerGeneratorOptions.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SwaggerGeneratorOptions.cs index 10d95778d..9100e8ec0 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SwaggerGeneratorOptions.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/SwaggerGeneratorOptions.cs @@ -1,22 +1,30 @@ -using System; +using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Surging.Core.CPlatform.Runtime.Server; +using Surging.Core.Swagger; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; -using Microsoft.AspNetCore.Mvc.ApiExplorer; -using Surging.Core.CPlatform.Runtime.Server; -using Surging.Core.Swagger; namespace Surging.Core.SwaggerGen { + /// + /// Defines the + /// public class SwaggerGeneratorOptions { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public SwaggerGeneratorOptions() { SwaggerDocs = new Dictionary(); DocInclusionPredicate = DefaultDocInclusionPredicate; DocInclusionPredicateV2 = DefaultDocInclusionPredicateV2; OperationIdSelector = DefaultOperationIdSelector; - TagsSelector = DefaultTagsSelector; + TagsSelector = DefaultTagsSelector; SortKeySelector = DefaultSortKeySelector; SecurityDefinitions = new Dictionary(); SecurityRequirements = new List>>(); @@ -25,39 +33,101 @@ public SwaggerGeneratorOptions() DocumentFilters = new List(); } - public IDictionary SwaggerDocs { get; set; } + #endregion 构造函数 + + #region 属性 + /// + /// Gets or sets the ConflictingActionsResolver + /// + public Func, ApiDescription> ConflictingActionsResolver { get; set; } + + /// + /// Gets or sets a value indicating whether DescribeAllParametersInCamelCase + /// + public bool DescribeAllParametersInCamelCase { get; set; } + + /// + /// Gets or sets the DocInclusionPredicate + /// public Func DocInclusionPredicate { get; set; } + /// + /// Gets or sets the DocInclusionPredicateV2 + /// public Func DocInclusionPredicateV2 { get; set; } + /// + /// Gets or sets the DocumentFilters + /// + public IList DocumentFilters { get; set; } + + /// + /// Gets or sets a value indicating whether IgnoreObsoleteActions + /// public bool IgnoreObsoleteActions { get; set; } - public Func, ApiDescription> ConflictingActionsResolver { get; set; } + /// + /// Gets or sets the OperationFilters + /// + public List OperationFilters { get; set; } + /// + /// Gets or sets the OperationIdSelector + /// public Func OperationIdSelector { get; set; } - public Func> TagsSelector { get; set; } - - public Func SortKeySelector { get; set; } - - public bool DescribeAllParametersInCamelCase { get; set; } + /// + /// Gets or sets the ParameterFilters + /// + public IList ParameterFilters { get; set; } + /// + /// Gets or sets the SecurityDefinitions + /// public IDictionary SecurityDefinitions { get; set; } + /// + /// Gets or sets the SecurityRequirements + /// public IList>> SecurityRequirements { get; set; } - public IList ParameterFilters { get; set; } + /// + /// Gets or sets the SortKeySelector + /// + public Func SortKeySelector { get; set; } - public List OperationFilters { get; set; } + /// + /// Gets or sets the SwaggerDocs + /// + public IDictionary SwaggerDocs { get; set; } - public IList DocumentFilters { get; set; } + /// + /// Gets or sets the TagsSelector + /// + public Func> TagsSelector { get; set; } + + #endregion 属性 + + #region 方法 + /// + /// The DefaultDocInclusionPredicate + /// + /// The documentName + /// The apiDescription + /// The private bool DefaultDocInclusionPredicate(string documentName, ApiDescription apiDescription) { return apiDescription.GroupName == null || apiDescription.GroupName == documentName; } + /// + /// The DefaultDocInclusionPredicateV2 + /// + /// The documentName + /// The apiDescription + /// The private bool DefaultDocInclusionPredicateV2(string documentName, ServiceEntry apiDescription) { var assembly = apiDescription.Type.Assembly; @@ -65,9 +135,14 @@ private bool DefaultDocInclusionPredicateV2(string documentName, ServiceEntry ap var versions = assembly .GetCustomAttributes(true) .OfType(); - return versions != null; + return versions != null; } + /// + /// The DefaultOperationIdSelector + /// + /// The apiDescription + /// The private string DefaultOperationIdSelector(ApiDescription apiDescription) { var routeName = apiDescription.ActionDescriptor.AttributeRouteInfo?.Name; @@ -78,14 +153,26 @@ private string DefaultOperationIdSelector(ApiDescription apiDescription) return null; } - private IList DefaultTagsSelector(ApiDescription apiDescription) + /// + /// The DefaultSortKeySelector + /// + /// The apiDescription + /// The + private string DefaultSortKeySelector(ApiDescription apiDescription) { - return new[] { apiDescription.ActionDescriptor.RouteValues["controller"] }; + return TagsSelector(apiDescription).First(); } - private string DefaultSortKeySelector(ApiDescription apiDescription) + /// + /// The DefaultTagsSelector + /// + /// The apiDescription + /// The + private IList DefaultTagsSelector(ApiDescription apiDescription) { - return TagsSelector(apiDescription).First(); + return new[] { apiDescription.ActionDescriptor.RouteValues["controller"] }; } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/TypeExtensions.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/TypeExtensions.cs index 995919fdb..eaf70e31a 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/TypeExtensions.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/Generator/TypeExtensions.cs @@ -5,8 +5,19 @@ namespace Surging.Core.SwaggerGen { + /// + /// Defines the + /// public static class TypeExtensions { + #region 方法 + + /// + /// The FriendlyId + /// + /// The type + /// The fullyQualified + /// The public static string FriendlyId(this Type type, bool fullyQualified = false) { var typeName = fullyQualified @@ -28,16 +39,31 @@ public static string FriendlyId(this Type type, bool fullyQualified = false) return typeName; } - internal static bool IsNullable(this Type type) + /// + /// The IsFSharpOption + /// + /// The type + /// The + internal static bool IsFSharpOption(this Type type) { - return Nullable.GetUnderlyingType(type) != null; + return type.FullNameSansTypeArguments() == "Microsoft.FSharp.Core.FSharpOption`1"; } - internal static bool IsFSharpOption(this Type type) + /// + /// The IsNullable + /// + /// The type + /// The + internal static bool IsNullable(this Type type) { - return type.FullNameSansTypeArguments() == "Microsoft.FSharp.Core.FSharpOption`1"; + return Nullable.GetUnderlyingType(type) != null; } + /// + /// The FullNameSansTypeArguments + /// + /// The type + /// The private static string FullNameSansTypeArguments(this Type type) { if (string.IsNullOrEmpty(type.FullName)) return string.Empty; @@ -46,5 +72,7 @@ private static string FullNameSansTypeArguments(this Type type) var chopIndex = fullName.IndexOf("[["); return (chopIndex == -1) ? fullName : fullName.Substring(0, chopIndex); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/XmlComments/XmlCommentsDocumentFilter.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/XmlComments/XmlCommentsDocumentFilter.cs index 0bddfd9a9..2d7c5d5fb 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/XmlComments/XmlCommentsDocumentFilter.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/XmlComments/XmlCommentsDocumentFilter.cs @@ -1,23 +1,59 @@ -using System.Xml.XPath; -using System.Linq; -using Microsoft.AspNetCore.Mvc.Controllers; -using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc.Controllers; using Surging.Core.Swagger; +using System.Collections.Generic; +using System.Linq; +using System.Xml.XPath; namespace Surging.Core.SwaggerGen { + /// + /// Defines the + /// public class XmlCommentsDocumentFilter : IDocumentFilter { + #region 常量 + + /// + /// Defines the MemberXPath + /// private const string MemberXPath = "/doc/members/member[@name='{0}']"; + + /// + /// Defines the SummaryTag + /// private const string SummaryTag = "summary"; + #endregion 常量 + + #region 字段 + + /// + /// Defines the _xmlNavigator + /// private readonly XPathNavigator _xmlNavigator; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The xmlDoc public XmlCommentsDocumentFilter(XPathDocument xmlDoc) { _xmlNavigator = xmlDoc.CreateNavigator(); } + #endregion 构造函数 + + #region 方法 + + /// + /// The Apply + /// + /// The swaggerDoc + /// The context public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context) { // Collect (unique) controller names and types in a dictionary @@ -49,5 +85,7 @@ public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context) } } } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/XmlComments/XmlCommentsMemberNameHelper.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/XmlComments/XmlCommentsMemberNameHelper.cs index 6a79b76bb..7a661b116 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/XmlComments/XmlCommentsMemberNameHelper.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/XmlComments/XmlCommentsMemberNameHelper.cs @@ -5,8 +5,32 @@ namespace Surging.Core.SwaggerGen { + /// + /// Defines the + /// public class XmlCommentsMemberNameHelper { + #region 方法 + + /// + /// The GetMemberNameForMember + /// + /// The memberInfo + /// The + public static string GetMemberNameForMember(MemberInfo memberInfo) + { + var builder = new StringBuilder(((memberInfo.MemberType & MemberTypes.Field) != 0) ? "F:" : "P:"); + builder.Append(QualifiedNameFor(memberInfo.DeclaringType)); + builder.Append($".{memberInfo.Name}"); + + return builder.ToString(); + } + + /// + /// The GetMemberNameForMethod + /// + /// The method + /// The public static string GetMemberNameForMethod(MethodInfo method) { var builder = new StringBuilder("M:"); @@ -29,6 +53,11 @@ public static string GetMemberNameForMethod(MethodInfo method) return builder.ToString(); } + /// + /// The GetMemberNameForType + /// + /// The type + /// The public static string GetMemberNameForType(Type type) { var builder = new StringBuilder("T:"); @@ -37,15 +66,12 @@ public static string GetMemberNameForType(Type type) return builder.ToString(); } - public static string GetMemberNameForMember(MemberInfo memberInfo) - { - var builder = new StringBuilder(((memberInfo.MemberType & MemberTypes.Field) != 0) ? "F:" : "P:"); - builder.Append(QualifiedNameFor(memberInfo.DeclaringType)); - builder.Append($".{memberInfo.Name}"); - - return builder.ToString(); - } - + /// + /// The QualifiedNameFor + /// + /// The type + /// The expandGenericArgs + /// The private static string QualifiedNameFor(Type type, bool expandGenericArgs = false) { if (type.IsArray) @@ -80,5 +106,7 @@ private static string QualifiedNameFor(Type type, bool expandGenericArgs = false return builder.ToString(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/XmlComments/XmlCommentsOperationFilter.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/XmlComments/XmlCommentsOperationFilter.cs index e2e4a9591..17d6c48c1 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/XmlComments/XmlCommentsOperationFilter.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/XmlComments/XmlCommentsOperationFilter.cs @@ -1,29 +1,77 @@ -using System; +using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Surging.Core.CPlatform.Runtime.Server; +using Surging.Core.Swagger; +using System; +using System.Collections.Generic; using System.Linq; -using System.Xml.XPath; using System.Reflection; -using System.Collections.Generic; -using Microsoft.AspNetCore.Mvc.ApiExplorer; -using Surging.Core.Swagger; -using Surging.Core.CPlatform.Runtime.Server; +using System.Xml.XPath; namespace Surging.Core.SwaggerGen { + /// + /// Defines the + /// public class XmlCommentsOperationFilter : IOperationFilter { + #region 常量 + + /// + /// Defines the MemberXPath + /// private const string MemberXPath = "/doc/members/member[@name='{0}']"; - private const string SummaryXPath = "summary"; - private const string RemarksXPath = "remarks"; + + /// + /// Defines the ParamXPath + /// private const string ParamXPath = "param[@name='{0}']"; + + /// + /// Defines the RemarksXPath + /// + private const string RemarksXPath = "remarks"; + + /// + /// Defines the ResponsesXPath + /// private const string ResponsesXPath = "response"; + /// + /// Defines the SummaryXPath + /// + private const string SummaryXPath = "summary"; + + #endregion 常量 + + #region 字段 + + /// + /// Defines the _xmlNavigator + /// private readonly XPathNavigator _xmlNavigator; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The xmlDoc public XmlCommentsOperationFilter(XPathDocument xmlDoc) { _xmlNavigator = xmlDoc.CreateNavigator(); } + #endregion 构造函数 + + #region 方法 + + /// + /// The Apply + /// + /// The operation + /// The context public void Apply(Operation operation, OperationFilterContext context) { if (context.MethodInfo == null) return; @@ -45,31 +93,18 @@ public void Apply(Operation operation, OperationFilterContext context) ApplyResponsesXmlToResponses(operation.Responses, methodNode.Select(ResponsesXPath)); } - if(context.ApiDescription !=null) - // Special handling for parameters that are bound to model properties - ApplyPropertiesXmlToPropertyParameters(operation.Parameters, context.ApiDescription); + if (context.ApiDescription != null) + // Special handling for parameters that are bound to model properties + ApplyPropertiesXmlToPropertyParameters(operation.Parameters, context.ApiDescription); else ApplyPropertiesXmlToPropertyParameters(operation.Parameters, context.ServiceEntry); } - private MethodInfo GetGenericTypeMethodOrNullFor(MethodInfo constructedTypeMethod) - { - var constructedType = constructedTypeMethod.DeclaringType; - var genericTypeDefinition = constructedType.GetGenericTypeDefinition(); - - // Retrieve list of candidate methods that match name and parameter count - var candidateMethods = genericTypeDefinition.GetMethods() - .Where(m => - { - return (m.Name == constructedTypeMethod.Name) - && (m.GetParameters().Length == constructedTypeMethod.GetParameters().Length); - }); - - - // If inconclusive, just return null - return (candidateMethods.Count() == 1) ? candidateMethods.First() : null; - } - + /// + /// The ApplyMethodXmlToOperation + /// + /// The operation + /// The methodNode private void ApplyMethodXmlToOperation(Operation operation, XPathNavigator methodNode) { var summaryNode = methodNode.SelectSingleNode(SummaryXPath); @@ -81,6 +116,12 @@ private void ApplyMethodXmlToOperation(Operation operation, XPathNavigator metho operation.Description = XmlCommentsTextHelper.Humanize(remarksNode.InnerXml); } + /// + /// The ApplyParamsXmlToActionParameters + /// + /// The parameters + /// The methodNode + /// The apiDescription private void ApplyParamsXmlToActionParameters( IList parameters, XPathNavigator methodNode, @@ -102,6 +143,12 @@ private void ApplyParamsXmlToActionParameters( } } + /// + /// The ApplyParamsXmlToActionParameters + /// + /// The parameters + /// The methodNode + /// The serviceEntry private void ApplyParamsXmlToActionParameters( IList parameters, XPathNavigator methodNode, @@ -123,19 +170,11 @@ private void ApplyParamsXmlToActionParameters( } } - private void ApplyResponsesXmlToResponses(IDictionary responses, XPathNodeIterator responseNodes) - { - while (responseNodes.MoveNext()) - { - var code = responseNodes.Current.GetAttribute("code", ""); - var response = responses.ContainsKey(code) - ? responses[code] - : responses[code] = new Response(); - - response.Description = XmlCommentsTextHelper.Humanize(responseNodes.Current.InnerXml); - } - } - + /// + /// The ApplyPropertiesXmlToPropertyParameters + /// + /// The parameters + /// The apiDescription private void ApplyPropertiesXmlToPropertyParameters( IList parameters, ApiDescription apiDescription) @@ -164,6 +203,11 @@ private void ApplyPropertiesXmlToPropertyParameters( } } + /// + /// The ApplyPropertiesXmlToPropertyParameters + /// + /// The parameters + /// The serviceEntry private void ApplyPropertiesXmlToPropertyParameters( IList parameters, ServiceEntry serviceEntry) @@ -173,10 +217,10 @@ private void ApplyPropertiesXmlToPropertyParameters( foreach (var parameter in parameters) { var methodInfo = serviceEntry.Type.GetTypeInfo().DeclaredMethods.Where(p => p.Name == serviceEntry.MethodName).FirstOrDefault(); - var propertyParam = methodInfo.GetParameters() + var propertyParam = methodInfo.GetParameters() .FirstOrDefault(p => parameter.Name.Equals(p.Name, StringComparison.OrdinalIgnoreCase)); - if (propertyParam == null) continue; - var memberInfo = propertyParam.Member ; + if (propertyParam == null) continue; + var memberInfo = propertyParam.Member; if (memberInfo == null) continue; var memberName = XmlCommentsMemberNameHelper.GetMemberNameForMember(memberInfo); @@ -188,5 +232,47 @@ private void ApplyPropertiesXmlToPropertyParameters( parameter.Description = XmlCommentsTextHelper.Humanize(summaryNode.InnerXml); } } + + /// + /// The ApplyResponsesXmlToResponses + /// + /// The responses + /// The responseNodes + private void ApplyResponsesXmlToResponses(IDictionary responses, XPathNodeIterator responseNodes) + { + while (responseNodes.MoveNext()) + { + var code = responseNodes.Current.GetAttribute("code", ""); + var response = responses.ContainsKey(code) + ? responses[code] + : responses[code] = new Response(); + + response.Description = XmlCommentsTextHelper.Humanize(responseNodes.Current.InnerXml); + } + } + + /// + /// The GetGenericTypeMethodOrNullFor + /// + /// The constructedTypeMethod + /// The + private MethodInfo GetGenericTypeMethodOrNullFor(MethodInfo constructedTypeMethod) + { + var constructedType = constructedTypeMethod.DeclaringType; + var genericTypeDefinition = constructedType.GetGenericTypeDefinition(); + + // Retrieve list of candidate methods that match name and parameter count + var candidateMethods = genericTypeDefinition.GetMethods() + .Where(m => + { + return (m.Name == constructedTypeMethod.Name) + && (m.GetParameters().Length == constructedTypeMethod.GetParameters().Length); + }); + + // If inconclusive, just return null + return (candidateMethods.Count() == 1) ? candidateMethods.First() : null; + } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/XmlComments/XmlCommentsSchemaFilter.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/XmlComments/XmlCommentsSchemaFilter.cs index d9b21b67f..fb5cdee33 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/XmlComments/XmlCommentsSchemaFilter.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/XmlComments/XmlCommentsSchemaFilter.cs @@ -1,23 +1,63 @@ -using System.Xml.XPath; -using System.Reflection; -using Newtonsoft.Json.Serialization; +using Newtonsoft.Json.Serialization; using Surging.Core.Swagger; +using System.Reflection; +using System.Xml.XPath; namespace Surging.Core.SwaggerGen { + /// + /// Defines the + /// public class XmlCommentsSchemaFilter : ISchemaFilter { + #region 常量 + + /// + /// Defines the ExampleXPath + /// + private const string ExampleXPath = "example"; + + /// + /// Defines the MemberXPath + /// private const string MemberXPath = "/doc/members/member[@name='{0}']"; + + /// + /// Defines the SummaryTag + /// private const string SummaryTag = "summary"; - private const string ExampleXPath = "example"; + #endregion 常量 + + #region 字段 + + /// + /// Defines the _xmlNavigator + /// private readonly XPathNavigator _xmlNavigator; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The xmlDoc public XmlCommentsSchemaFilter(XPathDocument xmlDoc) { _xmlNavigator = xmlDoc.CreateNavigator(); } + #endregion 构造函数 + + #region 方法 + + /// + /// The Apply + /// + /// The schema + /// The context public void Apply(Schema schema, SchemaFilterContext context) { var jsonObjectContract = context.JsonContract as JsonObjectContract; @@ -46,6 +86,11 @@ public void Apply(Schema schema, SchemaFilterContext context) } } + /// + /// The ApplyPropertyComments + /// + /// The propertySchema + /// The memberInfo private void ApplyPropertyComments(Schema propertySchema, MemberInfo memberInfo) { var memberName = XmlCommentsMemberNameHelper.GetMemberNameForMember(memberInfo); @@ -62,5 +107,7 @@ private void ApplyPropertyComments(Schema propertySchema, MemberInfo memberInfo) if (exampleNode != null) propertySchema.Example = XmlCommentsTextHelper.Humanize(exampleNode.InnerXml); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/XmlComments/XmlCommentsTextHelper.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/XmlComments/XmlCommentsTextHelper.cs index c36cb773e..f337b57cd 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/XmlComments/XmlCommentsTextHelper.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerGen/XmlComments/XmlCommentsTextHelper.cs @@ -4,11 +4,32 @@ namespace Surging.Core.SwaggerGen { + /// + /// Defines the + /// public static class XmlCommentsTextHelper { - private static Regex RefTagPattern = new Regex(@"<(see|paramref) (name|cref)=""([TPF]{1}:)?(?.+?)"" ?/>"); + #region 字段 + + /// + /// Defines the CodeTagPattern + /// private static Regex CodeTagPattern = new Regex(@"(?.+?)"); + /// + /// Defines the RefTagPattern + /// + private static Regex RefTagPattern = new Regex(@"<(see|paramref) (name|cref)=""([TPF]{1}:)?(?.+?)"" ?/>"); + + #endregion 字段 + + #region 方法 + + /// + /// The Humanize + /// + /// The text + /// The public static string Humanize(string text) { if (text == null) @@ -20,29 +41,11 @@ public static string Humanize(string text) .HumanizeCodeTags(); } - private static string NormalizeIndentation(this string text) - { - string[] lines = text.Split('\n'); - string padding = GetCommonLeadingWhitespace(lines); - - int padLen = padding == null ? 0 : padding.Length; - - // remove leading padding from each line - for (int i = 0, l = lines.Length; i < l; ++i) - { - string line = lines[i].TrimEnd('\r'); // remove trailing '\r' - - if (padLen != 0 && line.Length >= padLen && line.Substring(0, padLen) == padding) - line = line.Substring(padLen); - - lines[i] = line; - } - - // remove leading empty lines, but not all leading padding - // remove all trailing whitespace, regardless - return string.Join("\r\n", lines.SkipWhile(x => string.IsNullOrWhiteSpace(x))).TrimEnd(); - } - + /// + /// The GetCommonLeadingWhitespace + /// + /// The lines + /// The private static string GetCommonLeadingWhitespace(string[] lines) { if (null == lines) @@ -79,15 +82,54 @@ private static string GetCommonLeadingWhitespace(string[] lines) return null; } + /// + /// The HumanizeCodeTags + /// + /// The text + /// The + private static string HumanizeCodeTags(this string text) + { + return CodeTagPattern.Replace(text, (match) => "{" + match.Groups["display"].Value + "}"); + } + + /// + /// The HumanizeRefTags + /// + /// The text + /// The private static string HumanizeRefTags(this string text) { return RefTagPattern.Replace(text, (match) => match.Groups["display"].Value); } - private static string HumanizeCodeTags(this string text) + /// + /// The NormalizeIndentation + /// + /// The text + /// The + private static string NormalizeIndentation(this string text) { - return CodeTagPattern.Replace(text, (match) => "{" + match.Groups["display"].Value + "}"); + string[] lines = text.Split('\n'); + string padding = GetCommonLeadingWhitespace(lines); + + int padLen = padding == null ? 0 : padding.Length; + + // remove leading padding from each line + for (int i = 0, l = lines.Length; i < l; ++i) + { + string line = lines[i].TrimEnd('\r'); // remove trailing '\r' + + if (padLen != 0 && line.Length >= padLen && line.Substring(0, padLen) == padding) + line = line.Substring(padLen); + + lines[i] = line; + } + + // remove leading empty lines, but not all leading padding + // remove all trailing whitespace, regardless + return string.Join("\r\n", lines.SkipWhile(x => string.IsNullOrWhiteSpace(x))).TrimEnd(); } + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerModule.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerModule.cs index 9d1ccb63d..8f4f7d80e 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerModule.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerModule.cs @@ -16,18 +16,31 @@ namespace Surging.Core.Swagger { - public class SwaggerModule: KestrelHttpModule + /// + /// Defines the + /// + public class SwaggerModule : KestrelHttpModule { - private IServiceSchemaProvider _serviceSchemaProvider; - private IServiceEntryProvider _serviceEntryProvider; + #region 字段 - public override void Initialize(AppModuleContext context) - { - var serviceProvider = context.ServiceProvoider; - _serviceSchemaProvider = serviceProvider.GetInstances(); - _serviceEntryProvider = serviceProvider.GetInstances(); - } + /// + /// Defines the _serviceEntryProvider + /// + private IServiceEntryProvider _serviceEntryProvider; + /// + /// Defines the _serviceSchemaProvider + /// + private IServiceSchemaProvider _serviceSchemaProvider; + + #endregion 字段 + + #region 方法 + + /// + /// The Initialize + /// + /// The context public override void Initialize(ApplicationInitializationContext context) { var info = AppConfig.SwaggerConfig.Info == null @@ -44,6 +57,21 @@ public override void Initialize(ApplicationInitializationContext context) } } + /// + /// The Initialize + /// + /// The context + public override void Initialize(AppModuleContext context) + { + var serviceProvider = context.ServiceProvoider; + _serviceSchemaProvider = serviceProvider.GetInstances(); + _serviceEntryProvider = serviceProvider.GetInstances(); + } + + /// + /// The RegisterBuilder + /// + /// The context public override void RegisterBuilder(ConfigurationContext context) { var serviceCollection = context.Services; @@ -54,7 +82,6 @@ public override void RegisterBuilder(ConfigurationContext context) { serviceCollection.AddSwaggerGen(options => { - options.SwaggerDoc(info.Version, info); if (swaggerOptions != null && swaggerOptions.IgnoreFullyQualified) options.IgnoreFullyQualified(); @@ -91,7 +118,8 @@ protected override void RegisterBuilder(ContainerBuilderWrapper builder) AppConfig.SwaggerConfig = section.Get(); } builder.RegisterType(typeof(DefaultServiceSchemaProvider)).As(typeof(IServiceSchemaProvider)).SingleInstance(); - } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerUI/SwaggerUIBuilderExtensions.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerUI/SwaggerUIBuilderExtensions.cs index faab0af90..1eb65d7a7 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerUI/SwaggerUIBuilderExtensions.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerUI/SwaggerUIBuilderExtensions.cs @@ -3,8 +3,19 @@ namespace Surging.Core.Swagger.SwaggerUI { - public static class SwaggerUIBuilderExtensions + /// + /// Defines the + /// + public static class SwaggerUIBuilderExtensions { + #region 方法 + + /// + /// The UseSwaggerUI + /// + /// The app + /// The setupAction + /// The public static IApplicationBuilder UseSwaggerUI( this IApplicationBuilder app, Action setupAction = null) @@ -25,5 +36,7 @@ public static IApplicationBuilder UseSwaggerUI( return app; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerUI/SwaggerUIMiddleware.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerUI/SwaggerUIMiddleware.cs index 61dcd456c..93c40902e 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerUI/SwaggerUIMiddleware.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerUI/SwaggerUIMiddleware.cs @@ -19,21 +19,59 @@ namespace Surging.Core.Swagger.SwaggerUI { - public class SwaggerUIMiddleware + /// + /// Defines the + /// + public class SwaggerUIMiddleware { + #region 常量 + + /// + /// Defines the EmbeddedFileNamespace + /// private const string EmbeddedFileNamespace = "Surging.Core.Swagger.SwaggerUI.node_modules.swagger_ui_dist"; + #endregion 常量 + + #region 字段 + + /// + /// Defines the _options + /// private readonly SwaggerUIOptions _options; + + /// + /// Defines the _staticFileMiddleware + /// private readonly StaticFileMiddleware _staticFileMiddleware; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The next + /// The hostingEnv + /// The loggerFactory + /// The optionsAccessor public SwaggerUIMiddleware( RequestDelegate next, IHostingEnvironment hostingEnv, ILoggerFactory loggerFactory, IOptions optionsAccessor) : this(next, hostingEnv, loggerFactory, optionsAccessor.Value) - { } + { + } + /// + /// Initializes a new instance of the class. + /// + /// The next + /// The hostingEnv + /// The loggerFactory + /// The options public SwaggerUIMiddleware( RequestDelegate next, IHostingEnvironment hostingEnv, @@ -44,6 +82,15 @@ public SwaggerUIMiddleware( _staticFileMiddleware = CreateStaticFileMiddleware(next, hostingEnv, loggerFactory, options); } + #endregion 构造函数 + + #region 方法 + + /// + /// The Invoke + /// + /// The httpContext + /// The public async Task Invoke(HttpContext httpContext) { var httpMethod = httpContext.Request.Method; @@ -70,12 +117,49 @@ public async Task Invoke(HttpContext httpContext) await _staticFileMiddleware.Invoke(httpContext); } - private void RespondWithRedirect(HttpResponse response, string location) + /// + /// The CreateStaticFileMiddleware + /// + /// The next + /// The hostingEnv + /// The loggerFactory + /// The options + /// The + private StaticFileMiddleware CreateStaticFileMiddleware( + RequestDelegate next, + IHostingEnvironment hostingEnv, + ILoggerFactory loggerFactory, + SwaggerUIOptions options) { - response.StatusCode = 301; - response.Headers["Location"] = location; + var staticFileOptions = new StaticFileOptions + { + RequestPath = string.IsNullOrEmpty(options.RoutePrefix) ? string.Empty : $"/{options.RoutePrefix}", + FileProvider = new EmbeddedFileProvider(typeof(SwaggerUIMiddleware).GetTypeInfo().Assembly, EmbeddedFileNamespace), + }; + + return new StaticFileMiddleware(next, hostingEnv, Options.Create(staticFileOptions), loggerFactory); } + /// + /// The GetIndexArguments + /// + /// The + private IDictionary GetIndexArguments() + { + return new Dictionary() + { + { "%(DocumentTitle)", _options.DocumentTitle }, + { "%(HeadContent)", _options.HeadContent }, + { "%(ConfigObject)", SerializeToJson(_options.ConfigObject) }, + { "%(OAuthConfigObject)", SerializeToJson(_options.OAuthConfigObject) } + }; + } + + /// + /// The RespondWithIndexHtml + /// + /// The response + /// The private async Task RespondWithIndexHtml(HttpResponse response) { response.StatusCode = 200; @@ -94,17 +178,22 @@ private async Task RespondWithIndexHtml(HttpResponse response) } } - private IDictionary GetIndexArguments() + /// + /// The RespondWithRedirect + /// + /// The response + /// The location + private void RespondWithRedirect(HttpResponse response, string location) { - return new Dictionary() - { - { "%(DocumentTitle)", _options.DocumentTitle }, - { "%(HeadContent)", _options.HeadContent }, - { "%(ConfigObject)", SerializeToJson(_options.ConfigObject) }, - { "%(OAuthConfigObject)", SerializeToJson(_options.OAuthConfigObject) } - }; + response.StatusCode = 301; + response.Headers["Location"] = location; } + /// + /// The SerializeToJson + /// + /// The obj + /// The private string SerializeToJson(object obj) { return JsonConvert.SerializeObject(obj, new JsonSerializerSettings @@ -117,19 +206,6 @@ private string SerializeToJson(object obj) }); } - private StaticFileMiddleware CreateStaticFileMiddleware( - RequestDelegate next, - IHostingEnvironment hostingEnv, - ILoggerFactory loggerFactory, - SwaggerUIOptions options) - { - var staticFileOptions = new StaticFileOptions - { - RequestPath = string.IsNullOrEmpty(options.RoutePrefix) ? string.Empty : $"/{options.RoutePrefix}", - FileProvider = new EmbeddedFileProvider(typeof(SwaggerUIMiddleware).GetTypeInfo().Assembly, EmbeddedFileNamespace), - }; - - return new StaticFileMiddleware(next, hostingEnv, Options.Create(staticFileOptions), loggerFactory); - } + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerUI/SwaggerUIOptions.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerUI/SwaggerUIOptions.cs index 19e89b383..272f0d870 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerUI/SwaggerUIOptions.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerUI/SwaggerUIOptions.cs @@ -8,85 +8,156 @@ namespace Surging.Core.Swagger.SwaggerUI { - public class SwaggerUIOptions + #region 枚举 + + /// + /// Defines the ModelRendering + /// + public enum ModelRendering { /// - /// Gets or sets a route prefix for accessing the swagger-ui + /// Defines the Example /// - public string RoutePrefix { get; set; } = "swagger"; + Example, /// - /// Gets or sets a Stream function for retrieving the swagger-ui page + /// Defines the Model /// - public Func IndexStream { get; set; } = () => typeof(SwaggerUIOptions).GetTypeInfo().Assembly - .GetManifestResourceStream("Surging.Core.Swagger.SwaggerUI.index.html"); + Model + } + /// + /// Defines the DocExpansion + /// + public enum DocExpansion + { /// - /// Gets or sets a title for the swagger-ui page + /// Defines the List /// - public string DocumentTitle { get; set; } = "Swagger UI"; + List, /// - /// Gets or sets additional content to place in the head of the swagger-ui page + /// Defines the Full /// - public string HeadContent { get; set; } = ""; + Full, /// - /// Gets the JavaScript config object, represented as JSON, that will be passed to the SwaggerUI + /// Defines the None /// - public ConfigObject ConfigObject { get; set; } = new ConfigObject(); + None + } + + /// + /// Defines the SubmitMethod + /// + public enum SubmitMethod + { + /// + /// Defines the Get + /// + Get, /// - /// Gets the JavaScript config object, represented as JSON, that will be passed to the initOAuth method + /// Defines the Put /// - public OAuthConfigObject OAuthConfigObject { get; set; } = new OAuthConfigObject(); - } + Put, - public class ConfigObject - { /// - /// One or more Swagger JSON endpoints (url and name) to power the UI + /// Defines the Post /// - public IEnumerable Urls { get; set; } = null; + Post, /// - /// If set to true, enables deep linking for tags and operations + /// Defines the Delete /// - public bool DeepLinking { get; set; } = false; + Delete, /// - /// Controls the display of operationId in operations list + /// Defines the Options /// - public bool DisplayOperationId { get; set; } = false; + Options, /// - /// The default expansion depth for models (set to -1 completely hide the models) + /// Defines the Head /// - public int DefaultModelsExpandDepth { get; set; } = 1; + Head, /// + /// Defines the Patch + /// + Patch, + + /// + /// Defines the Trace + /// + Trace + } + + #endregion 枚举 + + /// + /// Defines the + /// + public class ConfigObject + { + #region 字段 + + /// + /// Defines the AdditionalItems + /// + [JsonExtensionData] + public Dictionary AdditionalItems = new Dictionary(); + + #endregion 字段 + + #region 属性 + + /// + /// Gets or sets a value indicating whether DeepLinking + /// If set to true, enables deep linking for tags and operations + /// + public bool DeepLinking { get; set; } = false; + + /// + /// Gets or sets the DefaultModelExpandDepth /// The default expansion depth for the model on the model-example section /// public int DefaultModelExpandDepth { get; set; } = 1; /// + /// Gets or sets the DefaultModelRendering /// Controls how the model is shown when the API is first rendered. /// (The user can always switch the rendering for a given model by clicking the 'Model' and 'Example Value' links) /// public ModelRendering DefaultModelRendering { get; set; } = ModelRendering.Example; /// + /// Gets or sets the DefaultModelsExpandDepth + /// The default expansion depth for models (set to -1 completely hide the models) + /// + public int DefaultModelsExpandDepth { get; set; } = 1; + + /// + /// Gets or sets a value indicating whether DisplayOperationId + /// Controls the display of operationId in operations list + /// + public bool DisplayOperationId { get; set; } = false; + + /// + /// Gets or sets a value indicating whether DisplayRequestDuration /// Controls the display of the request duration (in milliseconds) for Try-It-Out requests /// public bool DisplayRequestDuration { get; set; } = false; /// + /// Gets or sets the DocExpansion /// Controls the default expansion setting for the operations and tags. /// It can be 'list' (expands only the tags), 'full' (expands the tags and operations) or 'none' (expands nothing) /// public DocExpansion DocExpansion { get; set; } = DocExpansion.List; /// + /// Gets or sets the Filter /// If set, enables filtering. The top bar will show an edit box that you can use to filter the tagged operations /// that are shown. Can be an empty string or specific value, in which case filtering will be enabled using that /// value as the filter expression. Filtering is case sensitive matching the filter expression anywhere inside the tag @@ -94,27 +165,32 @@ public class ConfigObject public string Filter { get; set; } = null; /// + /// Gets or sets the MaxDisplayedTags /// If set, limits the number of tagged operations displayed to at most this many. The default is to show all operations /// public int? MaxDisplayedTags { get; set; } = null; /// - /// Controls the display of vendor extension (x-) fields and values for Operations, Parameters, and Schema + /// Gets or sets the OAuth2RedirectUrl + /// OAuth redirect URL /// - public bool ShowExtensions { get; set; } = false; + [JsonProperty("oauth2RedirectUrl")] + public string OAuth2RedirectUrl { get; set; } = null; /// + /// Gets or sets a value indicating whether ShowCommonExtensions /// Controls the display of extensions (pattern, maxLength, minLength, maximum, minimum) fields and values for Parameters /// public bool ShowCommonExtensions { get; set; } = false; /// - /// OAuth redirect URL + /// Gets or sets a value indicating whether ShowExtensions + /// Controls the display of vendor extension (x-) fields and values for Operations, Parameters, and Schema /// - [JsonProperty("oauth2RedirectUrl")] - public string OAuth2RedirectUrl { get; set; } = null; + public bool ShowExtensions { get; set; } = false; /// + /// Gets or sets the SupportedSubmitMethods /// List of HTTP methods that have the Try it out feature enabled. /// An empty array disables Try it out for all operations. This does not filter the operations from the display /// @@ -122,6 +198,13 @@ public class ConfigObject public IEnumerable SupportedSubmitMethods { get; set; } = Enum.GetValues(typeof(SubmitMethod)).Cast(); /// + /// Gets or sets the Urls + /// One or more Swagger JSON endpoints (url and name) to power the UI + /// + public IEnumerable Urls { get; set; } = null; + + /// + /// Gets or sets the ValidatorUrl /// By default, Swagger-UI attempts to validate specs against swagger.io's online validator. /// You can use this parameter to set a different validator URL, for example for locally deployed validators (Validator Badge). /// Setting it to null will disable validation @@ -129,79 +212,127 @@ public class ConfigObject [JsonProperty(NullValueHandling = NullValueHandling.Include)] public string ValidatorUrl { get; set; } = null; - [JsonExtensionData] - public Dictionary AdditionalItems = new Dictionary(); - } - - public class UrlDescriptor - { - public string Url { get; set; } - - public string Name { get; set; } + #endregion 属性 } - public enum ModelRendering + /// + /// Defines the + /// + public class OAuthConfigObject { - Example, - Model - } + #region 属性 - public enum DocExpansion - { - List, - Full, - None - } + /// + /// Gets or sets the AdditionalQueryStringParams + /// Additional query parameters added to authorizationUrl and tokenUrl + /// + public Dictionary AdditionalQueryStringParams { get; set; } = null; - public enum SubmitMethod - { - Get, - Put, - Post, - Delete, - Options, - Head, - Patch, - Trace - } + /// + /// Gets or sets the AppName + /// Application name, displayed in authorization popup + /// + public string AppName { get; set; } = null; - public class OAuthConfigObject - { /// + /// Gets or sets the ClientId /// Default clientId /// public string ClientId { get; set; } = "clientId"; /// + /// Gets or sets the ClientSecret /// Default clientSecret /// public string ClientSecret { get; set; } = "clientSecret"; /// + /// Gets or sets the Realm /// Realm query parameter (for oauth1) added to authorizationUrl and tokenUrl /// public string Realm { get; set; } = null; /// - /// Application name, displayed in authorization popup - /// - public string AppName { get; set; } = null; - - /// + /// Gets or sets the ScopeSeperator /// Scope separator for passing scopes, encoded before calling, default value is a space (encoded value %20) /// public string ScopeSeperator { get; set; } = " "; /// - /// Additional query parameters added to authorizationUrl and tokenUrl - /// - public Dictionary AdditionalQueryStringParams { get; set; } = null; - - /// + /// Gets or sets a value indicating whether UseBasicAuthenticationWithAccessCodeGrant /// Only activated for the accessCode flow. During the authorization_code request to the tokenUrl, /// pass the Client Password using the HTTP Basic Authentication scheme /// (Authorization header with Basic base64encode(client_id + client_secret)) /// public bool UseBasicAuthenticationWithAccessCodeGrant { get; set; } = false; + + #endregion 属性 + } + + /// + /// Defines the + /// + public class SwaggerUIOptions + { + #region 属性 + + /// + /// Gets or sets the ConfigObject + /// Gets the JavaScript config object, represented as JSON, that will be passed to the SwaggerUI + /// + public ConfigObject ConfigObject { get; set; } = new ConfigObject(); + + /// + /// Gets or sets the DocumentTitle + /// Gets or sets a title for the swagger-ui page + /// + public string DocumentTitle { get; set; } = "Swagger UI"; + + /// + /// Gets or sets the HeadContent + /// Gets or sets additional content to place in the head of the swagger-ui page + /// + public string HeadContent { get; set; } = ""; + + /// + /// Gets or sets the IndexStream + /// Gets or sets a Stream function for retrieving the swagger-ui page + /// + public Func IndexStream { get; set; } = () => typeof(SwaggerUIOptions).GetTypeInfo().Assembly + .GetManifestResourceStream("Surging.Core.Swagger.SwaggerUI.index.html"); + + /// + /// Gets or sets the OAuthConfigObject + /// Gets the JavaScript config object, represented as JSON, that will be passed to the initOAuth method + /// + public OAuthConfigObject OAuthConfigObject { get; set; } = new OAuthConfigObject(); + + /// + /// Gets or sets the RoutePrefix + /// Gets or sets a route prefix for accessing the swagger-ui + /// + public string RoutePrefix { get; set; } = "swagger"; + + #endregion 属性 + } + + /// + /// Defines the + /// + public class UrlDescriptor + { + #region 属性 + + /// + /// Gets or sets the Name + /// + public string Name { get; set; } + + /// + /// Gets or sets the Url + /// + public string Url { get; set; } + + #endregion 属性 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Swagger/SwaggerUI/SwaggerUIOptionsExtensions.cs.cs b/src/Surging.Core/Surging.Core.Swagger/SwaggerUI/SwaggerUIOptionsExtensions.cs.cs index f11516687..a0361bf7b 100644 --- a/src/Surging.Core/Surging.Core.Swagger/SwaggerUI/SwaggerUIOptionsExtensions.cs.cs +++ b/src/Surging.Core/Surging.Core.Swagger/SwaggerUI/SwaggerUIOptionsExtensions.cs.cs @@ -7,96 +7,32 @@ namespace Surging.Core.Swagger.SwaggerUI { - public static class SwaggerUIOptionsExtensions + /// + /// Defines the + /// + public static class SwaggerUIOptionsExtensions { - /// - /// Injects additional CSS stylesheets into the index.html page - /// - /// - /// A path to the stylesheet - i.e. the link "href" attribute - /// The target media - i.e. the link "media" attribute - public static void InjectStylesheet(this SwaggerUIOptions options, string path, string media = "screen") - { - var builder = new StringBuilder(options.HeadContent); - builder.AppendLine($""); - options.HeadContent = builder.ToString(); - } - - /// - /// Injects additional Javascript files into the index.html page - /// - /// - /// A path to the javascript - i.e. the script "src" attribute - /// The script type - i.e. the script "type" attribute - public static void InjectJavascript(this SwaggerUIOptions options, string path, string type = "text/javascript") - { - var builder = new StringBuilder(options.HeadContent); - builder.AppendLine($""); - options.HeadContent = builder.ToString(); - } - - /// - /// Adds Swagger JSON endpoints. Can be fully-qualified or relative to the UI page - /// - /// - /// Can be fully qualified or relative to the current host - /// The description that appears in the document selector drop-down - public static void SwaggerEndpoint(this SwaggerUIOptions options, string url, string name,string areaName) - { - var urls = new List(options.ConfigObject.Urls ?? Enumerable.Empty()); - urls.Add(new UrlDescriptor { Url = string.IsNullOrEmpty(areaName) ? url : $"{areaName}{url}", Name = name }); - options.ConfigObject.Urls = urls; - } - - public static void SwaggerEndpoint(this SwaggerUIOptions options, IEnumerable entries,string areaName) - { - - var list = new List(); - var assemblies = entries.Select(p => p.Type.Assembly).Distinct(); - foreach (var assembly in assemblies) - { - var version = assembly - .GetCustomAttributes(true) - .OfType().FirstOrDefault(); - - var title = assembly - .GetCustomAttributes(true) - .OfType().FirstOrDefault(); - - var des = assembly - .GetCustomAttributes(true) - .OfType().FirstOrDefault(); - - if (version == null || title == null) - continue; - - var info = new Info() - { - Title = title.Title, - Version = version.Version, - Description = des?.Description, - - }; - options.SwaggerEndpoint($"../swagger/{info.Title}/swagger.json", info.Title,areaName); - } - } + #region 方法 /// - /// Enables deep linking for tags and operations + /// The default expansion depth for the model on the model-example section /// /// - public static void EnableDeepLinking(this SwaggerUIOptions options) + /// + public static void DefaultModelExpandDepth(this SwaggerUIOptions options, int depth) { - options.ConfigObject.DeepLinking = true; + options.ConfigObject.DefaultModelExpandDepth = depth; } /// - /// Controls the display of operationId in operations list + /// Controls how the model is shown when the API is first rendered. + /// (The user can always switch the rendering for a given model by clicking the 'Model' and 'Example Value' links.) /// /// - public static void DisplayOperationId(this SwaggerUIOptions options) + /// + public static void DefaultModelRendering(this SwaggerUIOptions options, ModelRendering modelRendering) { - options.ConfigObject.DisplayOperationId = true; + options.ConfigObject.DefaultModelRendering = modelRendering; } /// @@ -110,24 +46,12 @@ public static void DefaultModelsExpandDepth(this SwaggerUIOptions options, int d } /// - /// The default expansion depth for the model on the model-example section - /// - /// - /// - public static void DefaultModelExpandDepth(this SwaggerUIOptions options, int depth) - { - options.ConfigObject.DefaultModelExpandDepth = depth; - } - - /// - /// Controls how the model is shown when the API is first rendered. - /// (The user can always switch the rendering for a given model by clicking the 'Model' and 'Example Value' links.) + /// Controls the display of operationId in operations list /// /// - /// - public static void DefaultModelRendering(this SwaggerUIOptions options, ModelRendering modelRendering) + public static void DisplayOperationId(this SwaggerUIOptions options) { - options.ConfigObject.DefaultModelRendering = modelRendering; + options.ConfigObject.DisplayOperationId = true; } /// @@ -150,6 +74,15 @@ public static void DocExpansion(this SwaggerUIOptions options, DocExpansion docE options.ConfigObject.DocExpansion = docExpansion; } + /// + /// Enables deep linking for tags and operations + /// + /// + public static void EnableDeepLinking(this SwaggerUIOptions options) + { + options.ConfigObject.DeepLinking = true; + } + /// /// Enables filtering. The top bar will show an edit box that you can use to filter the tagged operations that are shown. /// If an expression is provided it will be used and applied initially. @@ -163,33 +96,50 @@ public static void EnableFilter(this SwaggerUIOptions options, string expression } /// - /// Limits the number of tagged operations displayed to at most this many. The default is to show all operations + /// You can use this parameter to enable the swagger-ui's built-in validator (badge) functionality + /// Setting it to null will disable validation /// /// - /// - public static void MaxDisplayedTags(this SwaggerUIOptions options, int count) + /// + public static void EnableValidator(this SwaggerUIOptions options, string url = "https://online.swagger.io/validator") { - options.ConfigObject.MaxDisplayedTags = count; + options.ConfigObject.ValidatorUrl = url; } /// - /// Controls the display of vendor extension (x-) fields and values for Operations, Parameters, and Schema + /// Injects additional Javascript files into the index.html page /// /// - public static void ShowExtensions(this SwaggerUIOptions options) + /// A path to the javascript - i.e. the script "src" attribute + /// The script type - i.e. the script "type" attribute + public static void InjectJavascript(this SwaggerUIOptions options, string path, string type = "text/javascript") { - options.ConfigObject.ShowExtensions = true; + var builder = new StringBuilder(options.HeadContent); + builder.AppendLine($""); + options.HeadContent = builder.ToString(); } /// - /// List of HTTP methods that have the Try it out feature enabled. An empty array disables Try it out for all operations. - /// This does not filter the operations from the display + /// Injects additional CSS stylesheets into the index.html page /// /// - /// - public static void SupportedSubmitMethods(this SwaggerUIOptions options, params SubmitMethod[] submitMethods) + /// A path to the stylesheet - i.e. the link "href" attribute + /// The target media - i.e. the link "media" attribute + public static void InjectStylesheet(this SwaggerUIOptions options, string path, string media = "screen") { - options.ConfigObject.SupportedSubmitMethods = submitMethods; + var builder = new StringBuilder(options.HeadContent); + builder.AppendLine($""); + options.HeadContent = builder.ToString(); + } + + /// + /// Limits the number of tagged operations displayed to at most this many. The default is to show all operations + /// + /// + /// + public static void MaxDisplayedTags(this SwaggerUIOptions options, int count) + { + options.ConfigObject.MaxDisplayedTags = count; } /// @@ -202,21 +152,26 @@ public static void OAuth2RedirectUrl(this SwaggerUIOptions options, string url) options.ConfigObject.OAuth2RedirectUrl = url; } - [Obsolete("The validator is disabled by default. Use EnableValidator to enable it")] - public static void ValidatorUrl(this SwaggerUIOptions options, string url) + /// + /// Additional query parameters added to authorizationUrl and tokenUrl + /// + /// + /// + public static void OAuthAdditionalQueryStringParams( + this SwaggerUIOptions options, + Dictionary value) { - options.ConfigObject.ValidatorUrl = url; + options.OAuthConfigObject.AdditionalQueryStringParams = value; } /// - /// You can use this parameter to enable the swagger-ui's built-in validator (badge) functionality - /// Setting it to null will disable validation + /// Application name, displayed in authorization popup /// /// - /// - public static void EnableValidator(this SwaggerUIOptions options, string url = "https://online.swagger.io/validator") + /// + public static void OAuthAppName(this SwaggerUIOptions options, string value) { - options.ConfigObject.ValidatorUrl = url; + options.OAuthConfigObject.AppName = value; } /// @@ -250,46 +205,108 @@ public static void OAuthRealm(this SwaggerUIOptions options, string value) } /// - /// Application name, displayed in authorization popup + /// Scope separator for passing scopes, encoded before calling, default value is a space (encoded value %20) /// /// /// - public static void OAuthAppName(this SwaggerUIOptions options, string value) + public static void OAuthScopeSeparator(this SwaggerUIOptions options, string value) { - options.OAuthConfigObject.AppName = value; + options.OAuthConfigObject.ScopeSeperator = value; } /// - /// Scope separator for passing scopes, encoded before calling, default value is a space (encoded value %20) + /// Only activated for the accessCode flow. During the authorization_code request to the tokenUrl, + /// pass the Client Password using the HTTP Basic Authentication scheme (Authorization header with + /// Basic base64encoded[client_id:client_secret]). The default is false /// /// - /// - public static void OAuthScopeSeparator(this SwaggerUIOptions options, string value) + public static void OAuthUseBasicAuthenticationWithAccessCodeGrant(this SwaggerUIOptions options) { - options.OAuthConfigObject.ScopeSeperator = value; + options.OAuthConfigObject.UseBasicAuthenticationWithAccessCodeGrant = true; } /// - /// Additional query parameters added to authorizationUrl and tokenUrl + /// Controls the display of vendor extension (x-) fields and values for Operations, Parameters, and Schema /// /// - /// - public static void OAuthAdditionalQueryStringParams( - this SwaggerUIOptions options, - Dictionary value) + public static void ShowExtensions(this SwaggerUIOptions options) { - options.OAuthConfigObject.AdditionalQueryStringParams = value; + options.ConfigObject.ShowExtensions = true; } /// - /// Only activated for the accessCode flow. During the authorization_code request to the tokenUrl, - /// pass the Client Password using the HTTP Basic Authentication scheme (Authorization header with - /// Basic base64encoded[client_id:client_secret]). The default is false + /// List of HTTP methods that have the Try it out feature enabled. An empty array disables Try it out for all operations. + /// This does not filter the operations from the display /// /// - public static void OAuthUseBasicAuthenticationWithAccessCodeGrant(this SwaggerUIOptions options) + /// + public static void SupportedSubmitMethods(this SwaggerUIOptions options, params SubmitMethod[] submitMethods) { - options.OAuthConfigObject.UseBasicAuthenticationWithAccessCodeGrant = true; + options.ConfigObject.SupportedSubmitMethods = submitMethods; + } + + /// + /// The SwaggerEndpoint + /// + /// The options + /// The entries + /// The areaName + public static void SwaggerEndpoint(this SwaggerUIOptions options, IEnumerable entries, string areaName) + { + var list = new List(); + var assemblies = entries.Select(p => p.Type.Assembly).Distinct(); + foreach (var assembly in assemblies) + { + var version = assembly + .GetCustomAttributes(true) + .OfType().FirstOrDefault(); + + var title = assembly + .GetCustomAttributes(true) + .OfType().FirstOrDefault(); + + var des = assembly + .GetCustomAttributes(true) + .OfType().FirstOrDefault(); + + if (version == null || title == null) + continue; + + var info = new Info() + { + Title = title.Title, + Version = version.Version, + Description = des?.Description, + }; + options.SwaggerEndpoint($"../swagger/{info.Title}/swagger.json", info.Title, areaName); + } + } + + /// + /// Adds Swagger JSON endpoints. Can be fully-qualified or relative to the UI page + /// + /// + /// Can be fully qualified or relative to the current host + /// The description that appears in the document selector drop-down + /// The areaName + public static void SwaggerEndpoint(this SwaggerUIOptions options, string url, string name, string areaName) + { + var urls = new List(options.ConfigObject.Urls ?? Enumerable.Empty()); + urls.Add(new UrlDescriptor { Url = string.IsNullOrEmpty(areaName) ? url : $"{areaName}{url}", Name = name }); + options.ConfigObject.Urls = urls; + } + + /// + /// The ValidatorUrl + /// + /// The options + /// The url + [Obsolete("The validator is disabled by default. Use EnableValidator to enable it")] + public static void ValidatorUrl(this SwaggerUIOptions options, string url) + { + options.ConfigObject.ValidatorUrl = url; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/Intercept/CacheKeyAttribute.cs b/src/Surging.Core/Surging.Core.System/Intercept/CacheKeyAttribute.cs index b0f95e07f..be8ffcd18 100644 --- a/src/Surging.Core/Surging.Core.System/Intercept/CacheKeyAttribute.cs +++ b/src/Surging.Core/Surging.Core.System/Intercept/CacheKeyAttribute.cs @@ -12,8 +12,16 @@ namespace Surging.Core.System.Intercept AllowMultiple = false)] public class CacheKeyAttribute : KeyAttribute { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The sortIndex public CacheKeyAttribute(int sortIndex) : base(sortIndex) { } + + #endregion 构造函数 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/Intercept/CacheProviderExtension.cs b/src/Surging.Core/Surging.Core.System/Intercept/CacheProviderExtension.cs index 07180513b..0c869a505 100644 --- a/src/Surging.Core/Surging.Core.System/Intercept/CacheProviderExtension.cs +++ b/src/Surging.Core/Surging.Core.System/Intercept/CacheProviderExtension.cs @@ -6,35 +6,62 @@ namespace Surging.Core.System.Intercept { + /// + /// Defines the + /// public static class CacheProviderExtension { - public static async Task GetFromCacheFirst(this ICacheProvider cacheProvider, string key, Func> getFromPersistence, Type returnType, long? storeTime = null) where T : class + #region 方法 + + /// + /// The GetFromCacheFirst + /// + /// + /// The cacheProvider + /// The l2cacheProvider + /// The l2Key + /// The key + /// The getFromPersistence + /// The returnType + /// The storeTime + /// The + public static async Task GetFromCacheFirst(this ICacheProvider cacheProvider, ICacheProvider l2cacheProvider, string l2Key, string key, Func> getFromPersistence, Type returnType, long? storeTime = null) where T : class { object returnValue; try { - var resultJson = cacheProvider.Get(key); - if (string.IsNullOrEmpty(resultJson) || resultJson == "\"[]\"") + var signJson = cacheProvider.Get(key); + if (string.IsNullOrEmpty(signJson) || signJson == "\"[]\"") { returnValue = await getFromPersistence(); if (returnValue != null) { - resultJson = JsonConvert.SerializeObject(returnValue); - if (storeTime.HasValue) - { - cacheProvider.Remove(key); - cacheProvider.Add(key, resultJson, storeTime.Value); - } - else + var resultJson = JsonConvert.SerializeObject(returnValue); + var sign = Guid.NewGuid(); + signJson = JsonConvert.SerializeObject(sign); + if (l2Key == key) { - cacheProvider.Remove(key); - cacheProvider.Add(key, resultJson); + SetCache(cacheProvider, key, signJson, storeTime); } + SetCache(l2cacheProvider, l2Key, new ValueTuple(signJson, resultJson), storeTime); } } else { - returnValue = JsonConvert.DeserializeObject(resultJson, returnType); + var l2Cache = l2cacheProvider.Get>(l2Key); + if (l2Cache == default || l2Cache.Item1 != signJson) + { + returnValue = await getFromPersistence(); + if (returnValue != null) + { + var resultJson = JsonConvert.SerializeObject(returnValue); + SetCache(l2cacheProvider, l2Key, new ValueTuple(signJson, resultJson), storeTime); + } + } + else + { + returnValue = JsonConvert.DeserializeObject(l2Cache.Item2, returnType); + } } return returnValue as T; } @@ -45,43 +72,43 @@ public static async Task GetFromCacheFirst(this ICacheProvider cacheProvid } } - public static async Task GetFromCacheFirst(this ICacheProvider cacheProvider, ICacheProvider l2cacheProvider,string l2Key, string key, Func> getFromPersistence, Type returnType, long? storeTime = null) where T : class + /// + /// The GetFromCacheFirst + /// + /// + /// The cacheProvider + /// The key + /// The getFromPersistence + /// The returnType + /// The storeTime + /// The + public static async Task GetFromCacheFirst(this ICacheProvider cacheProvider, string key, Func> getFromPersistence, Type returnType, long? storeTime = null) where T : class { object returnValue; try { - var signJson = cacheProvider.Get(key); - if (string.IsNullOrEmpty(signJson) || signJson == "\"[]\"") + var resultJson = cacheProvider.Get(key); + if (string.IsNullOrEmpty(resultJson) || resultJson == "\"[]\"") { returnValue = await getFromPersistence(); if (returnValue != null) { - var resultJson = JsonConvert.SerializeObject(returnValue); - var sign = Guid.NewGuid(); - signJson = JsonConvert.SerializeObject(sign); - if (l2Key == key) + resultJson = JsonConvert.SerializeObject(returnValue); + if (storeTime.HasValue) { - SetCache(cacheProvider, key, signJson, storeTime); + cacheProvider.Remove(key); + cacheProvider.Add(key, resultJson, storeTime.Value); + } + else + { + cacheProvider.Remove(key); + cacheProvider.Add(key, resultJson); } - SetCache(l2cacheProvider, l2Key, new ValueTuple(signJson, resultJson), storeTime); } } else { - var l2Cache= l2cacheProvider.Get>(l2Key); - if(l2Cache==default || l2Cache.Item1!=signJson) - { - returnValue = await getFromPersistence(); - if (returnValue != null) - { - var resultJson = JsonConvert.SerializeObject(returnValue); - SetCache(l2cacheProvider, l2Key, new ValueTuple(signJson, resultJson), storeTime); - } - } - else - { - returnValue = JsonConvert.DeserializeObject(l2Cache.Item2, returnType); - } + returnValue = JsonConvert.DeserializeObject(resultJson, returnType); } return returnValue as T; } @@ -92,7 +119,14 @@ public static async Task GetFromCacheFirst(this ICacheProvider cacheProvid } } - private static void SetCache(ICacheProvider cacheProvider, string key, object value, long? numOfMinutes) + /// + /// The SetCache + /// + /// The cacheProvider + /// The key + /// The value + /// The numOfMinutes + private static void SetCache(ICacheProvider cacheProvider, string key, object value, long? numOfMinutes) { if (numOfMinutes.HasValue) { @@ -105,5 +139,7 @@ private static void SetCache(ICacheProvider cacheProvider, string key, object v cacheProvider.Add(key, value); } } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/Intercept/CacheProviderInterceptor.cs b/src/Surging.Core/Surging.Core.System/Intercept/CacheProviderInterceptor.cs index e70bba939..4042978d0 100644 --- a/src/Surging.Core/Surging.Core.System/Intercept/CacheProviderInterceptor.cs +++ b/src/Surging.Core/Surging.Core.System/Intercept/CacheProviderInterceptor.cs @@ -11,6 +11,13 @@ namespace Surging.Core.System.Intercept /// public class CacheProviderInterceptor : CacheInterceptor { + #region 方法 + + /// + /// The Intercept + /// + /// The invocation + /// The public override async Task Intercept(ICacheInvocation invocation) { var attribute = @@ -20,10 +27,19 @@ public override async Task Intercept(ICacheInvocation invocation) string.Format(attribute.Key ?? "", invocation.CacheKey); var l2CacheKey = invocation.CacheKey == null ? attribute.L2Key : string.Format(attribute.L2Key ?? "", invocation.CacheKey); - await CacheIntercept(attribute, cacheKey, invocation, l2CacheKey,attribute.EnableL2Cache); + await CacheIntercept(attribute, cacheKey, invocation, l2CacheKey, attribute.EnableL2Cache); } - private async Task CacheIntercept(InterceptMethodAttribute attribute, string key, ICacheInvocation invocation,string l2Key,bool enableL2Cache) + /// + /// The CacheIntercept + /// + /// The attribute + /// The key + /// The invocation + /// The l2Key + /// The enableL2Cache + /// The + private async Task CacheIntercept(InterceptMethodAttribute attribute, string key, ICacheInvocation invocation, string l2Key, bool enableL2Cache) { ICacheProvider cacheProvider = null; switch (attribute.Mode) @@ -41,25 +57,34 @@ private async Task CacheIntercept(InterceptMethodAttribute attribute, string key } } if (cacheProvider != null && !enableL2Cache) await Invoke(cacheProvider, attribute, key, invocation); - else if(cacheProvider != null && enableL2Cache) + else if (cacheProvider != null && enableL2Cache) { var l2CacheProvider = CacheContainer.GetService(CacheTargetType.MemoryCache.ToString()); - if(l2CacheProvider !=null) await Invoke(cacheProvider,l2CacheProvider,l2Key, attribute, key, invocation); + if (l2CacheProvider != null) await Invoke(cacheProvider, l2CacheProvider, l2Key, attribute, key, invocation); } } - private async Task Invoke(ICacheProvider cacheProvider, InterceptMethodAttribute attribute, string key, ICacheInvocation invocation) + /// + /// The Invoke + /// + /// The cacheProvider + /// The l2cacheProvider + /// The l2Key + /// The attribute + /// The key + /// The invocation + /// The + private async Task Invoke(ICacheProvider cacheProvider, ICacheProvider l2cacheProvider, string l2Key, InterceptMethodAttribute attribute, string key, ICacheInvocation invocation) { - switch (attribute.Method) { case CachingMethod.Get: { - var retrunValue = await cacheProvider.GetFromCacheFirst(key, async () => - { - await invocation.Proceed(); - return invocation.ReturnValue; - }, invocation.ReturnType, attribute.Time); + var retrunValue = await cacheProvider.GetFromCacheFirst(l2cacheProvider, l2Key, key, async () => + { + await invocation.Proceed(); + return invocation.ReturnValue; + }, invocation.ReturnType, attribute.Time); invocation.ReturnValue = retrunValue; break; } @@ -73,15 +98,21 @@ private async Task Invoke(ICacheProvider cacheProvider, InterceptMethodAttribute } } - - private async Task Invoke(ICacheProvider cacheProvider, ICacheProvider l2cacheProvider,string l2Key, InterceptMethodAttribute attribute, string key, ICacheInvocation invocation) + /// + /// The Invoke + /// + /// The cacheProvider + /// The attribute + /// The key + /// The invocation + /// The + private async Task Invoke(ICacheProvider cacheProvider, InterceptMethodAttribute attribute, string key, ICacheInvocation invocation) { - switch (attribute.Method) { case CachingMethod.Get: { - var retrunValue = await cacheProvider.GetFromCacheFirst(l2cacheProvider, l2Key,key, async () => + var retrunValue = await cacheProvider.GetFromCacheFirst(key, async () => { await invocation.Proceed(); return invocation.ReturnValue; @@ -98,5 +129,7 @@ private async Task Invoke(ICacheProvider cacheProvider, ICacheProvider l2cachePr } } } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/Intercept/CachingMethod.cs b/src/Surging.Core/Surging.Core.System/Intercept/CachingMethod.cs index d82483e21..8ca91fad7 100644 --- a/src/Surging.Core/Surging.Core.System/Intercept/CachingMethod.cs +++ b/src/Surging.Core/Surging.Core.System/Intercept/CachingMethod.cs @@ -4,6 +4,8 @@ namespace Surging.Core.System.Intercept { + #region 枚举 + /// /// 表示用于Caching特性的缓存方式。 /// @@ -14,15 +16,18 @@ public enum CachingMethod /// 然后将获得的结果添加到缓存中。 /// Get, + /// /// 表示需要将对象存入缓存。此方式会调用实际方法以获取对象,然后将获得的结果添加到缓存中, /// 并直接返回方法的调用结果。 /// Put, + /// /// 表示需要将对象从缓存中移除。 /// Remove } -} + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/Intercept/InterceptMethodAttribute.cs b/src/Surging.Core/Surging.Core.System/Intercept/InterceptMethodAttribute.cs index 5e737c717..42cbfa14a 100644 --- a/src/Surging.Core/Surging.Core.System/Intercept/InterceptMethodAttribute.cs +++ b/src/Surging.Core/Surging.Core.System/Intercept/InterceptMethodAttribute.cs @@ -12,24 +12,42 @@ namespace Surging.Core.System.Intercept public class InterceptMethodAttribute : Attribute { #region 字段 - int _time = 60; - CacheTargetType _mode = CacheTargetType.MemoryCache; - CachingMethod _method; - string[] _correspondingKeys; - #endregion + /// + /// Defines the _correspondingKeys + /// + internal string[] _correspondingKeys; + + /// + /// Defines the _method + /// + internal CachingMethod _method; + + /// + /// Defines the _mode + /// + internal CacheTargetType _mode = CacheTargetType.MemoryCache; + + /// + /// Defines the _time + /// + internal int _time = 60; + + #endregion 字段 #region 构造函数 + /// - /// 初始化一个新的InterceptMethodAttribute类型。 + /// Initializes a new instance of the class. /// /// 缓存方式。 public InterceptMethodAttribute(CachingMethod method) { this._method = method; } + /// - /// 初始化一个新的InterceptMethodAttribute类型。 + /// Initializes a new instance of the class. /// /// 缓存方式。 /// 与当前缓存方式相关的方法名称。注:此参数仅在缓存方式为Remove时起作用。 @@ -38,78 +56,84 @@ public InterceptMethodAttribute(CachingMethod method, params string[] correspond { this._correspondingKeys = correspondingMethodNames; } - #endregion - #region 公共属性 + #endregion 构造函数 + + #region 属性 + /// - /// 有效时间 + /// Gets or sets the CacheSectionType + /// 设置SectionType /// - public int Time - { - get { return _time; } - set { _time = value; } - } + public SectionType CacheSectionType { get; set; } + /// - /// 采用什么进行缓存 + /// Gets or sets the CorrespondingKeys + /// 获取或设置与当前缓存方式相关的方法名称。注:此参数仅在缓存方式为Remove时起作用。 /// - public CacheTargetType Mode - { - get { return _mode; } - set { _mode = value; } - } - - ///// - ///// 设置SectionType - ///// - public SectionType CacheSectionType + public string[] CorrespondingKeys { - get; - set; + get + { + return _correspondingKeys; + } + set + { + _correspondingKeys = value; + } } - public string L2Key - { - get; set; - } + /// + /// Gets or sets a value indicating whether EnableL2Cache + /// + public bool EnableL2Cache { get; set; } - public bool EnableL2Cache - { - get;set; - } + /// + /// Gets or sets a value indicating whether Force + /// 获取或设置一个值,该值表示当缓存方式为Put时,是否强制将值写入缓存中。 + /// + public bool Force { get; set; } + /// + /// Gets or sets the Key + /// public string Key { get; set; } + /// + /// Gets or sets the L2Key + /// + public string L2Key { get; set; } + + /// + /// Gets or sets the Method /// 获取或设置缓存方式。 /// public CachingMethod Method { - get - { - return _method; - } + get { return _method; } set { _method = value; } } /// - /// 获取或设置一个值,该值表示当缓存方式为Put时,是否强制将值写入缓存中。 + /// Gets or sets the Mode + /// 采用什么进行缓存 /// - public bool Force { get; set; } + public CacheTargetType Mode + { + get { return _mode; } + set { _mode = value; } + } + /// - /// 获取或设置与当前缓存方式相关的方法名称。注:此参数仅在缓存方式为Remove时起作用。 + /// Gets or sets the Time + /// 有效时间 /// - public string[] CorrespondingKeys + public int Time { - get - { - return _correspondingKeys; - } - set - { - _correspondingKeys = value; - } + get { return _time; } + set { _time = value; } } - #endregion - + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/Intercept/LogProviderInterceptor.cs b/src/Surging.Core/Surging.Core.System/Intercept/LogProviderInterceptor.cs index f6432aeef..a843ad90d 100644 --- a/src/Surging.Core/Surging.Core.System/Intercept/LogProviderInterceptor.cs +++ b/src/Surging.Core/Surging.Core.System/Intercept/LogProviderInterceptor.cs @@ -8,13 +8,25 @@ namespace Surging.Core.System.Intercept { + /// + /// Defines the + /// public class LogProviderInterceptor : IInterceptor { + #region 方法 + + /// + /// The Intercept + /// + /// The invocation + /// The public async Task Intercept(IInvocation invocation) { var watch = Stopwatch.StartNew(); await invocation.Proceed(); var result = invocation.ReturnValue; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/Intercept/LoggerInterceptAttribute.cs b/src/Surging.Core/Surging.Core.System/Intercept/LoggerInterceptAttribute.cs index eb811b421..de6c21291 100644 --- a/src/Surging.Core/Surging.Core.System/Intercept/LoggerInterceptAttribute.cs +++ b/src/Surging.Core/Surging.Core.System/Intercept/LoggerInterceptAttribute.cs @@ -3,38 +3,47 @@ using System.Text; namespace Surging.Core.System.Intercept -{ /// - /// 设置判断日志拦截方法的特性类 - /// +{ + /// + /// 设置判断日志拦截方法的特性类 + /// [AttributeUsage(AttributeTargets.Method | AttributeTargets.Interface)] public class LoggerInterceptAttribute : Attribute { #region 字段 + + /// + /// Defines the _message + /// private string _message; - #endregion + + #endregion 字段 #region 构造函数 + /// - /// 初始化一个新的InterceptMethodAttribute类型。 + /// Initializes a new instance of the class. /// - /// 缓存方式。 - public LoggerInterceptAttribute(string message) + public LoggerInterceptAttribute() + : this(null) { - this._message = message; } /// - /// 初始化一个新的InterceptMethodAttribute类型。 + /// Initializes a new instance of the class. /// - public LoggerInterceptAttribute() - : this(null) + /// The message + public LoggerInterceptAttribute(string message) { - + this._message = message; } - #endregion - #region 公共属性 + #endregion 构造函数 + + #region 属性 + /// + /// Gets or sets the Message /// 日志内容 /// public string Message @@ -48,6 +57,7 @@ public string Message _message = value; } } - #endregion + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/Intercept/SectionType.cs b/src/Surging.Core/Surging.Core.System/Intercept/SectionType.cs index fc8d85b48..696ccda07 100644 --- a/src/Surging.Core/Surging.Core.System/Intercept/SectionType.cs +++ b/src/Surging.Core/Surging.Core.System/Intercept/SectionType.cs @@ -4,8 +4,18 @@ namespace Surging.Core.System.Intercept { + #region 枚举 + + /// + /// Defines the SectionType + /// public enum SectionType { + /// + /// Defines the ddlCache + /// ddlCache } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/Ioc/ModuleNameAttribute.cs b/src/Surging.Core/Surging.Core.System/Ioc/ModuleNameAttribute.cs index 784296cb2..2680d93e8 100644 --- a/src/Surging.Core/Surging.Core.System/Ioc/ModuleNameAttribute.cs +++ b/src/Surging.Core/Surging.Core.System/Ioc/ModuleNameAttribute.cs @@ -4,18 +4,39 @@ namespace Surging.Core.System.Ioc { + /// + /// Defines the + /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] public class ModuleNameAttribute : Attribute { - public string ModuleName { get; set; } + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// public ModuleNameAttribute() { - } + + /// + /// Initializes a new instance of the class. + /// + /// The moduleName public ModuleNameAttribute(string moduleName) { ModuleName = moduleName; } + + #endregion 构造函数 + + #region 属性 + + /// + /// Gets or sets the ModuleName + /// + public string ModuleName { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/Ioc/RegistrationExtensions.cs b/src/Surging.Core/Surging.Core.System/Ioc/RegistrationExtensions.cs index 9f6f8ac1e..bfae6dd62 100644 --- a/src/Surging.Core/Surging.Core.System/Ioc/RegistrationExtensions.cs +++ b/src/Surging.Core/Surging.Core.System/Ioc/RegistrationExtensions.cs @@ -1,19 +1,19 @@ -using Autofac.Core; +using Autofac; +using Autofac.Builder; +using Autofac.Core; +using Autofac.Core.Registration; using Autofac.Features.Scanning; +using Surging.Core.Common.ServicesException; +using Surging.Core.CPlatform.EventBus.Events; +using Surging.Core.System.Intercept; using Surging.Core.System.Module; using Surging.Core.System.Module.Attributes; using System; using System.Collections.Generic; using System.IO; -using System.Text.RegularExpressions; -using Autofac; -using Autofac.Builder; using System.Linq; using System.Reflection; -using Autofac.Core.Registration; -using Surging.Core.Common.ServicesException; -using Surging.Core.System.Intercept; -using Surging.Core.CPlatform.EventBus.Events; +using System.Text.RegularExpressions; namespace Surging.Core.System.Ioc { @@ -23,19 +23,87 @@ namespace Surging.Core.System.Ioc [Obsolete] public static class RegistrationExtensions { - private static readonly IEnumerable EmptyServices = new Service[0]; + #region 常量 + /// + /// Defines the InterceptorsPropertyName + /// private const string InterceptorsPropertyName = "Surging.Core.System.Ioc.RegistrationExtensions.InterceptorsPropertyName"; + #endregion 常量 + + #region 字段 + + /// + /// Defines the EmptyServices + /// + private static readonly IEnumerable EmptyServices = new Service[0]; + + /// + /// Defines the _referenceAssembly + /// private static Dictionary _referenceAssembly = new Dictionary(); + #endregion 字段 + + #region 方法 + + /// + /// The GetInterfaceService + /// + /// The builder + /// The + [Obsolete] + public static List GetInterfaceService(this ContainerBuilder builder) + { + var referenceAssemblies = builder.GetReferenceAssembly(); + var interfaceAssemblies = referenceAssemblies.Where( + p => + p.GetCustomAttribute().Type == ModuleType.InterFaceService || + p.GetCustomAttribute().Type == ModuleType.SystemModule).ToList(); + var types = new List(); + interfaceAssemblies.ForEach(p => + { + types.AddRange(p.GetTypes().Where(t => t.Name.StartsWith("I")).Where(t => t.Name.EndsWith("Service"))); + }); + return types; + } + + /// + /// The GetReferenceAssembly + /// + /// The builder + /// The [Obsolete] public static List GetReferenceAssembly(this ContainerBuilder builder) { return _referenceAssembly.Values.ToList(); } + /// + /// 获取指定类型的自定义特性对象。 + /// + /// 类型参数:自定义特性的类型。 + /// 类型。 + /// 返回指定类型的自定义特性对象。 + [Obsolete] + public static TAttributeType GetTypeCustomAttribute(Type type) where TAttributeType : Attribute + { + var attributes = type.GetTypeInfo().GetCustomAttributes(typeof(TAttributeType), true); + var attr = default(TAttributeType); + if (attributes.Count() > 0) + { + attr = attributes.First() as TAttributeType; + } + return attr; + } + + /// + /// The Initialize + /// + /// The builder + /// The pattern [Obsolete] public static void Initialize(this ContainerBuilder builder, string pattern = "") @@ -53,116 +121,77 @@ public static void Initialize(this ContainerBuilder builder, { _referenceAssembly.Add(referencedAssemblyName, referencedAssembly); } - } } } - /// - /// 依赖注入注册wcf相关的程序集 + /// The InitializeModule /// - /// ioc容器 - /// 返回注册模块信息 + /// The builder [Obsolete] - public static IRegistrationBuilder RegisterWcfServices( - this ContainerBuilder builder) + public static void InitializeModule(this ContainerBuilder builder) { var referenceAssemblies = builder.GetReferenceAssembly(); - IRegistrationBuilder result = null; if (builder == null) throw new ArgumentNullException("builder"); - var interfaceAssemblies = referenceAssemblies.Where( + var moduleAssemblies = referenceAssemblies.Where( p => - p.GetCustomAttribute().Type == ModuleType.WcfService).ToList(); - foreach (var interfaceAssembly in interfaceAssemblies) + p.GetCustomAttribute().Type == ModuleType.SystemModule || + p.GetCustomAttribute().Type == ModuleType.BusinessModule).ToList(); + foreach (var moduleAssembly in moduleAssemblies) { - result = builder.RegisterAssemblyTypes(interfaceAssembly) - .Where(t => t.Name.EndsWith("Service")).SingleInstance(); - + GetAbstractModules(moduleAssembly).ForEach(p => p.Initialize()); } - return result; - } /// - /// 依赖注入业务模块程序集 + /// The RegisterBusinessModules /// - /// ioc容器 - /// 返回注册模块信息 + /// The builder + /// The [Obsolete] - public static IRegistrationBuilder RegisterServices( - this ContainerBuilder builder) + public static IRegistrationBuilder + RegisterBusinessModules(this ContainerBuilder builder) { var referenceAssemblies = builder.GetReferenceAssembly(); IRegistrationBuilder result = null; if (builder == null) throw new ArgumentNullException("builder"); - - #region 接口服务注入 - var interfaceAssemblies = referenceAssemblies.Where( - p => - p.GetCustomAttribute().Type == ModuleType.InterFaceService || - p.GetCustomAttribute().Type == ModuleType.SystemModule).ToList(); - foreach (var interfaceAssembly in interfaceAssemblies) - { - result = builder.RegisterAssemblyTypes(interfaceAssembly) - .Where(t => t.Name.StartsWith("I")).Where(t => t.Name.EndsWith("Service")) - .AsImplementedInterfaces(); - - } - #endregion - - #region 领域服务注入 - var domainAssemblies = referenceAssemblies.Where( + var repositoryAssemblies = referenceAssemblies.Where( p => p.GetCustomAttribute().Type == ModuleType.BusinessModule || p.GetCustomAttribute().Type == ModuleType.Domain).ToList(); - foreach (var domainAssembly in domainAssemblies) + foreach (var repositoryAssembly in repositoryAssemblies) { - result = builder.RegisterAssemblyTypes(domainAssembly) - .Where(t => t.Name.EndsWith("Service") && t.GetTypeInfo().GetCustomAttribute() == null).AsImplementedInterfaces(); - // result = builder.RegisterAssemblyTypes(domainAssembly) - //.Where(t => t.Name.EndsWith("Service") && t.GetTypeInfo().GetCustomAttribute() == null).EnableClassInterceptors(); - var types = domainAssembly.GetTypes().Where(t => t.Name.EndsWith("Service") && t.GetTypeInfo().GetCustomAttribute() != null); - foreach (var type in types) - { - var module = type.GetTypeInfo().GetCustomAttribute(); - var interfaceObj = type.GetInterfaces() - .FirstOrDefault(t => t.Name.StartsWith("I") && t.Name.EndsWith("Service")); - if (interfaceObj != null) - { - builder.RegisterType(type).AsImplementedInterfaces().Named(module.ModuleName, interfaceObj); - builder.RegisterType(type).Named(module.ModuleName, type); - } - } - + result = builder.RegisterAssemblyTypes(repositoryAssembly).AsImplementedInterfaces().SingleInstance(); } - #endregion return result; } + /// + /// The RegisterModules + /// + /// The builder + /// The [Obsolete] - public static IRegistrationBuilder RegisterServiceBus - ( + public static IModuleRegistrar RegisterModules( this ContainerBuilder builder) { var referenceAssemblies = builder.GetReferenceAssembly(); - IRegistrationBuilder result = null; - var assemblies = referenceAssemblies.Where( - p => - p.GetCustomAttribute().Type == ModuleType.BusinessModule || - p.GetCustomAttribute().Type == ModuleType.Domain).ToList(); - - foreach (var assembly in assemblies) + IModuleRegistrar result = default(IModuleRegistrar); + if (builder == null) throw new ArgumentNullException("builder"); + var moduleAssemblies = referenceAssemblies.Where( + p => + p.GetCustomAttribute().Type == ModuleType.SystemModule || + p.GetCustomAttribute().Type == ModuleType.BusinessModule).ToList(); + foreach (var moduleAssembly in moduleAssemblies) { - result = builder.RegisterAssemblyTypes(assembly) - .Where(t => typeof(IIntegrationEventHandler).GetTypeInfo().IsAssignableFrom(t)).AsImplementedInterfaces().SingleInstance(); - result = builder.RegisterAssemblyTypes(assembly) - .Where(t => typeof(IIntegrationEventHandler).IsAssignableFrom(t)).SingleInstance(); + result = builder.RegisterModules(moduleAssembly); } return result; } + /// - ///依赖注入仓储模块程序集 + /// 依赖注入仓储模块程序集 /// /// IOC容器 /// 返回注册模块信息 @@ -171,7 +200,6 @@ public static IRegistrationBuilder result = null; if (builder == null) throw new ArgumentNullException("builder"); @@ -187,71 +215,111 @@ public static IRegistrationBuilder + /// The RegisterServiceBus + /// + /// The builder + /// The [Obsolete] - public static IModuleRegistrar RegisterModules( + public static IRegistrationBuilder RegisterServiceBus + ( this ContainerBuilder builder) { var referenceAssemblies = builder.GetReferenceAssembly(); - IModuleRegistrar result = default(IModuleRegistrar); - if (builder == null) throw new ArgumentNullException("builder"); - var moduleAssemblies = referenceAssemblies.Where( - p => - p.GetCustomAttribute().Type == ModuleType.SystemModule || - p.GetCustomAttribute().Type == ModuleType.BusinessModule).ToList(); - foreach (var moduleAssembly in moduleAssemblies) - { - result = builder.RegisterModules(moduleAssembly); - } - return result; - } - - [Obsolete] - private static IModuleRegistrar RegisterModules( - this ContainerBuilder builder, Assembly assembly) - { - IModuleRegistrar result = default(IModuleRegistrar); + IRegistrationBuilder result = null; + var assemblies = referenceAssemblies.Where( + p => + p.GetCustomAttribute().Type == ModuleType.BusinessModule || + p.GetCustomAttribute().Type == ModuleType.Domain).ToList(); - GetAbstractModules(assembly).ForEach(p => + foreach (var assembly in assemblies) { - result = builder.RegisterModule(p); - }); - + result = builder.RegisterAssemblyTypes(assembly) + .Where(t => typeof(IIntegrationEventHandler).GetTypeInfo().IsAssignableFrom(t)).AsImplementedInterfaces().SingleInstance(); + result = builder.RegisterAssemblyTypes(assembly) + .Where(t => typeof(IIntegrationEventHandler).IsAssignableFrom(t)).SingleInstance(); + } return result; } + /// + /// 依赖注入业务模块程序集 + /// + /// ioc容器 + /// 返回注册模块信息 [Obsolete] - public static IRegistrationBuilder - RegisterBusinessModules(this ContainerBuilder builder) + public static IRegistrationBuilder RegisterServices( + this ContainerBuilder builder) { var referenceAssemblies = builder.GetReferenceAssembly(); IRegistrationBuilder result = null; if (builder == null) throw new ArgumentNullException("builder"); - var repositoryAssemblies = referenceAssemblies.Where( - p => - p.GetCustomAttribute().Type == ModuleType.BusinessModule || - p.GetCustomAttribute().Type == ModuleType.Domain).ToList(); - foreach (var repositoryAssembly in repositoryAssemblies) + + var interfaceAssemblies = referenceAssemblies.Where( + p => + p.GetCustomAttribute().Type == ModuleType.InterFaceService || + p.GetCustomAttribute().Type == ModuleType.SystemModule).ToList(); + foreach (var interfaceAssembly in interfaceAssemblies) { - result = builder.RegisterAssemblyTypes(repositoryAssembly).AsImplementedInterfaces().SingleInstance(); + result = builder.RegisterAssemblyTypes(interfaceAssembly) + .Where(t => t.Name.StartsWith("I")).Where(t => t.Name.EndsWith("Service")) + .AsImplementedInterfaces(); + } + + var domainAssemblies = referenceAssemblies.Where( + p => + p.GetCustomAttribute().Type == ModuleType.BusinessModule || + p.GetCustomAttribute().Type == ModuleType.Domain).ToList(); + foreach (var domainAssembly in domainAssemblies) + { + result = builder.RegisterAssemblyTypes(domainAssembly) + .Where(t => t.Name.EndsWith("Service") && t.GetTypeInfo().GetCustomAttribute() == null).AsImplementedInterfaces(); + // result = builder.RegisterAssemblyTypes(domainAssembly) + //.Where(t => t.Name.EndsWith("Service") && t.GetTypeInfo().GetCustomAttribute() == null).EnableClassInterceptors(); + var types = domainAssembly.GetTypes().Where(t => t.Name.EndsWith("Service") && t.GetTypeInfo().GetCustomAttribute() != null); + foreach (var type in types) + { + var module = type.GetTypeInfo().GetCustomAttribute(); + var interfaceObj = type.GetInterfaces() + .FirstOrDefault(t => t.Name.StartsWith("I") && t.Name.EndsWith("Service")); + if (interfaceObj != null) + { + builder.RegisterType(type).AsImplementedInterfaces().Named(module.ModuleName, interfaceObj); + builder.RegisterType(type).Named(module.ModuleName, type); + } + } } return result; } + /// + /// 依赖注入注册wcf相关的程序集 + /// + /// ioc容器 + /// 返回注册模块信息 [Obsolete] - public static void InitializeModule(this ContainerBuilder builder) + public static IRegistrationBuilder RegisterWcfServices( + this ContainerBuilder builder) { var referenceAssemblies = builder.GetReferenceAssembly(); + IRegistrationBuilder result = null; if (builder == null) throw new ArgumentNullException("builder"); - var moduleAssemblies = referenceAssemblies.Where( + var interfaceAssemblies = referenceAssemblies.Where( p => - p.GetCustomAttribute().Type == ModuleType.SystemModule || - p.GetCustomAttribute().Type == ModuleType.BusinessModule).ToList(); - foreach (var moduleAssembly in moduleAssemblies) + p.GetCustomAttribute().Type == ModuleType.WcfService).ToList(); + foreach (var interfaceAssembly in interfaceAssemblies) { - GetAbstractModules(moduleAssembly).ForEach(p => p.Initialize()); + result = builder.RegisterAssemblyTypes(interfaceAssembly) + .Where(t => t.Name.EndsWith("Service")).SingleInstance(); } + return result; } + /// + /// The GetAbstractModules + /// + /// The assembly + /// The private static List GetAbstractModules(Assembly assembly) { var abstractModules = new List(); @@ -260,8 +328,6 @@ private static List GetAbstractModules(Assembly assembly) t => t.GetTypeInfo().IsSubclassOf(typeof(AbstractModule)) && t.Name.EndsWith("Module")).ToArray(); foreach (var moduleType in arrayModule) { - #region 模块描述 Attribute - var moduleDescriptionAttribute = GetTypeCustomAttribute(moduleType); if (moduleDescriptionAttribute == null) @@ -270,57 +336,45 @@ private static List GetAbstractModules(Assembly assembly) moduleType.AssemblyQualifiedName)); } - #endregion - var abstractModule = (AbstractModule)Activator.CreateInstance(moduleType); abstractModules.Add(abstractModule); } return abstractModules; } - [Obsolete] - public static List GetInterfaceService(this ContainerBuilder builder) + /// + /// The GetAllAssemblyFiles + /// + /// The parentDir + /// The pattern + /// The + private static List GetAllAssemblyFiles(string parentDir, string pattern) { - var referenceAssemblies = builder.GetReferenceAssembly(); - var interfaceAssemblies = referenceAssemblies.Where( - p => - p.GetCustomAttribute().Type == ModuleType.InterFaceService || - p.GetCustomAttribute().Type == ModuleType.SystemModule).ToList(); - var types = new List(); - interfaceAssemblies.ForEach(p => - { - types.AddRange(p.GetTypes().Where(t => t.Name.StartsWith("I")).Where(t => t.Name.EndsWith("Service"))); - }); - return types; + return + Directory.GetFiles(parentDir, "*.dll").Select(Path.GetFileNameWithoutExtension).Where( + a => Regex.IsMatch(a, pattern)).ToList(); } /// - /// 获取指定类型的自定义特性对象。 + /// The RegisterModules /// - /// 类型参数:自定义特性的类型。 - /// 类型。 - /// 返回指定类型的自定义特性对象。 - /// - /// 创建:范亮 - /// 日期:2015/12/5 - /// + /// The builder + /// The assembly + /// The [Obsolete] - public static TAttributeType GetTypeCustomAttribute(Type type) where TAttributeType : Attribute + private static IModuleRegistrar RegisterModules( + this ContainerBuilder builder, Assembly assembly) { - var attributes = type.GetTypeInfo().GetCustomAttributes(typeof(TAttributeType), true); - var attr = default(TAttributeType); - if (attributes.Count() > 0) + IModuleRegistrar result = default(IModuleRegistrar); + + GetAbstractModules(assembly).ForEach(p => { - attr = attributes.First() as TAttributeType; - } - return attr; - } + result = builder.RegisterModule(p); + }); - private static List GetAllAssemblyFiles(string parentDir, string pattern) - { - return - Directory.GetFiles(parentDir, "*.dll").Select(Path.GetFileNameWithoutExtension).Where( - a => Regex.IsMatch(a, pattern)).ToList(); + return result; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/Module/AbstractModule.cs b/src/Surging.Core/Surging.Core.System/Module/AbstractModule.cs index 1532512fe..3d4171fea 100644 --- a/src/Surging.Core/Surging.Core.System/Module/AbstractModule.cs +++ b/src/Surging.Core/Surging.Core.System/Module/AbstractModule.cs @@ -1,9 +1,9 @@ using Autofac; using Surging.Core.Common.ServicesException; -using System.Reflection; using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -13,148 +13,110 @@ namespace Surging.Core.System.Module /// /// 抽象模块业务模块和系统模块的基类。 /// - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// public abstract class AbstractModule : Autofac.Module { - #region 实例属性 + #region 属性 + /// + /// Gets or sets the Builder + /// public ContainerBuilderWrapper Builder { get; set; } + /// - /// 获取或设置模块标识符 GUID 。 + /// Gets or sets the Components + /// 获取或设置模块组件(定义了接口+实现类)列表。 /// - /// - /// 模块全局标识符 GUID 。 - /// - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// - public Guid Identifier { get; set; } + public List Components { get; set; } /// - /// 获取或设置模块名称(对应视图目录名称)唯一键。 + /// Gets or sets the Description + /// 获取或设置模块功能描述。 /// - /// - /// 模块的名称需大小写字母组合。 - /// - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// - - public string ModuleName { get; set; } + public string Description { get; set; } /// - /// 获取或设置模块类型名称(包含程序集名称的限定名)。 + /// Gets or sets the Identifier + /// 获取或设置模块标识符 GUID 。 /// - /// - /// 模块类型名称。 - /// - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// - - public string TypeName { get; set; } + public Guid Identifier { get; set; } /// - /// 获取或设置模块标题文本。 + /// Gets or sets the ModuleName + /// 获取或设置模块名称(对应视图目录名称)唯一键。 /// - /// - /// 标题文本。 - /// - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// - - public string Title { get; set; } + public string ModuleName { get; set; } /// - /// 获取或设置模块功能描述。 + /// Gets or sets the Title + /// 获取或设置模块标题文本。 /// - /// - /// 模块功能描述文本。 - /// - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// - public string Description { get; set; } + public string Title { get; set; } /// - /// 获取或设置模块组件(定义了接口+实现类)列表。 + /// Gets or sets the TypeName + /// 获取或设置模块类型名称(包含程序集名称的限定名)。 /// - /// - /// 组件列表 List 泛型集合。 - /// - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// - public List Components { get; set; } + public string TypeName { get; set; } - #endregion + #endregion 属性 - #region 实例方法 + #region 方法 /// /// 初始化模块,该操作在应用程序启动时执行。 /// - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// public virtual void Initialize() { } /// - /// 加载组件到依赖注入容器。 + /// 获取模块的字符串文本描述信息。 /// - /// 容器构建对象。 - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// - protected override void Load(ContainerBuilder builder) + /// The + public override string ToString() { - try - { - base.Load(builder); - Builder = new ContainerBuilderWrapper(builder); - RegisterBuilder(Builder); - RegisterComponents(Builder); - } - catch (Exception ex) + StringBuilder sb = new StringBuilder(); + sb.AppendFormat("标识符:{0}", Identifier); + sb.AppendLine(); + sb.AppendFormat("模块名:{0}", ModuleName); + sb.AppendLine(); + sb.AppendFormat("类型名:{0}", TypeName); + sb.AppendLine(); + sb.AppendFormat("标题:{0}", Title); + sb.AppendLine(); + sb.AppendFormat("描述:{0}", Description); + sb.AppendLine(); + sb.AppendFormat("组件详细 {0}个", Components.Count); + sb.AppendLine(); + Components.ForEach(c => { - throw new ServiceException(string.Format("注册模块组件类型时发生错误:{0}", ex.Message)); - } + sb.AppendLine(c.ToString()); + }); + return sb.ToString(); } /// - /// 注册构建。 + /// 验证校验模块。 /// - /// 容器构建对象。 - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// - protected virtual void RegisterBuilder(ContainerBuilderWrapper builder) + public virtual void ValidateModule() { + if (this.Identifier == Guid.Empty || string.IsNullOrEmpty(this.ModuleName) || string.IsNullOrEmpty(this.TypeName) + || string.IsNullOrEmpty(this.Title)) + { + throw new ServiceException("模块属性:Identifier,ModuleName,TypeName,Title 是必须的不能为空!"); + } + + Regex regex = new Regex(@"^[a-zA-Z][a-zA-Z0-9_]*$"); + if (!regex.IsMatch(this.ModuleName)) + { + throw new ServiceException("模块属性:ModuleName 必须为字母开头数字或下划线的组合!"); + } } /// /// 注册组件到依赖注入容器。 /// /// 容器构建对象。 - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// internal virtual void RegisterComponents(ContainerBuilderWrapper builder) { if (Components != null) @@ -179,6 +141,7 @@ internal virtual void RegisterComponents(ContainerBuilderWrapper builder) builder.RegisterType(implementType).As(serviceType).InstancePerDependency(); } break; + case LifetimeScope.SingleInstance://单例 if (serviceType.GetTypeInfo().IsGenericType || implementType.GetTypeInfo().IsGenericType) { @@ -189,6 +152,7 @@ internal virtual void RegisterComponents(ContainerBuilderWrapper builder) builder.RegisterType(implementType).As(serviceType).SingleInstance(); } break; + default://默认依赖创建 if (serviceType.GetTypeInfo().IsGenericType || implementType.GetTypeInfo().IsGenericType) { @@ -200,65 +164,37 @@ internal virtual void RegisterComponents(ContainerBuilderWrapper builder) } break; } - }); } } /// - /// 验证校验模块。 + /// 加载组件到依赖注入容器。 /// - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// - public virtual void ValidateModule() + /// 容器构建对象。 + protected override void Load(ContainerBuilder builder) { - if (this.Identifier == Guid.Empty || string.IsNullOrEmpty(this.ModuleName) || string.IsNullOrEmpty(this.TypeName) - || string.IsNullOrEmpty(this.Title)) + try { - throw new ServiceException("模块属性:Identifier,ModuleName,TypeName,Title 是必须的不能为空!"); + base.Load(builder); + Builder = new ContainerBuilderWrapper(builder); + RegisterBuilder(Builder); + RegisterComponents(Builder); } - - Regex regex = new Regex(@"^[a-zA-Z][a-zA-Z0-9_]*$"); - if (!regex.IsMatch(this.ModuleName)) + catch (Exception ex) { - throw new ServiceException("模块属性:ModuleName 必须为字母开头数字或下划线的组合!"); + throw new ServiceException(string.Format("注册模块组件类型时发生错误:{0}", ex.Message)); } } /// - /// 获取模块的字符串文本描述信息。 + /// 注册构建。 /// - /// - /// 返回模块对象的字符串文本描述信息。 - /// - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// - public override string ToString() + /// 容器构建对象。 + protected virtual void RegisterBuilder(ContainerBuilderWrapper builder) { - StringBuilder sb = new StringBuilder(); - sb.AppendFormat("标识符:{0}", Identifier); - sb.AppendLine(); - sb.AppendFormat("模块名:{0}", ModuleName); - sb.AppendLine(); - sb.AppendFormat("类型名:{0}", TypeName); - sb.AppendLine(); - sb.AppendFormat("标题:{0}", Title); - sb.AppendLine(); - sb.AppendFormat("描述:{0}", Description); - sb.AppendLine(); - sb.AppendFormat("组件详细 {0}个", Components.Count); - sb.AppendLine(); - Components.ForEach(c => - { - sb.AppendLine(c.ToString()); - }); - return sb.ToString(); } - #endregion + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/Module/Attributes/AssemblyDisableStopAndUninstalledAttribute.cs b/src/Surging.Core/Surging.Core.System/Module/Attributes/AssemblyDisableStopAndUninstalledAttribute.cs index 187ef33f1..c703aaa91 100644 --- a/src/Surging.Core/Surging.Core.System/Module/Attributes/AssemblyDisableStopAndUninstalledAttribute.cs +++ b/src/Surging.Core/Surging.Core.System/Module/Attributes/AssemblyDisableStopAndUninstalledAttribute.cs @@ -6,48 +6,30 @@ namespace Surging.Core.System.Module.Attributes /// /// AssemblyDisableStopAndUninstalled 自定义特性类。 /// - /// - /// 创建:范亮 - /// 日期:2015/12/8 - /// [AttributeUsage(AttributeTargets.Assembly, Inherited = false), ComVisible(true)] public sealed class AssemblyDisableStopAndUninstalledAttribute : Attribute { - #region 属性 + #region 构造函数 /// - /// 获取一个值指示是否禁止停止和卸载。 + /// Initializes a new instance of the class. /// - /// - /// 如果 true 禁止停止和卸载; 否则, false 允许停止和卸载。 - /// - /// - /// 创建:范亮 - /// 日期:2015/12/8 - /// - public bool DisableStopAndUninstalled + /// 禁止停止和卸载。 + public AssemblyDisableStopAndUninstalledAttribute(bool disableStopAndUninstalled) { - get; - private set; + DisableStopAndUninstalled = disableStopAndUninstalled; } - #endregion + #endregion 构造函数 - #region 方法 + #region 属性 /// - /// 初始化一个新的 类实例。 + /// Gets a value indicating whether DisableStopAndUninstalled + /// 获取一个值指示是否禁止停止和卸载。 /// - /// 禁止停止和卸载。 - /// - /// 创建:范亮 - /// 日期:2015/12/8 - /// - public AssemblyDisableStopAndUninstalledAttribute(bool disableStopAndUninstalled) - { - DisableStopAndUninstalled = disableStopAndUninstalled; - } + public bool DisableStopAndUninstalled { get; private set; } - #endregion + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/Module/Attributes/AssemblyModuleTypeAttribute.cs b/src/Surging.Core/Surging.Core.System/Module/Attributes/AssemblyModuleTypeAttribute.cs index 3609a76a2..847f36ca4 100644 --- a/src/Surging.Core/Surging.Core.System/Module/Attributes/AssemblyModuleTypeAttribute.cs +++ b/src/Surging.Core/Surging.Core.System/Module/Attributes/AssemblyModuleTypeAttribute.cs @@ -1,53 +1,51 @@ - -using System; +using System; using System.Runtime.InteropServices; namespace Surging.Core.System.Module.Attributes { + /// + /// Defines the + /// [AttributeUsage(AttributeTargets.Assembly, Inherited = false), ComVisible(true)] - public sealed class AssemblyModuleTypeAttribute:Attribute + public sealed class AssemblyModuleTypeAttribute : Attribute { - #region 属性 + #region 构造函数 /// - /// 获取模块类型 + /// Initializes a new instance of the class. /// - /// - /// 创建:范亮 - /// 日期:2015/12/8 - /// - public ModuleType Type + /// The type + public AssemblyModuleTypeAttribute(ModuleType type) { - get; - private set; + Type = type; } - public int SerialNumber { get; private set; } - - #endregion - - #region 方法 - /// - /// 初始化一个新的 类实例。 + /// Initializes a new instance of the class. /// /// 模块类型。 /// 序号 - /// - /// 创建:范亮 - /// 日期:2015/12/8 - /// public AssemblyModuleTypeAttribute(ModuleType type, int serialNumber) { Type = type; SerialNumber = serialNumber; } + #endregion 构造函数 - public AssemblyModuleTypeAttribute(ModuleType type) - { - Type = type; - } - #endregion + #region 属性 + + /// + /// Gets the SerialNumber + /// + public int SerialNumber { get; private set; } + + /// + /// Gets the Type + /// 获取模块类型 + /// + public ModuleType Type { get; private set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/Module/Attributes/ModelBinderTypeAttribute.cs b/src/Surging.Core/Surging.Core.System/Module/Attributes/ModelBinderTypeAttribute.cs index 9fa2c13af..9843cd328 100644 --- a/src/Surging.Core/Surging.Core.System/Module/Attributes/ModelBinderTypeAttribute.cs +++ b/src/Surging.Core/Surging.Core.System/Module/Attributes/ModelBinderTypeAttribute.cs @@ -12,29 +12,38 @@ namespace Surging.Core.System.Module.Attributes [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] public sealed class ModelBinderTypeAttribute : Attribute { + #region 构造函数 + /// - /// 目标类型 + /// Initializes a new instance of the class. /// - public IEnumerable TargetTypes { get; private set; } - - /// - /// 初始化一个新的 类实例。 - /// - /// 目标类型列表 + /// 目标类型列表 public ModelBinderTypeAttribute(params Type[] targetTypes) { if (targetTypes == null) throw new ArgumentNullException("targetTypes"); TargetTypes = targetTypes; } - /// - /// 初始化一个新的 类实例。 - /// - /// 目标类型 + /// + /// Initializes a new instance of the class. + /// + /// 目标类型 public ModelBinderTypeAttribute(Type targetType) { if (targetType == null) throw new ArgumentNullException("targetType"); TargetTypes = new Type[] { targetType }; } + + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the TargetTypes + /// 目标类型 + /// + public IEnumerable TargetTypes { get; private set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/Module/Attributes/ModuleDescriptionAttribute.cs b/src/Surging.Core/Surging.Core.System/Module/Attributes/ModuleDescriptionAttribute.cs index e985ecb2b..1b7a38632 100644 --- a/src/Surging.Core/Surging.Core.System/Module/Attributes/ModuleDescriptionAttribute.cs +++ b/src/Surging.Core/Surging.Core.System/Module/Attributes/ModuleDescriptionAttribute.cs @@ -9,75 +9,46 @@ namespace Surging.Core.System.Module.Attributes /// /// ModuleDescriptionAttribute 自定义特性类。 /// - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// [AttributeUsage(AttributeTargets.Class, Inherited = true)] public class ModuleDescriptionAttribute : Attribute { - #region 属性 + #region 构造函数 /// - /// 获取标识符 GUID 。 + /// Initializes a new instance of the class. /// - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// - public string Identifier + /// 标识符 GUID 。 + /// 标题。 + /// 描述。 + public ModuleDescriptionAttribute(string identifier, string title, string description) { - get; - private set; + Identifier = identifier; + Title = title; + Description = description; } - /// - /// 获取标题信息。 - /// - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// - public string Title - { - get; - private set; - } + #endregion 构造函数 + + #region 属性 /// + /// Gets the Description /// 获取描述信息。 /// - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// - public string Description - { - get; - private set; - } + public string Description { get; private set; } - #endregion - - #region 方法 + /// + /// Gets the Identifier + /// 获取标识符 GUID 。 + /// + public string Identifier { get; private set; } /// - /// 初始化一个新的 类实例。 + /// Gets the Title + /// 获取标题信息。 /// - /// 标识符 GUID 。 - /// 标题。 - /// 描述。 - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// - public ModuleDescriptionAttribute(string identifier, string title, string description) - { - Identifier = identifier; - Title = title; - Description = description; - } + public string Title { get; private set; } - #endregion + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/Module/BusinessModule.cs b/src/Surging.Core/Surging.Core.System/Module/BusinessModule.cs index fade9ddc9..cc2da9e55 100644 --- a/src/Surging.Core/Surging.Core.System/Module/BusinessModule.cs +++ b/src/Surging.Core/Surging.Core.System/Module/BusinessModule.cs @@ -8,18 +8,16 @@ namespace Surging.Core.System.Module { /// - /// 业务模块基类 + /// 业务模块基类 /// - [ModuleDescription("13C13D64-693A-4D1E-93EB-B2E1465C24C7", "业务基础模块", "业务基础模块")] - public class BusinessModule : AbstractModule + [ModuleDescription("13C13D64-693A-4D1E-93EB-B2E1465C24C7", "业务基础模块", "业务基础模块")] + public class BusinessModule : AbstractModule { + #region 方法 + /// /// 初始化模块,该操作在应用程序启动时执行。 /// - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// public override void Initialize() { base.Initialize(); @@ -29,13 +27,11 @@ public override void Initialize() /// 注册组件到依赖注入容器。 /// /// 容器构建对象。 - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// internal override void RegisterComponents(ContainerBuilderWrapper builder) { base.RegisterComponents(builder); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/Module/Component.cs b/src/Surging.Core/Surging.Core.System/Module/Component.cs index b29e7074a..89f2ce1cb 100644 --- a/src/Surging.Core/Surging.Core.System/Module/Component.cs +++ b/src/Surging.Core/Surging.Core.System/Module/Component.cs @@ -6,7 +6,7 @@ namespace Surging.Core.System.Module { - #region 组件生命周期枚举类 + #region 枚举 /// /// 组件生命周期枚举。 @@ -45,71 +45,41 @@ public enum LifetimeScope SingleInstance } - #endregion - - #region 组件类 + #endregion 枚举 /// /// 组件描述类(定义了接口+实现类)。 /// - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// public class Component { - #region 实例属性 - - /// - /// 获取或设置接口服务类型名称(包含程序集名称的限定名)。 - /// - /// - /// 接口类型名称字符串。 - /// - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// - public string ServiceType { get; set; } + #region 属性 /// + /// Gets or sets the ImplementType /// 获取或设置接口实现类的类型名称(包含程序集名称的限定名)。 /// - /// - /// 实现类的类型名称字符串。 - /// - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// public string ImplementType { get; set; } /// + /// Gets or sets the LifetimeScope /// 获取或设置组件生命周期枚举。 /// - /// - /// 生命周期枚举值:InstancePerDependency | InstancePerHttpRequest | SingleInstance 。 - /// - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// public LifetimeScope LifetimeScope { get; set; } - #endregion + /// + /// Gets or sets the ServiceType + /// 获取或设置接口服务类型名称(包含程序集名称的限定名)。 + /// + public string ServiceType { get; set; } + + #endregion 属性 - #region 实例方法 + #region 方法 /// /// 获取组件的字符串文本描述信息。 /// - /// - /// 返回组件对象的字符串文本描述信息。 - /// - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// + /// The public override string ToString() { StringBuilder sb = new StringBuilder(); @@ -121,8 +91,6 @@ public override string ToString() return sb.ToString(); } - #endregion + #endregion 方法 } - - #endregion -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/Module/ContainerBuilderWrapper.cs b/src/Surging.Core/Surging.Core.System/Module/ContainerBuilderWrapper.cs index 297faf0f4..e16990916 100644 --- a/src/Surging.Core/Surging.Core.System/Module/ContainerBuilderWrapper.cs +++ b/src/Surging.Core/Surging.Core.System/Module/ContainerBuilderWrapper.cs @@ -7,41 +7,45 @@ namespace Surging.Core.System.Module { - public class ContainerBuilderWrapper + /// + /// Defines the + /// + public class ContainerBuilderWrapper { - /// - /// 获取内部容器构建对象。 - /// - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// - public ContainerBuilder ContainerBuilder { get; private set; } + #region 构造函数 /// - /// 初始化一个新的 类实例。 + /// Initializes a new instance of the class. /// /// 容器构建对象。 - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// public ContainerBuilderWrapper(ContainerBuilder builder) { ContainerBuilder = builder; } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the ContainerBuilder + /// 获取内部容器构建对象。 + /// + public ContainerBuilder ContainerBuilder { get; private set; } + + #endregion 属性 + + #region 方法 + /// /// 构建容器。 /// /// - /// - /// 创建:范亮 - /// 日期:2015/12/4 - /// public IContainer Build() { return ContainerBuilder.Build(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/Module/ModuleType.cs b/src/Surging.Core/Surging.Core.System/Module/ModuleType.cs index 3eefae1f8..5f032132c 100644 --- a/src/Surging.Core/Surging.Core.System/Module/ModuleType.cs +++ b/src/Surging.Core/Surging.Core.System/Module/ModuleType.cs @@ -6,6 +6,8 @@ namespace Surging.Core.System.Module { + #region 枚举 + /// /// 模块类型 /// @@ -15,33 +17,42 @@ public enum ModuleType /// 接口服务 /// InterFaceService, + /// /// 领域 /// Domain, + /// /// 仓储 /// Repository, + /// /// 模块 /// Module, + /// /// 业务模块,包括领域和仓储 /// BusinessModule, + /// /// 系统模块,包括InterFaceService和Module /// SystemModule, + /// /// wcf /// WcfService, + /// /// webapi /// WebApi } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/Module/RegistrationExtensions.cs b/src/Surging.Core/Surging.Core.System/Module/RegistrationExtensions.cs index 64f290e9d..8fb54dd7e 100644 --- a/src/Surging.Core/Surging.Core.System/Module/RegistrationExtensions.cs +++ b/src/Surging.Core/Surging.Core.System/Module/RegistrationExtensions.cs @@ -11,82 +11,175 @@ namespace Surging.Core.System.Module { - public static class RegistrationExtensions + /// + /// Defines the + /// + public static class RegistrationExtensions { - public static IRegistrationBuilder Register(this ContainerBuilderWrapper builder, Func @delegate) + #region 方法 + + /// + /// The AsInheritedClasses + /// + /// + /// The registration + /// The + public static IRegistrationBuilder AsInheritedClasses(this IRegistrationBuilder registration) { - return builder.ContainerBuilder.Register(@delegate); + if (registration == null) throw new ArgumentNullException("registration"); + return registration.As(t => GetInheritedClasses(t)); } + /// + /// The Register + /// + /// + /// The builder + /// The delegate + /// The public static IRegistrationBuilder Register(this ContainerBuilderWrapper builder, Func, T> @delegate) { return builder.ContainerBuilder.Register(@delegate); } - public static void RegisterModule(this ContainerBuilderWrapper builder, IModule module) + /// + /// The Register + /// + /// + /// The builder + /// The delegate + /// The + public static IRegistrationBuilder Register(this ContainerBuilderWrapper builder, Func @delegate) { - builder.ContainerBuilder.RegisterModule(module); + return builder.ContainerBuilder.Register(@delegate); } - public static void RegisterModule(this ContainerBuilderWrapper builder) - where TModule : IModule, new() + /// + /// The RegisterAdapter + /// + /// + /// + /// The builder + /// The adapter + /// The + public static IRegistrationBuilder RegisterAdapter(this ContainerBuilderWrapper builder, Func adapter) { - builder.ContainerBuilder.RegisterModule(); + return builder.ContainerBuilder.RegisterAdapter(adapter); } - public static IRegistrationBuilder RegisterInstance(this ContainerBuilderWrapper builder, T instance) - where T : class + /// + /// The RegisterAssemblyTypes + /// + /// The builder + /// The assemblies + /// The + public static IRegistrationBuilder RegisterAssemblyTypes(this ContainerBuilderWrapper builder, params Assembly[] assemblies) { - return builder.ContainerBuilder.RegisterInstance(instance); + return builder.ContainerBuilder.RegisterAssemblyTypes(assemblies); } - public static IRegistrationBuilder RegisterType(this ContainerBuilderWrapper builder) + /// + /// The RegisterDecorator + /// + /// + /// The builder + /// The decorator + /// The fromKey + /// The toKey + /// The + public static IRegistrationBuilder RegisterDecorator(this ContainerBuilderWrapper builder, Func decorator, object fromKey, object toKey = null) { - return builder.ContainerBuilder.RegisterType(); + return builder.ContainerBuilder.RegisterDecorator(decorator, fromKey, toKey); } - public static IRegistrationBuilder RegisterType(this ContainerBuilderWrapper builder, Type implementationType) + /// + /// The RegisterGeneratedFactory + /// + /// + /// The builder + /// The + public static IRegistrationBuilder RegisterGeneratedFactory(this ContainerBuilderWrapper builder) + where TDelegate : class { - return builder.ContainerBuilder.RegisterType(implementationType); + return builder.ContainerBuilder.RegisterGeneratedFactory(); } - public static IRegistrationBuilder RegisterAssemblyTypes(this ContainerBuilderWrapper builder, params Assembly[] assemblies) + /// + /// The RegisterGeneratedFactory + /// + /// The builder + /// The delegateType + /// The + public static IRegistrationBuilder RegisterGeneratedFactory(this ContainerBuilderWrapper builder, Type delegateType) { - return builder.ContainerBuilder.RegisterAssemblyTypes(assemblies); + return builder.ContainerBuilder.RegisterGeneratedFactory(delegateType); } + /// + /// The RegisterGeneric + /// + /// The builder + /// The implementor + /// The public static IRegistrationBuilder RegisterGeneric(this ContainerBuilderWrapper builder, Type implementor) { return builder.ContainerBuilder.RegisterGeneric(implementor); } - public static IRegistrationBuilder RegisterAdapter(this ContainerBuilderWrapper builder, Func adapter) - { - return builder.ContainerBuilder.RegisterAdapter(adapter); - } - + /// + /// The RegisterGenericDecorator + /// + /// The builder + /// The decoratorType + /// The decoratedServiceType + /// The fromKey + /// The toKey + /// The public static IRegistrationBuilder RegisterGenericDecorator(this ContainerBuilderWrapper builder, Type decoratorType, Type decoratedServiceType, object fromKey, object toKey = null) { return builder.ContainerBuilder.RegisterGenericDecorator(decoratorType, decoratedServiceType, fromKey, toKey); } - public static IRegistrationBuilder RegisterDecorator(this ContainerBuilderWrapper builder, Func decorator, object fromKey, object toKey = null) + /// + /// The RegisterInstance + /// + /// + /// The builder + /// The instance + /// The + public static IRegistrationBuilder RegisterInstance(this ContainerBuilderWrapper builder, T instance) + where T : class { - return builder.ContainerBuilder.RegisterDecorator(decorator, fromKey, toKey); + return builder.ContainerBuilder.RegisterInstance(instance); } - - public static IRegistrationBuilder RegisterGeneratedFactory(this ContainerBuilderWrapper builder, Type delegateType) + /// + /// The RegisterModule + /// + /// + /// The builder + public static void RegisterModule(this ContainerBuilderWrapper builder) + where TModule : IModule, new() { - return builder.ContainerBuilder.RegisterGeneratedFactory(delegateType); + builder.ContainerBuilder.RegisterModule(); } - public static IRegistrationBuilder RegisterGeneratedFactory(this ContainerBuilderWrapper builder) - where TDelegate : class + /// + /// The RegisterModule + /// + /// The builder + /// The module + public static void RegisterModule(this ContainerBuilderWrapper builder, IModule module) { - return builder.ContainerBuilder.RegisterGeneratedFactory(); + builder.ContainerBuilder.RegisterModule(module); } + /// + /// The RegisterRepositories + /// + /// The builder + /// The repositoriesAssemblies + /// The public static IRegistrationBuilder RegisterRepositories(this ContainerBuilderWrapper builder, params Assembly[] repositoriesAssemblies) { return builder.RegisterAssemblyTypes(repositoriesAssemblies) @@ -95,6 +188,12 @@ public static IRegistrationBuilder + /// The RegisterServices + /// + /// The builder + /// The serviceAssemblies + /// The public static IRegistrationBuilder RegisterServices(this ContainerBuilderWrapper builder, params Assembly[] serviceAssemblies) { return builder.RegisterAssemblyTypes(serviceAssemblies) @@ -103,12 +202,33 @@ public static IRegistrationBuilder AsInheritedClasses(this IRegistrationBuilder registration) + /// + /// The RegisterType + /// + /// + /// The builder + /// The + public static IRegistrationBuilder RegisterType(this ContainerBuilderWrapper builder) { - if (registration == null) throw new ArgumentNullException("registration"); - return registration.As(t => GetInheritedClasses(t)); + return builder.ContainerBuilder.RegisterType(); + } + + /// + /// The RegisterType + /// + /// The builder + /// The implementationType + /// The + public static IRegistrationBuilder RegisterType(this ContainerBuilderWrapper builder, Type implementationType) + { + return builder.ContainerBuilder.RegisterType(implementationType); } + /// + /// The GetInheritedClasses + /// + /// The type + /// The private static List GetInheritedClasses(Type type) { List types = new List(); @@ -134,5 +254,7 @@ private static List GetInheritedClasses(Type type) return types; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/Module/SystemModule.cs b/src/Surging.Core/Surging.Core.System/Module/SystemModule.cs index 11c768a2f..13be0e3fc 100644 --- a/src/Surging.Core/Surging.Core.System/Module/SystemModule.cs +++ b/src/Surging.Core/Surging.Core.System/Module/SystemModule.cs @@ -13,13 +13,11 @@ namespace Surging.Core.System.Module [ModuleDescription("12C16D64-693A-4D1E-93EB-B2E1465C24C7", "系统基础模块", "系统基础模块")] public class SystemModule : AbstractModule { + #region 方法 + /// /// 初始化模块,该操作在应用程序启动时执行。 /// - /// - /// 创建:范亮 - /// 日期:2015/12/5 - /// public override void Initialize() { base.Initialize(); @@ -29,13 +27,11 @@ public override void Initialize() /// 注册组件到依赖注入容器。 /// /// 容器构建对象。 - /// - /// 创建:范亮 - /// 日期:2015/12/5 - /// internal override void RegisterComponents(ContainerBuilderWrapper builder) { base.RegisterComponents(builder); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/MongoProvider/Attributes/CollectionNameAttribute.cs b/src/Surging.Core/Surging.Core.System/MongoProvider/Attributes/CollectionNameAttribute.cs index 252a271f3..ab259c3b9 100644 --- a/src/Surging.Core/Surging.Core.System/MongoProvider/Attributes/CollectionNameAttribute.cs +++ b/src/Surging.Core/Surging.Core.System/MongoProvider/Attributes/CollectionNameAttribute.cs @@ -4,9 +4,18 @@ namespace Surging.Core.System.MongoProvider.Attributes { + /// + /// Defines the + /// [AttributeUsage(AttributeTargets.Class, Inherited = true)] public class CollectionNameAttribute : Attribute { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The value public CollectionNameAttribute(string value) { if (string.IsNullOrWhiteSpace(value)) @@ -16,6 +25,16 @@ public CollectionNameAttribute(string value) Name = value; } + + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the Name + /// public string Name { get; private set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/MongoProvider/Entity.cs b/src/Surging.Core/Surging.Core.System/MongoProvider/Entity.cs index e9517530a..d11522249 100644 --- a/src/Surging.Core/Surging.Core.System/MongoProvider/Entity.cs +++ b/src/Surging.Core/Surging.Core.System/MongoProvider/Entity.cs @@ -6,10 +6,20 @@ namespace Surging.Core.System.MongoProvider { + /// + /// Defines the + /// [BsonIgnoreExtraElements(Inherited = true)] public abstract class Entity : IEntity - { + { + #region 属性 + + /// + /// Gets or sets the Id + /// [BsonRepresentation(BsonType.ObjectId)] public string Id { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/MongoProvider/IEntity.cs b/src/Surging.Core/Surging.Core.System/MongoProvider/IEntity.cs index 2ba853902..ec467b879 100644 --- a/src/Surging.Core/Surging.Core.System/MongoProvider/IEntity.cs +++ b/src/Surging.Core/Surging.Core.System/MongoProvider/IEntity.cs @@ -1,11 +1,25 @@ -using System; -using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Bson.Serialization.Attributes; +using System; namespace Surging.Core.System.MongoProvider { + #region 接口 + + /// + /// Defines the + /// public interface IEntity { + #region 属性 + + /// + /// Gets or sets the Id + /// [BsonId] string Id { get; set; } + + #endregion 属性 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/MongoProvider/MongoConfig.cs b/src/Surging.Core/Surging.Core.System/MongoProvider/MongoConfig.cs index c39997a30..4c5b08212 100644 --- a/src/Surging.Core/Surging.Core.System/MongoProvider/MongoConfig.cs +++ b/src/Surging.Core/Surging.Core.System/MongoProvider/MongoConfig.cs @@ -5,17 +5,44 @@ namespace Surging.Core.System.MongoProvider { + /// + /// Defines the + /// public class MongoConfig { - private static MongoConfig _configuration; + #region 字段 + + /// + /// Defines the _config + /// private readonly IConfigurationRoot _config; + /// + /// Defines the _configuration + /// + private static MongoConfig _configuration; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The Configuration public MongoConfig(IConfigurationRoot Configuration) { _config = Configuration; _configuration = this; } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the DefaultInstance + /// public static MongoConfig DefaultInstance { get @@ -23,6 +50,10 @@ public static MongoConfig DefaultInstance return _configuration; } } + + /// + /// Gets the MongConnectionString + /// public string MongConnectionString { get @@ -31,5 +62,6 @@ public string MongConnectionString } } + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/MongoProvider/MongoModule.cs b/src/Surging.Core/Surging.Core.System/MongoProvider/MongoModule.cs index c19d08069..6b316c9a6 100644 --- a/src/Surging.Core/Surging.Core.System/MongoProvider/MongoModule.cs +++ b/src/Surging.Core/Surging.Core.System/MongoProvider/MongoModule.cs @@ -8,11 +8,17 @@ namespace Surging.Core.System.MongoProvider { + /// + /// Defines the + /// public class MongoModule : SystemModule { + #region 方法 + /// - /// Function module initialization,trigger when the module starts loading + /// Function module initialization,trigger when the module starts loading /// + /// The context public override void Initialize(AppModuleContext context) { base.Initialize(context); @@ -27,5 +33,7 @@ protected override void RegisterBuilder(ContainerBuilderWrapper builder) base.RegisterBuilder(builder); builder.RegisterGeneric(typeof(MongoRepository<>)).As(typeof(IMongoRepository<>)).SingleInstance(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/MongoProvider/QueryParams.cs b/src/Surging.Core/Surging.Core.System/MongoProvider/QueryParams.cs index 4fcb250cc..30f755212 100644 --- a/src/Surging.Core/Surging.Core.System/MongoProvider/QueryParams.cs +++ b/src/Surging.Core/Surging.Core.System/MongoProvider/QueryParams.cs @@ -4,37 +4,85 @@ namespace Surging.Core.System.MongoProvider { + /// + /// Defines the + /// public class QueryParams { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public QueryParams() { Index = 1; Size = 15; } - public int Total { get; set; } + #endregion 构造函数 + + #region 属性 + /// + /// Gets or sets the Index + /// public int Index { get; set; } + /// + /// Gets or sets the Size + /// public int Size { get; set; } + /// + /// Gets or sets the Total + /// + public int Total { get; set; } + + #endregion 属性 } + /// + /// Defines the + /// + /// public class QueryParams { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public QueryParams() { Index = 1; Size = 15; } - public int Total { get; set; } + #endregion 构造函数 - public T Params { get; set; } + #region 属性 + /// + /// Gets or sets the Index + /// public int Index { get; set; } + /// + /// Gets or sets the Params + /// + public T Params { get; set; } + + /// + /// Gets or sets the Size + /// public int Size { get; set; } + /// + /// Gets or sets the Total + /// + public int Total { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/MongoProvider/Repositories/IMongoRepository.cs b/src/Surging.Core/Surging.Core.System/MongoProvider/Repositories/IMongoRepository.cs index 116da544d..8704bd698 100644 --- a/src/Surging.Core/Surging.Core.System/MongoProvider/Repositories/IMongoRepository.cs +++ b/src/Surging.Core/Surging.Core.System/MongoProvider/Repositories/IMongoRepository.cs @@ -1,47 +1,240 @@ -using System; +using MongoDB.Driver; +using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Text; using System.Threading.Tasks; - -using MongoDB.Driver; namespace Surging.Core.System.MongoProvider.Repositories { - public interface IMongoRepository where T : IEntity + #region 接口 + + /// + /// Defines the + /// + /// + public interface IMongoRepository where T : IEntity { + #region 属性 + + /// + /// Gets the Collection + /// IMongoCollection Collection { get; } - T GetById(string id); - T GetSingle(Expression> criteria); - Task GetSingleAsync(Expression> criteria); - Task> GetListAsync(Expression> criteria); - List GetList(Expression> criteria); - IQueryable All(); - IQueryable All(Expression> criteria); + + #endregion 属性 + + #region 方法 + + /// + /// The Add + /// + /// The entities + /// The + bool Add(IEnumerable entities); + + /// + /// The Add + /// + /// The entity + /// The T Add(T entity); + + /// + /// The AddAsync + /// + /// The entity + /// The Task AddAsync(T entity); - bool Add(IEnumerable entities); + + /// + /// The AddManyAsync + /// + /// The entities + /// The Task AddManyAsync(IEnumerable entities); - UpdateResult Update(FilterDefinition filter, UpdateDefinition entity); - Task UpdateAsync(FilterDefinition filter, UpdateDefinition entity); - UpdateResult UpdateMany(FilterDefinition filter, UpdateDefinition entity); - Task UpdateManyAsync(FilterDefinition filter, UpdateDefinition entity); - T FindOneAndUpdate(FilterDefinition filter, UpdateDefinition entity); - Task FindOneAndUpdateAsync(FilterDefinition filter, UpdateDefinition entity); + + /// + /// The All + /// + /// The + IQueryable All(); + + /// + /// The All + /// + /// The criteria + /// The + IQueryable All(Expression> criteria); + + /// + /// The Count + /// + /// The filter + /// The + long Count(FilterDefinition filter); + + /// + /// The CountAsync + /// + /// The filter + /// The + Task CountAsync(FilterDefinition filter); + + /// + /// The Delete + /// + /// The filter + /// The DeleteResult Delete(FilterDefinition filter); + + /// + /// The DeleteAsync + /// + /// The filter + /// The Task DeleteAsync(FilterDefinition filter); + + /// + /// The DeleteMany + /// + /// The filter + /// The DeleteResult DeleteMany(FilterDefinition filter); + + /// + /// The DeleteManyAsync + /// + /// The filter + /// The Task DeleteManyAsync(FilterDefinition filter); - List GetPageAsc(Expression> criteria, Expression> ascSort, QueryParams pParams); - List GetPageDesc(Expression> criteria, Expression> descSort, QueryParams pParams); + + /// + /// The Exists + /// + /// The criteria + /// The exists + /// The + bool Exists(Expression> criteria, bool exists); + + /// + /// The FindOneAndDelete + /// + /// The criteria + /// The T FindOneAndDelete(Expression> criteria); + + /// + /// The FindOneAndDeleteAsync + /// + /// The criteria + /// The Task FindOneAndDeleteAsync(Expression> criteria); - long Count(FilterDefinition filter); - Task CountAsync(FilterDefinition filter); - bool Exists(Expression> criteria, bool exists); - } -} + /// + /// The FindOneAndUpdate + /// + /// The filter + /// The entity + /// The + T FindOneAndUpdate(FilterDefinition filter, UpdateDefinition entity); + + /// + /// The FindOneAndUpdateAsync + /// + /// The filter + /// The entity + /// The + Task FindOneAndUpdateAsync(FilterDefinition filter, UpdateDefinition entity); + + /// + /// The GetById + /// + /// The id + /// The + T GetById(string id); + + /// + /// The GetList + /// + /// The criteria + /// The + List GetList(Expression> criteria); + + /// + /// The GetListAsync + /// + /// The criteria + /// The + Task> GetListAsync(Expression> criteria); + /// + /// The GetPageAsc + /// + /// The criteria + /// The ascSort + /// The pParams + /// The + List GetPageAsc(Expression> criteria, Expression> ascSort, QueryParams pParams); + + /// + /// The GetPageDesc + /// + /// The criteria + /// The descSort + /// The pParams + /// The + List GetPageDesc(Expression> criteria, Expression> descSort, QueryParams pParams); + + /// + /// The GetSingle + /// + /// The criteria + /// The + T GetSingle(Expression> criteria); + + /// + /// The GetSingleAsync + /// + /// The criteria + /// The + Task GetSingleAsync(Expression> criteria); + + /// + /// The Update + /// + /// The filter + /// The entity + /// The + UpdateResult Update(FilterDefinition filter, UpdateDefinition entity); + + /// + /// The UpdateAsync + /// + /// The filter + /// The entity + /// The + Task UpdateAsync(FilterDefinition filter, UpdateDefinition entity); + + /// + /// The UpdateMany + /// + /// The filter + /// The entity + /// The + UpdateResult UpdateMany(FilterDefinition filter, UpdateDefinition entity); + + /// + /// The UpdateManyAsync + /// + /// The filter + /// The entity + /// The + Task UpdateManyAsync(FilterDefinition filter, UpdateDefinition entity); + + #endregion 方法 + } + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/MongoProvider/Repositories/MongoRepository.cs b/src/Surging.Core/Surging.Core.System/MongoProvider/Repositories/MongoRepository.cs index 756fea279..869b91a66 100644 --- a/src/Surging.Core/Surging.Core.System/MongoProvider/Repositories/MongoRepository.cs +++ b/src/Surging.Core/Surging.Core.System/MongoProvider/Repositories/MongoRepository.cs @@ -4,87 +4,85 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -using System.Threading.Tasks; - +using System.Threading.Tasks; namespace Surging.Core.System.MongoProvider.Repositories { + /// + /// Defines the + /// + /// public class MongoRepository : IMongoRepository where T : IEntity { + #region 字段 + + /// + /// Defines the _collection + /// private readonly IMongoCollection _collection; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public MongoRepository() : this(Util.GetDefaultConnectionString()) { } + /// + /// Initializes a new instance of the class. + /// + /// The connectionString public MongoRepository(string connectionString) { _collection = Util.GetCollectionFromConnectionString(Util.GetDefaultConnectionString()); } - public IMongoCollection Collection - { - get { return _collection; } - } + #endregion 构造函数 - public T GetById(string id) - { - var builder = Builders.Filter; - var filter = builder.Eq("_id", ObjectId.Parse(id)); - return _collection.Find(filter).FirstOrDefault(); - } - - public T GetSingle(Expression> criteria) - { - return _collection.Find(criteria).FirstOrDefault(); - } - - public async Task GetSingleAsync(Expression> criteria) - { - var result = await _collection.FindSync(criteria).FirstOrDefaultAsync(); - return result; - } + #region 属性 - public List GetPageDesc(Expression> criteria, Expression> descSort, QueryParams pParams) + /// + /// Gets the Collection + /// + public IMongoCollection Collection { - var sort = new SortDefinitionBuilder(); - var result = _collection.Find(criteria).Sort(sort.Descending(descSort)).Skip((pParams.Index - 1) * pParams.Size).Limit( - pParams.Size).ToList(); - return result; + get { return _collection; } } - public List GetPageAsc(Expression> criteria, Expression> ascSort, QueryParams pParams) - { - var sort = new SortDefinitionBuilder(); - var result = _collection.Find(criteria).Sort(sort.Ascending(ascSort)).Skip((pParams.Index - 1) * pParams.Size).Limit( - pParams.Size).ToList(); - return result; - } + #endregion 属性 - public async Task> GetListAsync(Expression> criteria) - { - var result = await _collection.FindSync(criteria).ToListAsync(); - return result; - } + #region 方法 - public List GetList(Expression> criteria) + /// + /// The Add + /// + /// The entities + /// The + public bool Add(IEnumerable entities) { - var result = _collection.Find(criteria).ToList(); + var result = true; + try + { + _collection.InsertMany(entities); + } + catch + { + result = false; + } return result; } - public IQueryable All() - { - return _collection.AsQueryable(); - } - - public IQueryable All(Expression> criteria) - { - return _collection.AsQueryable().Where(criteria); - } - + /// + /// The Add + /// + /// The entity + /// The public T Add(T entity) { try @@ -98,6 +96,11 @@ public T Add(T entity) } } + /// + /// The AddAsync + /// + /// The entity + /// The public async Task AddAsync(T entity) { var result = true; @@ -112,12 +115,17 @@ public async Task AddAsync(T entity) return result; } - public bool Add(IEnumerable entities) + /// + /// The AddManyAsync + /// + /// The entities + /// The + public async Task AddManyAsync(IEnumerable entities) { var result = true; try { - _collection.InsertMany(entities); + await _collection.InsertManyAsync(entities); } catch { @@ -126,111 +134,284 @@ public bool Add(IEnumerable entities) return result; } - public async Task AddManyAsync(IEnumerable entities) + /// + /// The All + /// + /// The + public IQueryable All() { - var result = true; - try - { - await _collection.InsertManyAsync(entities); - } - catch - { - result = false; - } + return _collection.AsQueryable(); + } + + /// + /// The All + /// + /// The criteria + /// The + public IQueryable All(Expression> criteria) + { + return _collection.AsQueryable().Where(criteria); + } + + /// + /// The Count + /// + /// The filter + /// The + public long Count(FilterDefinition filter) + { + var result = _collection.Count(filter); return result; } - public UpdateResult Update(FilterDefinition filter, UpdateDefinition entity) + /// + /// The CountAsync + /// + /// The filter + /// The + public async Task CountAsync(FilterDefinition filter) { - return _collection.UpdateOne(filter, entity, new UpdateOptions() - { - IsUpsert = true, - BypassDocumentValidation = true, - }); + var result = await _collection.CountAsync(filter); + return result; } - public async Task UpdateAsync(FilterDefinition filter, UpdateDefinition entity) + /// + /// The Delete + /// + /// The filter + /// The + public DeleteResult Delete(FilterDefinition filter) { - var result = await _collection.UpdateOneAsync(filter, entity); + return _collection.DeleteOne(filter); + } + + /// + /// The DeleteAsync + /// + /// The filter + /// The + public async Task DeleteAsync(FilterDefinition filter) + { + var result = await _collection.DeleteOneAsync(filter); return result; } - public UpdateResult UpdateMany(FilterDefinition filter, UpdateDefinition entity) + /// + /// The DeleteMany + /// + /// The filter + /// The + public DeleteResult DeleteMany(FilterDefinition filter) { - return _collection.UpdateMany(filter, entity); + var result = _collection.DeleteMany(filter); + return result; } - public async Task UpdateManyAsync(FilterDefinition filter, UpdateDefinition entity) + /// + /// The DeleteManyAsync + /// + /// The filter + /// The + public async Task DeleteManyAsync(FilterDefinition filter) { - var result = await _collection.UpdateManyAsync(filter, entity); + var result = await _collection.DeleteManyAsync(filter); + return result; + } + + /// + /// The Exists + /// + /// The criteria + /// The exists + /// The + public bool Exists(Expression> criteria, bool exists) + { + var builder = Builders.Filter; + var filter = builder.Exists(criteria, exists); + return _collection.Find(filter).Any(); + } + + /// + /// The FindOneAndDelete + /// + /// The criteria + /// The + public T FindOneAndDelete(Expression> criteria) + { + var result = _collection.FindOneAndDelete(criteria); return result; } + /// + /// The FindOneAndDeleteAsync + /// + /// The criteria + /// The + public async Task FindOneAndDeleteAsync(Expression> criteria) + { + var result = await _collection.FindOneAndDeleteAsync(criteria); + return result; + } + + /// + /// The FindOneAndUpdate + /// + /// The filter + /// The entity + /// The public T FindOneAndUpdate(FilterDefinition filter, UpdateDefinition entity) { var result = _collection.FindOneAndUpdate(filter, entity); return result; } + /// + /// The FindOneAndUpdateAsync + /// + /// The filter + /// The entity + /// The public async Task FindOneAndUpdateAsync(FilterDefinition filter, UpdateDefinition entity) { var result = await _collection.FindOneAndUpdateAsync(filter, entity); return result; } - public DeleteResult Delete(FilterDefinition filter) + /// + /// The GetById + /// + /// The id + /// The + public T GetById(string id) { - return _collection.DeleteOne(filter); + var builder = Builders.Filter; + var filter = builder.Eq("_id", ObjectId.Parse(id)); + return _collection.Find(filter).FirstOrDefault(); } - public async Task DeleteAsync(FilterDefinition filter) + /// + /// The GetList + /// + /// The criteria + /// The + public List GetList(Expression> criteria) { - var result = await _collection.DeleteOneAsync(filter); + var result = _collection.Find(criteria).ToList(); return result; } - public DeleteResult DeleteMany(FilterDefinition filter) + /// + /// The GetListAsync + /// + /// The criteria + /// The + public async Task> GetListAsync(Expression> criteria) { - var result = _collection.DeleteMany(filter); + var result = await _collection.FindSync(criteria).ToListAsync(); return result; } - public async Task DeleteManyAsync(FilterDefinition filter) + /// + /// The GetPageAsc + /// + /// The criteria + /// The ascSort + /// The pParams + /// The + public List GetPageAsc(Expression> criteria, Expression> ascSort, QueryParams pParams) { - var result = await _collection.DeleteManyAsync(filter); + var sort = new SortDefinitionBuilder(); + var result = _collection.Find(criteria).Sort(sort.Ascending(ascSort)).Skip((pParams.Index - 1) * pParams.Size).Limit( + pParams.Size).ToList(); return result; } - public T FindOneAndDelete(Expression> criteria) + /// + /// The GetPageDesc + /// + /// The criteria + /// The descSort + /// The pParams + /// The + public List GetPageDesc(Expression> criteria, Expression> descSort, QueryParams pParams) { - var result = _collection.FindOneAndDelete(criteria); + var sort = new SortDefinitionBuilder(); + var result = _collection.Find(criteria).Sort(sort.Descending(descSort)).Skip((pParams.Index - 1) * pParams.Size).Limit( + pParams.Size).ToList(); return result; } - public async Task FindOneAndDeleteAsync(Expression> criteria) + /// + /// The GetSingle + /// + /// The criteria + /// The + public T GetSingle(Expression> criteria) { - var result = await _collection.FindOneAndDeleteAsync(criteria); + return _collection.Find(criteria).FirstOrDefault(); + } + + /// + /// The GetSingleAsync + /// + /// The criteria + /// The + public async Task GetSingleAsync(Expression> criteria) + { + var result = await _collection.FindSync(criteria).FirstOrDefaultAsync(); return result; } + /// + /// The Update + /// + /// The filter + /// The entity + /// The + public UpdateResult Update(FilterDefinition filter, UpdateDefinition entity) + { + return _collection.UpdateOne(filter, entity, new UpdateOptions() + { + IsUpsert = true, + BypassDocumentValidation = true, + }); + } - public long Count(FilterDefinition filter) + /// + /// The UpdateAsync + /// + /// The filter + /// The entity + /// The + public async Task UpdateAsync(FilterDefinition filter, UpdateDefinition entity) { - var result = _collection.Count(filter); + var result = await _collection.UpdateOneAsync(filter, entity); return result; } - public async Task CountAsync(FilterDefinition filter) + /// + /// The UpdateMany + /// + /// The filter + /// The entity + /// The + public UpdateResult UpdateMany(FilterDefinition filter, UpdateDefinition entity) { - var result = await _collection.CountAsync(filter); - return result; + return _collection.UpdateMany(filter, entity); } - public bool Exists(Expression> criteria, bool exists) + /// + /// The UpdateManyAsync + /// + /// The filter + /// The entity + /// The + public async Task UpdateManyAsync(FilterDefinition filter, UpdateDefinition entity) { - var builder = Builders.Filter; - var filter = builder.Exists(criteria, exists); - return _collection.Find(filter).Any(); + var result = await _collection.UpdateManyAsync(filter, entity); + return result; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/MongoProvider/Util.cs b/src/Surging.Core/Surging.Core.System/MongoProvider/Util.cs index 3f02d9027..dfbeffb6e 100644 --- a/src/Surging.Core/Surging.Core.System/MongoProvider/Util.cs +++ b/src/Surging.Core/Surging.Core.System/MongoProvider/Util.cs @@ -7,28 +7,69 @@ namespace Surging.Core.System.MongoProvider { + /// + /// Defines the + /// internal static class Util { + #region 字段 + + /// + /// Defines the _config + /// private static IConfigurationRoot _config; - private static IMongoDatabase GetDatabase(string connectString) - { - var mongoUrl = new MongoUrl(connectString); - var client = new MongoClient(connectString); - return client.GetDatabase(mongoUrl.DatabaseName); + #endregion 字段 + + #region 方法 + + /// + /// The GetCollectionFromConnectionString + /// + /// + /// The connectionstring + /// The + public static IMongoCollection GetCollectionFromConnectionString(string connectionstring) + where T : IEntity + { + return GetDatabase(GetDefaultConnectionString()).GetCollection(GetCollectionName()); } + /// + /// The GetDefaultConnectionString + /// + /// The public static string GetDefaultConnectionString() { return MongoConfig.DefaultInstance.MongConnectionString; } - public static IMongoCollection GetCollectionFromConnectionString(string connectionstring) - where T : IEntity + /// + /// The GetCollectioNameFromInterface + /// + /// + /// The + private static string GetCollectioNameFromInterface() { - return GetDatabase(GetDefaultConnectionString()).GetCollection(GetCollectionName()); + string collectionname; + Attribute att = Attribute.GetCustomAttribute(typeof(T), typeof(CollectionNameAttribute)); + if (att != null) + { + collectionname = ((CollectionNameAttribute)att).Name; + } + else + { + collectionname = typeof(T).Name; + } + + return collectionname; } + /// + /// The GetCollectionName + /// + /// + /// The private static string GetCollectionName() where T : IEntity { string collectionName; @@ -48,22 +89,11 @@ private static string GetCollectionName() where T : IEntity return collectionName; } - private static string GetCollectioNameFromInterface() - { - string collectionname; - Attribute att = Attribute.GetCustomAttribute(typeof(T), typeof(CollectionNameAttribute)); - if (att != null) - { - collectionname = ((CollectionNameAttribute)att).Name; - } - else - { - collectionname = typeof(T).Name; - } - - return collectionname; - } - + /// + /// The GetCollectionNameFromType + /// + /// The entitytype + /// The private static string GetCollectionNameFromType(Type entitytype) { string collectionname; @@ -82,6 +112,19 @@ private static string GetCollectionNameFromType(Type entitytype) } return collectionname; } - } -} + /// + /// The GetDatabase + /// + /// The connectString + /// The + private static IMongoDatabase GetDatabase(string connectString) + { + var mongoUrl = new MongoUrl(connectString); + var client = new MongoClient(connectString); + return client.GetDatabase(mongoUrl.DatabaseName); + } + + #endregion 方法 + } +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.System/Properties/AssemblyInfo.cs b/src/Surging.Core/Surging.Core.System/Properties/AssemblyInfo.cs index 2636d6801..34884b289 100644 --- a/src/Surging.Core/Surging.Core.System/Properties/AssemblyInfo.cs +++ b/src/Surging.Core/Surging.Core.System/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ - -using Surging.Core.System.Module; +using Surging.Core.System.Module; using Surging.Core.System.Module.Attributes; using System.Reflection; using System.Runtime.CompilerServices; diff --git a/src/Surging.Core/Surging.Core.System/ServiceBase.cs b/src/Surging.Core/Surging.Core.System/ServiceBase.cs index a4ebc0d81..581564a85 100644 --- a/src/Surging.Core/Surging.Core.System/ServiceBase.cs +++ b/src/Surging.Core/Surging.Core.System/ServiceBase.cs @@ -8,45 +8,97 @@ namespace Surging.Core.System { - public abstract class ServiceBase + /// + /// Defines the + /// + public abstract class ServiceBase { - public T CreateProxy(string key) where T : class + #region 方法 + + /// + /// The CreateProxy + /// + /// + /// The + public T CreateProxy() where T : class { - return ServiceLocator.GetService().CreateProxy(key); + return ServiceLocator.GetService().CreateProxy(); } - public object CreateProxy(Type type) + /// + /// The CreateProxy + /// + /// + /// The key + /// The + public T CreateProxy(string key) where T : class { - return ServiceLocator.GetService().CreateProxy(type); + return ServiceLocator.GetService().CreateProxy(key); } + /// + /// The CreateProxy + /// + /// The key + /// The type + /// The public object CreateProxy(string key, Type type) { return ServiceLocator.GetService().CreateProxy(key, type); } - public T CreateProxy() where T : class + + /// + /// The CreateProxy + /// + /// The type + /// The + public object CreateProxy(Type type) { - return ServiceLocator.GetService().CreateProxy(); + return ServiceLocator.GetService().CreateProxy(type); } + /// + /// The GetService + /// + /// + /// The public T GetService() { return ServiceLocator.GetService(); } + /// + /// The GetService + /// + /// + /// The key + /// The public T GetService(string key) { return ServiceLocator.GetService(key); } - public object GetService(Type type) + /// + /// The GetService + /// + /// The key + /// The type + /// The + public object GetService(string key, Type type) { - return ServiceLocator.GetService(type); + return ServiceLocator.GetService(key, type); } - public object GetService(string key, Type type) + /// + /// The GetService + /// + /// The type + /// The + public object GetService(Type type) { - return ServiceLocator.GetService(key, type); + return ServiceLocator.GetService(type); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/AppConfig.cs b/src/Surging.Core/Surging.Core.Zookeeper/AppConfig.cs index 7b1a0e30d..359184d04 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/AppConfig.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/AppConfig.cs @@ -1,10 +1,19 @@ using Microsoft.Extensions.Configuration; - namespace Surging.Core.Zookeeper { - public class AppConfig + /// + /// Defines the + /// + public class AppConfig { + #region 属性 + + /// + /// Gets or sets the Configuration + /// public static IConfigurationRoot Configuration { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/Configurations/ConfigInfo.cs b/src/Surging.Core/Surging.Core.Zookeeper/Configurations/ConfigInfo.cs index 16b6de5bb..ebece75bf 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/Configurations/ConfigInfo.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/Configurations/ConfigInfo.cs @@ -6,10 +6,15 @@ namespace Surging.Core.Zookeeper.Configurations { + /// + /// Defines the + /// public class ConfigInfo { + #region 构造函数 + /// - /// 初始化会话超时为20秒的Zookeeper配置信息。 + /// Initializes a new instance of the class. /// /// 连接字符串。 /// 路由配置路径。 @@ -18,6 +23,8 @@ public class ConfigInfo /// 缓存中心配置路径 /// mqtt路由配置路径 /// 根节点。 + /// The reloadOnChange + /// The enableChildrenMonitor public ConfigInfo(string connectionString, string routePath = "/services/serviceRoutes", string subscriberPath = "/services/serviceSubscribers", string commandPath = "/services/serviceCommands", @@ -37,16 +44,18 @@ public ConfigInfo(string connectionString, string routePath = "/services/service } /// - /// 初始化Zookeeper配置信息。 + /// Initializes a new instance of the class. /// /// 连接字符串。 + /// 会话超时时间。 /// 路由配置路径。 - /// 服务命令配置路径 /// 订阅者配置路径 - /// 会话超时时间。 + /// 服务命令配置路径 /// 缓存中心配置路径 /// mqtt路由配置路径 /// 根节点。 + /// The reloadOnChange + /// The enableChildrenMonitor public ConfigInfo(string connectionString, TimeSpan sessionTimeout, string routePath = "/services/serviceRoutes", string subscriberPath = "/services/serviceSubscribers", string commandPath = "/services/serviceCommands", @@ -78,60 +87,88 @@ public ConfigInfo(string connectionString, TimeSpan sessionTimeout, string route if (address != null) { var ipAddress = address as IpAddressModel; - Addresses = new IpAddressModel[] { ipAddress}; + Addresses = new IpAddressModel[] { ipAddress }; } } } } - public bool EnableChildrenMonitor { get; set; } + #endregion 构造函数 - public bool ReloadOnChange { get; set; } + #region 属性 /// - /// 连接字符串。 + /// Gets or sets the Addresses /// - public string ConnectionString { get; set; } + public IEnumerable Addresses { get; set; } + + /// + /// Gets or sets the CachePath + /// 缓存中心配置中心 + /// + public string CachePath { get; set; } + + /// + /// Gets or sets the ChRoot + /// 根节点。 + /// + public string ChRoot { get; set; } /// + /// Gets or sets the CommandPath /// 命令配置路径 /// public string CommandPath { get; set; } /// - /// 路由配置路径。 + /// Gets or sets the ConnectionString + /// 连接字符串。 /// - public string RoutePath { get; set; } + public string ConnectionString { get; set; } /// - /// 订阅者配置路径 + /// Gets or sets a value indicating whether EnableChildrenMonitor /// - public string SubscriberPath { get; set; } + public bool EnableChildrenMonitor { get; set; } /// - /// 会话超时时间。 + /// Gets or sets the MqttRoutePath + /// Mqtt路由配置路径。 /// - public TimeSpan SessionTimeout { get; set; } + public string MqttRoutePath { get; set; } /// - /// 根节点。 + /// Gets or sets a value indicating whether ReloadOnChange /// - public string ChRoot { get; set; } + public bool ReloadOnChange { get; set; } + /// + /// Gets or sets the RoutePath + /// 路由配置路径。 + /// + public string RoutePath { get; set; } - public IEnumerable Addresses { get; set; } + /// + /// Gets or sets the SessionTimeout + /// 会话超时时间。 + /// + public TimeSpan SessionTimeout { get; set; } /// - /// 缓存中心配置中心 + /// Gets or sets the SubscriberPath + /// 订阅者配置路径 /// - public string CachePath { get; set; } + public string SubscriberPath { get; set; } + + #endregion 属性 + #region 方法 /// - /// Mqtt路由配置路径。 + /// The ConvertAddressModel /// - public string MqttRoutePath { get; set; } - + /// The connection + /// The public AddressModel ConvertAddressModel(string connection) { var address = connection.Split(":"); @@ -143,5 +180,7 @@ public AddressModel ConvertAddressModel(string connection) } return null; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/Configurations/ZookeeperConfigurationExtensions.cs b/src/Surging.Core/Surging.Core.Zookeeper/Configurations/ZookeeperConfigurationExtensions.cs index 44a76c5c1..6ed4bca6e 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/Configurations/ZookeeperConfigurationExtensions.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/Configurations/ZookeeperConfigurationExtensions.cs @@ -8,23 +8,22 @@ namespace Surging.Core.Zookeeper.Configurations { + /// + /// Defines the + /// public static class ZookeeperConfigurationExtensions { - public static IConfigurationBuilder AddZookeeperFile(this IConfigurationBuilder builder, string path) - { - return AddZookeeperFile(builder, provider: null, path: path, optional: false, reloadOnChange: false); - } - - public static IConfigurationBuilder AddZookeeperFile(this IConfigurationBuilder builder, string path, bool optional) - { - return AddZookeeperFile(builder, provider: null, path: path, optional: optional, reloadOnChange: false); - } - - public static IConfigurationBuilder AddZookeeperFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange) - { - return AddZookeeperFile(builder, provider: null, path: path, optional: optional, reloadOnChange: reloadOnChange); - } + #region 方法 + /// + /// The AddZookeeperFile + /// + /// The builder + /// The provider + /// The path + /// The optional + /// The reloadOnChange + /// The public static IConfigurationBuilder AddZookeeperFile(this IConfigurationBuilder builder, IFileProvider provider, string path, bool optional, bool reloadOnChange) { Check.NotNull(builder, "builder"); @@ -45,5 +44,43 @@ public static IConfigurationBuilder AddZookeeperFile(this IConfigurationBuilder AppConfig.Configuration = builder.Build(); return builder; } + + /// + /// The AddZookeeperFile + /// + /// The builder + /// The path + /// The + public static IConfigurationBuilder AddZookeeperFile(this IConfigurationBuilder builder, string path) + { + return AddZookeeperFile(builder, provider: null, path: path, optional: false, reloadOnChange: false); + } + + /// + /// The AddZookeeperFile + /// + /// The builder + /// The path + /// The optional + /// The + public static IConfigurationBuilder AddZookeeperFile(this IConfigurationBuilder builder, string path, bool optional) + { + return AddZookeeperFile(builder, provider: null, path: path, optional: optional, reloadOnChange: false); + } + + /// + /// The AddZookeeperFile + /// + /// The builder + /// The path + /// The optional + /// The reloadOnChange + /// The + public static IConfigurationBuilder AddZookeeperFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange) + { + return AddZookeeperFile(builder, provider: null, path: path, optional: optional, reloadOnChange: reloadOnChange); + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/Configurations/ZookeeperConfigurationProvider.cs b/src/Surging.Core/Surging.Core.Zookeeper/Configurations/ZookeeperConfigurationProvider.cs index 2750c5ac4..7ac125475 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/Configurations/ZookeeperConfigurationProvider.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/Configurations/ZookeeperConfigurationProvider.cs @@ -4,14 +4,35 @@ namespace Surging.Core.Zookeeper.Configurations { + /// + /// Defines the + /// public class ZookeeperConfigurationProvider : FileConfigurationProvider { - public ZookeeperConfigurationProvider(ZookeeperConfigurationSource source) : base(source) { } + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The source + public ZookeeperConfigurationProvider(ZookeeperConfigurationSource source) : base(source) + { + } + + #endregion 构造函数 + + #region 方法 + + /// + /// The Load + /// + /// The stream public override void Load(Stream stream) { var parser = new JsonConfigurationParser(); this.Data = parser.Parse(stream, null); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/Configurations/ZookeeperConfigurationSource.cs b/src/Surging.Core/Surging.Core.Zookeeper/Configurations/ZookeeperConfigurationSource.cs index e3050dac4..a22aa345b 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/Configurations/ZookeeperConfigurationSource.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/Configurations/ZookeeperConfigurationSource.cs @@ -1,16 +1,34 @@ using Microsoft.Extensions.Configuration; - namespace Surging.Core.Zookeeper.Configurations { + /// + /// Defines the + /// public class ZookeeperConfigurationSource : FileConfigurationSource { + #region 属性 + + /// + /// Gets or sets the ConfigurationKeyPrefix + /// public string ConfigurationKeyPrefix { get; set; } + #endregion 属性 + + #region 方法 + + /// + /// The Build + /// + /// The builder + /// The public override IConfigurationProvider Build(IConfigurationBuilder builder) { FileProvider = FileProvider ?? builder.GetFileProvider(); return new ZookeeperConfigurationProvider(this); } + + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/Configurations/ZookeeperOption.cs b/src/Surging.Core/Surging.Core.Zookeeper/Configurations/ZookeeperOption.cs index a973cf1f2..c056e2047 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/Configurations/ZookeeperOption.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/Configurations/ZookeeperOption.cs @@ -4,27 +4,63 @@ namespace Surging.Core.Zookeeper.Configurations { + /// + /// Defines the + /// public class ZookeeperOption { + #region 属性 - public string SessionTimeout { get; set; } - - public string ConnectionString { get; set; } - - public string RoutePath { get; set; } + /// + /// Gets or sets the CachePath + /// + public string CachePath { get; set; } - public string SubscriberPath { get; set; } + /// + /// Gets or sets the ChRoot + /// + public string ChRoot { get; set; } + /// + /// Gets or sets the CommandPath + /// public string CommandPath { get; set; } - public string ChRoot { get; set; } + /// + /// Gets or sets the ConnectionString + /// + public string ConnectionString { get; set; } - public string CachePath { get; set; } + /// + /// Gets or sets the EnableChildrenMonitor + /// + public string EnableChildrenMonitor { get; set; } + /// + /// Gets or sets the MqttRoutePath + /// public string MqttRoutePath { get; set; } + /// + /// Gets or sets the ReloadOnChange + /// public string ReloadOnChange { get; set; } - public string EnableChildrenMonitor { get; set; } + /// + /// Gets or sets the RoutePath + /// + public string RoutePath { get; set; } + + /// + /// Gets or sets the SessionTimeout + /// + public string SessionTimeout { get; set; } + + /// + /// Gets or sets the SubscriberPath + /// + public string SubscriberPath { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/ContainerBuilderExtensions.cs b/src/Surging.Core/Surging.Core.Zookeeper/ContainerBuilderExtensions.cs index 08d2cbab3..20740425a 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/ContainerBuilderExtensions.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/ContainerBuilderExtensions.cs @@ -20,39 +20,68 @@ namespace Surging.Core.Zookeeper { + /// + /// Defines the + /// public static class ContainerBuilderExtensions { + #region 方法 + /// - /// 设置共享文件路由管理者。 + /// The UseHealthCheck /// - /// Rpc服务构建者。 - /// ZooKeeper设置信息。 - /// 服务构建者。 - public static IServiceBuilder UseZooKeeperRouteManager(this IServiceBuilder builder, ConfigInfo configInfo) + /// The builder + /// The + public static IServiceBuilder UseHealthCheck(this IServiceBuilder builder) { - return builder.UseRouteManager(provider => - new ZooKeeperServiceRouteManager( - GetConfigInfo(configInfo), + builder.Services.RegisterType().As().SingleInstance(); + return builder; + } + + /// + /// The UseZookeeperAddressSelector + /// + /// The builder + /// The + public static IServiceBuilder UseZookeeperAddressSelector(this IServiceBuilder builder) + { + builder.Services.RegisterType().As().SingleInstance(); + return builder; + } + + /// + /// The UseZooKeeperCacheManager + /// + /// The builder + /// The configInfo + /// The + public static IServiceBuilder UseZooKeeperCacheManager(this IServiceBuilder builder, ConfigInfo configInfo) + { + return builder.UseCacheManager(provider => + new ZookeeperServiceCacheManager( + GetConfigInfo(configInfo), provider.GetRequiredService>(), provider.GetRequiredService>(), - provider.GetRequiredService(), - provider.GetRequiredService>(), + provider.GetRequiredService(), + provider.GetRequiredService>(), provider.GetRequiredService())); } - public static IServiceBuilder UseZooKeeperMqttRouteManager(this IServiceBuilder builder, ConfigInfo configInfo) + /// + /// The UseZookeeperClientProvider + /// + /// The builder + /// The configInfo + /// The + public static IServiceBuilder UseZookeeperClientProvider(this IServiceBuilder builder, ConfigInfo configInfo) { - return builder.UseMqttRouteManager(provider => - { - var result = new ZooKeeperMqttServiceRouteManager( - GetConfigInfo(configInfo), - provider.GetRequiredService>(), - provider.GetRequiredService>(), - provider.GetRequiredService(), - provider.GetRequiredService>(), - provider.GetRequiredService()); - return result; - }); + builder.Services.Register(provider => + new DefaultZookeeperClientProvider( + GetConfigInfo(configInfo), + provider.Resolve(), + provider.Resolve(), + provider.Resolve>())).As().SingleInstance(); + return builder; } /// @@ -77,36 +106,14 @@ public static IServiceBuilder UseZooKeeperCommandManager(this IServiceBuilder bu }); } - public static IServiceBuilder UseZooKeeperServiceSubscribeManager(this IServiceBuilder builder, ConfigInfo configInfo) - { - return builder.UseSubscribeManager(provider => - { - var result = new ZooKeeperServiceSubscribeManager( - GetConfigInfo(configInfo), - provider.GetRequiredService>(), - provider.GetRequiredService>(), - provider.GetRequiredService(), - provider.GetRequiredService>(), - provider.GetRequiredService()); - return result; - }); - } - - public static IServiceBuilder UseZooKeeperCacheManager(this IServiceBuilder builder, ConfigInfo configInfo) - { - return builder.UseCacheManager(provider => - new ZookeeperServiceCacheManager( - GetConfigInfo(configInfo), - provider.GetRequiredService>(), - provider.GetRequiredService>(), - provider.GetRequiredService(), - provider.GetRequiredService>(), - provider.GetRequiredService())); - } - - - public static IServiceBuilder UseZooKeeperManager(this IServiceBuilder builder, ConfigInfo configInfo) + /// + /// The UseZooKeeperManager + /// + /// The builder + /// The + public static IServiceBuilder UseZooKeeperManager(this IServiceBuilder builder) { + var configInfo = new ConfigInfo(null); return builder.UseZooKeeperRouteManager(configInfo) .UseHealthCheck() .UseZookeeperAddressSelector() @@ -117,9 +124,14 @@ public static IServiceBuilder UseZooKeeperManager(this IServiceBuilder builder, .UseZooKeeperMqttRouteManager(configInfo); } - public static IServiceBuilder UseZooKeeperManager(this IServiceBuilder builder) + /// + /// The UseZooKeeperManager + /// + /// The builder + /// The configInfo + /// The + public static IServiceBuilder UseZooKeeperManager(this IServiceBuilder builder, ConfigInfo configInfo) { - var configInfo = new ConfigInfo(null); return builder.UseZooKeeperRouteManager(configInfo) .UseHealthCheck() .UseZookeeperAddressSelector() @@ -130,31 +142,71 @@ public static IServiceBuilder UseZooKeeperManager(this IServiceBuilder builder) .UseZooKeeperMqttRouteManager(configInfo); } - public static IServiceBuilder UseZookeeperAddressSelector(this IServiceBuilder builder) + /// + /// The UseZooKeeperMqttRouteManager + /// + /// The builder + /// The configInfo + /// The + public static IServiceBuilder UseZooKeeperMqttRouteManager(this IServiceBuilder builder, ConfigInfo configInfo) { - builder.Services.RegisterType().As().SingleInstance(); - return builder; + return builder.UseMqttRouteManager(provider => + { + var result = new ZooKeeperMqttServiceRouteManager( + GetConfigInfo(configInfo), + provider.GetRequiredService>(), + provider.GetRequiredService>(), + provider.GetRequiredService(), + provider.GetRequiredService>(), + provider.GetRequiredService()); + return result; + }); } - public static IServiceBuilder UseHealthCheck(this IServiceBuilder builder) + /// + /// 设置共享文件路由管理者。 + /// + /// Rpc服务构建者。 + /// ZooKeeper设置信息。 + /// 服务构建者。 + public static IServiceBuilder UseZooKeeperRouteManager(this IServiceBuilder builder, ConfigInfo configInfo) { - builder.Services.RegisterType().As().SingleInstance(); - return builder; + return builder.UseRouteManager(provider => + new ZooKeeperServiceRouteManager( + GetConfigInfo(configInfo), + provider.GetRequiredService>(), + provider.GetRequiredService>(), + provider.GetRequiredService(), + provider.GetRequiredService>(), + provider.GetRequiredService())); } - - public static IServiceBuilder UseZookeeperClientProvider(this IServiceBuilder builder, ConfigInfo configInfo) + /// + /// The UseZooKeeperServiceSubscribeManager + /// + /// The builder + /// The configInfo + /// The + public static IServiceBuilder UseZooKeeperServiceSubscribeManager(this IServiceBuilder builder, ConfigInfo configInfo) { - builder.Services.Register(provider => - new DefaultZookeeperClientProvider( - GetConfigInfo(configInfo), - provider.Resolve(), - provider.Resolve(), - provider.Resolve>())).As().SingleInstance(); - return builder; + return builder.UseSubscribeManager(provider => + { + var result = new ZooKeeperServiceSubscribeManager( + GetConfigInfo(configInfo), + provider.GetRequiredService>(), + provider.GetRequiredService>(), + provider.GetRequiredService(), + provider.GetRequiredService>(), + provider.GetRequiredService()); + return result; + }); } - + /// + /// The GetConfigInfo + /// + /// The config + /// The private static ConfigInfo GetConfigInfo(ConfigInfo config) { ZookeeperOption option = null; @@ -184,5 +236,7 @@ private static ConfigInfo GetConfigInfo(ConfigInfo config) } return config; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/Internal/Cluster/HealthChecks/IHealthCheckService.cs b/src/Surging.Core/Surging.Core.Zookeeper/Internal/Cluster/HealthChecks/IHealthCheckService.cs index 6f5228314..11393cf79 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/Internal/Cluster/HealthChecks/IHealthCheckService.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/Internal/Cluster/HealthChecks/IHealthCheckService.cs @@ -6,10 +6,30 @@ namespace Surging.Core.Zookeeper.Internal.Cluster.HealthChecks { - public interface IHealthCheckService + #region 接口 + + /// + /// Defines the + /// + public interface IHealthCheckService { - void Monitor(AddressModel address); + #region 方法 + /// + /// The IsHealth + /// + /// The address + /// The ValueTask IsHealth(AddressModel address); + + /// + /// The Monitor + /// + /// The address + void Monitor(AddressModel address); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/Internal/Cluster/HealthChecks/Implementation/DefaultHealthCheckService.cs b/src/Surging.Core/Surging.Core.Zookeeper/Internal/Cluster/HealthChecks/Implementation/DefaultHealthCheckService.cs index c62e1b856..16e88bd8f 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/Internal/Cluster/HealthChecks/Implementation/DefaultHealthCheckService.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/Internal/Cluster/HealthChecks/Implementation/DefaultHealthCheckService.cs @@ -1,4 +1,5 @@ -using System; +using Surging.Core.CPlatform.Address; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -7,18 +8,39 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using Surging.Core.CPlatform.Address; namespace Surging.Core.Zookeeper.Internal.Cluster.HealthChecks.Implementation { + /// + /// Defines the + /// public class DefaultHealthCheckService : IHealthCheckService { - private readonly int _timeout = 30000; - private readonly Timer _timer; + #region 字段 + + /// + /// Defines the _dictionary + /// private readonly ConcurrentDictionary, MonitorEntry> _dictionary = new ConcurrentDictionary, MonitorEntry>(); - #region Implementation of IHealthCheckService + /// + /// Defines the _timeout + /// + private readonly int _timeout = 30000; + + /// + /// Defines the _timer + /// + private readonly Timer _timer; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public DefaultHealthCheckService() { var timeSpan = TimeSpan.FromSeconds(60); @@ -29,6 +51,23 @@ public DefaultHealthCheckService() }, null, timeSpan, timeSpan); } + #endregion 构造函数 + + #region 方法 + + /// + /// The Dispose + /// + public void Dispose() + { + _timer.Dispose(); + } + + /// + /// The IsHealth + /// + /// The address + /// The public async ValueTask IsHealth(AddressModel address) { var ipAddress = address as IpAddressModel; @@ -37,24 +76,22 @@ public async ValueTask IsHealth(AddressModel address) return isHealth; } + /// + /// The Monitor + /// + /// The address public void Monitor(AddressModel address) { var ipAddress = address as IpAddressModel; _dictionary.GetOrAdd(new ValueTuple(ipAddress.Ip, ipAddress.Port), k => new MonitorEntry(address)); } - #region Implementation of IDisposable - - public void Dispose() - { - _timer.Dispose(); - } - #endregion - - #endregion Implementation of IDisposable - - #region Private Method - + /// + /// The Check + /// + /// The address + /// The timeout + /// The private static async Task Check(AddressModel address, int timeout) { bool isHealth = false; @@ -67,12 +104,17 @@ private static async Task Check(AddressModel address, int timeout) } catch { - } return isHealth; } } + /// + /// The Check + /// + /// The entrys + /// The timeout + /// The private static async Task Check(IEnumerable entrys, int timeout) { foreach (var entry in entrys) @@ -94,26 +136,46 @@ private static async Task Check(IEnumerable entrys, int timeout) } } - #endregion Private Method - - #region Help Class + #endregion 方法 + /// + /// Defines the + /// protected class MonitorEntry { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The addressModel + /// The health public MonitorEntry(AddressModel addressModel, bool health = true) { EndPoint = addressModel.CreateEndPoint(); Health = health; - } - public int UnhealthyTimes { get; set; } + #endregion 构造函数 + + #region 属性 + /// + /// Gets or sets the EndPoint + /// public EndPoint EndPoint { get; set; } + + /// + /// Gets or sets a value indicating whether Health + /// public bool Health { get; set; } - } - #endregion Help Class - } -} + /// + /// Gets or sets the UnhealthyTimes + /// + public int UnhealthyTimes { get; set; } + #endregion 属性 + } + } +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/Internal/Cluster/Implementation/Selectors/IZookeeperAddressSelector.cs b/src/Surging.Core/Surging.Core.Zookeeper/Internal/Cluster/Implementation/Selectors/IZookeeperAddressSelector.cs index dc8210871..9f8577eb2 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/Internal/Cluster/Implementation/Selectors/IZookeeperAddressSelector.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/Internal/Cluster/Implementation/Selectors/IZookeeperAddressSelector.cs @@ -5,7 +5,14 @@ namespace Surging.Core.Zookeeper.Internal.Cluster.Implementation.Selectors { - public interface IZookeeperAddressSelector: IAddressSelector + #region 接口 + + /// + /// Defines the + /// + public interface IZookeeperAddressSelector : IAddressSelector { } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/Internal/Cluster/Implementation/Selectors/Implementation/AddressSelectorMode.cs b/src/Surging.Core/Surging.Core.Zookeeper/Internal/Cluster/Implementation/Selectors/Implementation/AddressSelectorMode.cs index efa777a59..f71d90253 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/Internal/Cluster/Implementation/Selectors/Implementation/AddressSelectorMode.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/Internal/Cluster/Implementation/Selectors/Implementation/AddressSelectorMode.cs @@ -4,9 +4,23 @@ namespace Surging.Core.Zookeeper.Internal.Cluster.Implementation.Selectors.Implementation { - public enum AddressSelectorMode + #region 枚举 + + /// + /// Defines the AddressSelectorMode + /// + public enum AddressSelectorMode { + /// + /// Defines the Polling + /// Polling, + + /// + /// Defines the Random + /// Random } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/Internal/Cluster/Implementation/Selectors/Implementation/ZookeeperAddressSelectorBase.cs b/src/Surging.Core/Surging.Core.Zookeeper/Internal/Cluster/Implementation/Selectors/Implementation/ZookeeperAddressSelectorBase.cs index 641ba0b9f..2b38291b2 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/Internal/Cluster/Implementation/Selectors/Implementation/ZookeeperAddressSelectorBase.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/Internal/Cluster/Implementation/Selectors/Implementation/ZookeeperAddressSelectorBase.cs @@ -8,9 +8,19 @@ namespace Surging.Core.Zookeeper.Internal.Cluster.Implementation.Selectors.Implementation { + /// + /// Defines the + /// public abstract class ZookeeperAddressSelectorBase : IZookeeperAddressSelector { - #region Implementation of IAddressSelector + #region 方法 + + /// + /// 选择一个地址。 + /// + /// 地址选择上下文。 + /// 地址模型。 + protected abstract ValueTask SelectAsync(AddressSelectContext context); /// /// 选择一个地址。 @@ -41,13 +51,6 @@ async ValueTask IAddressSelector.SelectAsync(AddressSelectContext } } - #endregion Implementation of IAddressSelector - - /// - /// 选择一个地址。 - /// - /// 地址选择上下文。 - /// 地址模型。 - protected abstract ValueTask SelectAsync(AddressSelectContext context); + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/Internal/Cluster/Implementation/Selectors/Implementation/ZookeeperRandomAddressSelector.cs b/src/Surging.Core/Surging.Core.Zookeeper/Internal/Cluster/Implementation/Selectors/Implementation/ZookeeperRandomAddressSelector.cs index 34ef1de71..79c119a48 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/Internal/Cluster/Implementation/Selectors/Implementation/ZookeeperRandomAddressSelector.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/Internal/Cluster/Implementation/Selectors/Implementation/ZookeeperRandomAddressSelector.cs @@ -8,19 +8,29 @@ namespace Surging.Core.Zookeeper.Internal.Cluster.Implementation.Selectors.Implementation { - public class ZookeeperRandomAddressSelector : ZookeeperAddressSelectorBase + /// + /// Defines the + /// + public class ZookeeperRandomAddressSelector : ZookeeperAddressSelectorBase { - #region Field + #region 字段 + /// + /// Defines the _generate + /// private readonly Func _generate; + + /// + /// Defines the _random + /// private readonly Random _random; - #endregion Field + #endregion 字段 - #region Constructor + #region 构造函数 /// - /// 初始化一个以Random生成随机数的随机地址选择器。 + /// Initializes a new instance of the class. /// public ZookeeperRandomAddressSelector() { @@ -29,7 +39,7 @@ public ZookeeperRandomAddressSelector() } /// - /// 初始化一个自定义的随机地址选择器。 + /// Initializes a new instance of the class. /// /// 随机数生成委托,第一个参数为最小值,第二个参数为最大值(不可以超过该值)。 public ZookeeperRandomAddressSelector(Func generate) @@ -39,9 +49,9 @@ public ZookeeperRandomAddressSelector(Func generate) _generate = generate; } - #endregion Constructor + #endregion 构造函数 - #region Overrides of AddressSelectorBase + #region 方法 /// /// 选择一个地址。 @@ -57,7 +67,6 @@ protected override ValueTask SelectAsync(AddressSelectContext cont return new ValueTask(address[index]); } - #endregion Overrides of AddressSelectorBase + #endregion 方法 } -} - +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/Internal/IZookeeperClientProvider.cs b/src/Surging.Core/Surging.Core.Zookeeper/Internal/IZookeeperClientProvider.cs index 61d76b6e1..bb9d2c98f 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/Internal/IZookeeperClientProvider.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/Internal/IZookeeperClientProvider.cs @@ -7,12 +7,35 @@ namespace Surging.Core.Zookeeper.Internal { - public interface IZookeeperClientProvider + #region 接口 + + /// + /// Defines the + /// + public interface IZookeeperClientProvider { + #region 方法 + + /// + /// The Check + /// + /// The + ValueTask Check(); + + /// + /// The GetZooKeeper + /// + /// The ValueTask<(ManualResetEvent, ZooKeeper)> GetZooKeeper(); + /// + /// The GetZooKeepers + /// + /// The ValueTask> GetZooKeepers(); - ValueTask Check(); + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/Internal/Implementation/DefaultZookeeperClientProvider.cs b/src/Surging.Core/Surging.Core.Zookeeper/Internal/Implementation/DefaultZookeeperClientProvider.cs index 6dc7c164e..a6e23342a 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/Internal/Implementation/DefaultZookeeperClientProvider.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/Internal/Implementation/DefaultZookeeperClientProvider.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using org.apache.zookeeper; using Surging.Core.CPlatform; using Surging.Core.CPlatform.Address; @@ -14,20 +8,66 @@ using Surging.Core.Zookeeper.Internal.Cluster.HealthChecks; using Surging.Core.Zookeeper.Internal.Cluster.Implementation.Selectors; using Surging.Core.Zookeeper.WatcherProvider; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; using Level = Microsoft.Extensions.Logging.LogLevel; namespace Surging.Core.Zookeeper.Internal.Implementation { + /// + /// Defines the + /// public class DefaultZookeeperClientProvider : IZookeeperClientProvider { - private ConfigInfo _config; - private readonly IHealthCheckService _healthCheckService; - private readonly IZookeeperAddressSelector _zookeeperAddressSelector; - private readonly ILogger _logger; + #region 字段 + + /// + /// Defines the _addressSelectors + /// private readonly ConcurrentDictionary _addressSelectors = new ConcurrentDictionary(); - private readonly ConcurrentDictionary> _zookeeperClients = new + + /// + /// Defines the _healthCheckService + /// + private readonly IHealthCheckService _healthCheckService; + + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the _zookeeperAddressSelector + /// + private readonly IZookeeperAddressSelector _zookeeperAddressSelector; + + /// + /// Defines the _zookeeperClients + /// + private readonly ConcurrentDictionary> _zookeeperClients = new ConcurrentDictionary>(); + + /// + /// Defines the _config + /// + private ConfigInfo _config; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The config + /// The healthCheckService + /// The zookeeperAddressSelector + /// The logger public DefaultZookeeperClientProvider(ConfigInfo config, IHealthCheckService healthCheckService, IZookeeperAddressSelector zookeeperAddressSelector, ILogger logger) { @@ -36,6 +76,15 @@ public DefaultZookeeperClientProvider(ConfigInfo config, IHealthCheckService hea _zookeeperAddressSelector = zookeeperAddressSelector; _logger = logger; } + + #endregion 构造函数 + + #region 方法 + + /// + /// The Check + /// + /// The public async ValueTask Check() { foreach (var address in _config.Addresses) @@ -47,9 +96,12 @@ public async ValueTask Check() } } + /// + /// The GetZooKeeper + /// + /// The public async ValueTask<(ManualResetEvent, ZooKeeper)> GetZooKeeper() { - (ManualResetEvent, ZooKeeper) result = new ValueTuple(); var address = new List(); foreach (var addressModel in _config.Addresses) @@ -83,6 +135,29 @@ public async ValueTask Check() return result; } + /// + /// The GetZooKeepers + /// + /// The + public async ValueTask> GetZooKeepers() + { + var result = new List<(ManualResetEvent, ZooKeeper)>(); + foreach (var address in _config.Addresses) + { + var ipAddress = address as IpAddressModel; + if (await _healthCheckService.IsHealth(address)) + { + result.Add(CreateZooKeeper(ipAddress)); + } + } + return result; + } + + /// + /// The CreateZooKeeper + /// + /// The ipAddress + /// The protected (ManualResetEvent, ZooKeeper) CreateZooKeeper(IpAddressModel ipAddress) { if (!_zookeeperClients.TryGetValue(ipAddress, out (ManualResetEvent, ZooKeeper) result)) @@ -102,30 +177,17 @@ public async ValueTask Check() { connectionWait.Reset(); if (_zookeeperClients.TryRemove(ipAddress, out (ManualResetEvent, ZooKeeper) value)) - { + { await value.Item2.closeAsync(); value.Item1.Close(); } CreateZooKeeper(ipAddress); }))); - _zookeeperClients.AddOrUpdate(ipAddress, result,(k,v)=> result); + _zookeeperClients.AddOrUpdate(ipAddress, result, (k, v) => result); } return result; } - public async ValueTask> GetZooKeepers() - { - var result = new List<(ManualResetEvent, ZooKeeper)>(); - foreach (var address in _config.Addresses) - { - var ipAddress = address as IpAddressModel; - if (await _healthCheckService.IsHealth(address)) - { - result.Add(CreateZooKeeper(ipAddress)); - - } - } - return result; - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/WatcherProvider/ChildrenMonitorWatcher.cs b/src/Surging.Core/Surging.Core.Zookeeper/WatcherProvider/ChildrenMonitorWatcher.cs index ce37691a6..9289bdc58 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/WatcherProvider/ChildrenMonitorWatcher.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/WatcherProvider/ChildrenMonitorWatcher.cs @@ -7,12 +7,38 @@ namespace Surging.Core.Zookeeper.WatcherProvider { + /// + /// Defines the + /// internal class ChildrenMonitorWatcher : WatcherBase { - private readonly Func> _zooKeeperCall; + #region 字段 + + /// + /// Defines the _action + /// private readonly Action _action; + + /// + /// Defines the _zooKeeperCall + /// + private readonly Func> _zooKeeperCall; + + /// + /// Defines the _currentData + /// private string[] _currentData = new string[0]; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The zooKeeperCall + /// The path + /// The action public ChildrenMonitorWatcher(Func> zooKeeperCall, string path, Action action) : base(path) { @@ -20,6 +46,15 @@ public ChildrenMonitorWatcher(Func> zoo _action = action; } + #endregion 构造函数 + + #region 方法 + + /// + /// The SetCurrentData + /// + /// The currentData + /// The public ChildrenMonitorWatcher SetCurrentData(string[] currentData) { _currentData = currentData ?? new string[0]; @@ -27,12 +62,15 @@ public ChildrenMonitorWatcher SetCurrentData(string[] currentData) return this; } - #region Overrides of WatcherBase - + /// + /// The ProcessImpl + /// + /// The watchedEvent + /// The protected override async Task ProcessImpl(WatchedEvent watchedEvent) { var path = Path; - var zooKeeper =await _zooKeeperCall(); + var zooKeeper = await _zooKeeperCall(); Func getWatcher = () => new ChildrenMonitorWatcher(_zooKeeperCall, path, _action); switch (watchedEvent.get_Type()) { @@ -68,6 +106,7 @@ protected override async Task ProcessImpl(WatchedEvent watchedEvent) break; } } - #endregion Overrides of WatcherBase + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/WatcherProvider/NodeMonitorWatcher.cs b/src/Surging.Core/Surging.Core.Zookeeper/WatcherProvider/NodeMonitorWatcher.cs index b2d6d9cb3..bfbc8fea1 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/WatcherProvider/NodeMonitorWatcher.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/WatcherProvider/NodeMonitorWatcher.cs @@ -7,18 +7,53 @@ namespace Surging.Core.Zookeeper.WatcherProvider { + /// + /// Defines the + /// internal class NodeMonitorWatcher : WatcherBase { - private readonly Func> _zooKeeperCall; + #region 字段 + + /// + /// Defines the _action + /// private readonly Action _action; + + /// + /// Defines the _zooKeeperCall + /// + private readonly Func> _zooKeeperCall; + + /// + /// Defines the _currentData + /// private byte[] _currentData; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The zooKeeperCall + /// The path + /// The action public NodeMonitorWatcher(Func> zooKeeperCall, string path, Action action) : base(path) { _zooKeeperCall = zooKeeperCall; _action = action; } + #endregion 构造函数 + + #region 方法 + + /// + /// The SetCurrentData + /// + /// The currentData + /// The public NodeMonitorWatcher SetCurrentData(byte[] currentData) { _currentData = currentData; @@ -26,8 +61,11 @@ public NodeMonitorWatcher SetCurrentData(byte[] currentData) return this; } - #region Overrides of WatcherBase - + /// + /// The ProcessImpl + /// + /// The watchedEvent + /// The protected override async Task ProcessImpl(WatchedEvent watchedEvent) { var path = Path; @@ -44,6 +82,6 @@ protected override async Task ProcessImpl(WatchedEvent watchedEvent) } } - #endregion Overrides of WatcherBase + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/WatcherProvider/ReconnectionWatcher.cs b/src/Surging.Core/Surging.Core.Zookeeper/WatcherProvider/ReconnectionWatcher.cs index b884476d8..90e5f65d8 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/WatcherProvider/ReconnectionWatcher.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/WatcherProvider/ReconnectionWatcher.cs @@ -6,12 +6,38 @@ namespace Surging.Core.Zookeeper.WatcherProvider { + /// + /// Defines the + /// internal class ReconnectionWatcher : Watcher { + #region 字段 + + /// + /// Defines the _connectioned + /// private readonly Action _connectioned; + + /// + /// Defines the _disconnect + /// private readonly Action _disconnect; + + /// + /// Defines the _reconnection + /// private readonly Action _reconnection; + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The connectioned + /// The disconnect + /// The reconnection public ReconnectionWatcher(Action connectioned, Action disconnect, Action reconnection) { _connectioned = connectioned; @@ -19,9 +45,13 @@ public ReconnectionWatcher(Action connectioned, Action disconnect, Action reconn _reconnection = reconnection; } - #region Overrides of Watcher + #endregion 构造函数 + + #region 方法 - /// Processes the specified event. + /// + /// The process + /// /// The event. /// public override async Task process(WatchedEvent watchedEvent) @@ -55,9 +85,8 @@ public override async Task process(WatchedEvent watchedEvent) await Task.FromResult(1); #else await Task.CompletedTask; -#endif } - #endregion Overrides of Watcher +#endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/WatcherProvider/WatcherBase.cs b/src/Surging.Core/Surging.Core.Zookeeper/WatcherProvider/WatcherBase.cs index 836e88291..52fd43769 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/WatcherProvider/WatcherBase.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/WatcherProvider/WatcherBase.cs @@ -6,15 +6,40 @@ namespace Surging.Core.Zookeeper.WatcherProvider { - public abstract class WatcherBase : Watcher + /// + /// Defines the + /// + public abstract class WatcherBase : Watcher { - protected string Path { get; } + #region 构造函数 + /// + /// Initializes a new instance of the class. + /// + /// The path protected WatcherBase(string path) { Path = path; } + #endregion 构造函数 + + #region 属性 + + /// + /// Gets the Path + /// + protected string Path { get; } + + #endregion 属性 + + #region 方法 + + /// + /// The process + /// + /// The watchedEvent + /// The public override async Task process(WatchedEvent watchedEvent) { if (watchedEvent.getState() != Event.KeeperState.SyncConnected || watchedEvent.getPath() != Path) @@ -22,6 +47,13 @@ public override async Task process(WatchedEvent watchedEvent) await ProcessImpl(watchedEvent); } + /// + /// The ProcessImpl + /// + /// The watchedEvent + /// The protected abstract Task ProcessImpl(WatchedEvent watchedEvent); + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/ZooKeeperMqttServiceRouteManager.cs b/src/Surging.Core/Surging.Core.Zookeeper/ZooKeeperMqttServiceRouteManager.cs index 369d359f7..fcc19af33 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/ZooKeeperMqttServiceRouteManager.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/ZooKeeperMqttServiceRouteManager.cs @@ -18,15 +18,56 @@ namespace Surging.Core.Zookeeper { - public class ZooKeeperMqttServiceRouteManager : MqttServiceRouteManagerBase, IDisposable - { + /// + /// Defines the + /// + public class ZooKeeperMqttServiceRouteManager : MqttServiceRouteManagerBase, IDisposable + { + #region 字段 + + /// + /// Defines the _configInfo + /// private readonly ConfigInfo _configInfo; - private readonly ISerializer _serializer; - private readonly IMqttServiceFactory _mqttServiceFactory; + + /// + /// Defines the _logger + /// private readonly ILogger _logger; - private MqttServiceRoute[] _routes; + + /// + /// Defines the _mqttServiceFactory + /// + private readonly IMqttServiceFactory _mqttServiceFactory; + + /// + /// Defines the _serializer + /// + private readonly ISerializer _serializer; + + /// + /// Defines the _zookeeperClientProvider + /// private readonly IZookeeperClientProvider _zookeeperClientProvider; + /// + /// Defines the _routes + /// + private MqttServiceRoute[] _routes; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The configInfo + /// The serializer + /// The stringSerializer + /// The mqttServiceFactory + /// The logger + /// The zookeeperClientProvider public ZooKeeperMqttServiceRouteManager(ConfigInfo configInfo, ISerializer serializer, ISerializer stringSerializer, IMqttServiceFactory mqttServiceFactory, ILogger logger, IZookeeperClientProvider zookeeperClientProvider) : base(stringSerializer) @@ -39,15 +80,57 @@ public ZooKeeperMqttServiceRouteManager(ConfigInfo configInfo, ISerializer - /// 获取所有可用的mqtt服务路由信息。 + /// The ChildrenChange /// - /// 服务路由集合。 - public override async Task> GetRoutesAsync() + /// The oldChildrens + /// The newChildrens + /// The + public async Task ChildrenChange(string[] oldChildrens, string[] newChildrens) { - await EnterRoutes(); - return _routes; + if (_logger.IsEnabled(LogLevel.Debug)) + _logger.LogDebug($"最新的mqtt节点信息:{string.Join(",", newChildrens)}"); + + if (_logger.IsEnabled(LogLevel.Debug)) + _logger.LogDebug($"旧的mqtt节点信息:{string.Join(",", oldChildrens)}"); + + //计算出已被删除的节点。 + var deletedChildrens = oldChildrens.Except(newChildrens).ToArray(); + //计算出新增的节点。 + var createdChildrens = newChildrens.Except(oldChildrens).ToArray(); + + if (_logger.IsEnabled(LogLevel.Debug)) + _logger.LogDebug($"需要被删除的mqtt路由节点:{string.Join(",", deletedChildrens)}"); + if (_logger.IsEnabled(LogLevel.Debug)) + _logger.LogDebug($"需要被添加的mqtt路由节点:{string.Join(",", createdChildrens)}"); + + //获取新增的mqtt路由信息。 + var newRoutes = (await GetRoutes(createdChildrens)).ToArray(); + + var routes = _routes.ToArray(); + lock (_routes) + { + _routes = _routes + //删除无效的节点路由。 + .Where(i => !deletedChildrens.Contains(i.MqttDescriptor.Topic)) + //连接上新的mqtt路由。 + .Concat(newRoutes) + .ToArray(); + } + //需要删除的Topic路由集合。 + var deletedRoutes = routes.Where(i => deletedChildrens.Contains(i.MqttDescriptor.Topic)).ToArray(); + //触发删除事件。 + OnRemoved(deletedRoutes.Select(route => new MqttServiceRouteEventArgs(route)).ToArray()); + + //触发路由被创建事件。 + OnCreated(newRoutes.Select(route => new MqttServiceRouteEventArgs(route)).ToArray()); + + if (_logger.IsEnabled(LogLevel.Information)) + _logger.LogInformation("mqtt路由数据更新成功。"); } /// @@ -95,61 +178,56 @@ public override async Task ClearAsync() } /// - /// 设置mqtt服务路由。 + /// The Dispose /// - /// 服务路由集合。 - /// 一个任务。 - protected override async Task SetRoutesAsync(IEnumerable routes) + public void Dispose() { - if (_logger.IsEnabled(LogLevel.Information)) - _logger.LogInformation("准备添加mqtt服务路由。"); - var zooKeepers = await _zookeeperClientProvider.GetZooKeepers(); - foreach (var zooKeeper in zooKeepers) - { - await CreateSubdirectory(zooKeeper, _configInfo.MqttRoutePath); - - var path = _configInfo.MqttRoutePath; - if (!path.EndsWith("/")) - path += "/"; - - routes = routes.ToArray(); - - foreach (var serviceRoute in routes) - { - var nodePath = $"{path}{serviceRoute.MqttDescriptor.Topic}"; - var nodeData = _serializer.Serialize(serviceRoute); - if (await zooKeeper.Item2.existsAsync(nodePath) == null) - { - if (_logger.IsEnabled(LogLevel.Debug)) - _logger.LogDebug($"节点:{nodePath}不存在将进行创建。"); - - await zooKeeper.Item2.createAsync(nodePath, nodeData, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); - } - else - { - if (_logger.IsEnabled(LogLevel.Debug)) - _logger.LogDebug($"将更新节点:{nodePath}的数据。"); + } - var onlineData = (await zooKeeper.Item2.getDataAsync(nodePath)).Data; - if (!DataEquals(nodeData, onlineData)) - await zooKeeper.Item2.setDataAsync(nodePath, nodeData); - } - } - if (_logger.IsEnabled(LogLevel.Information)) - _logger.LogInformation("mqtt服务路由添加成功。"); - } + /// + /// 获取所有可用的mqtt服务路由信息。 + /// + /// 服务路由集合。 + public override async Task> GetRoutesAsync() + { + await EnterRoutes(); + return _routes; } - public override async Task RemveAddressAsync(IEnumerable Address) + /// + /// The NodeChange + /// + /// The oldData + /// The newData + /// The + public async Task NodeChange(byte[] oldData, byte[] newData) { - var routes = await GetRoutesAsync(); - foreach (var route in routes) + if (DataEquals(oldData, newData)) + return; + + var newRoute = await GetRoute(newData); + //得到旧的mqtt路由。 + var oldRoute = _routes.FirstOrDefault(i => i.MqttDescriptor.Topic == newRoute.MqttDescriptor.Topic); + + lock (_routes) { - route.MqttEndpoint = route.MqttEndpoint.Except(Address); + //删除旧mqtt路由,并添加上新的mqtt路由。 + _routes = + _routes + .Where(i => i.MqttDescriptor.Topic != newRoute.MqttDescriptor.Topic) + .Concat(new[] { newRoute }).ToArray(); } - await base.SetRoutesAsync(routes); + + //触发路由变更事件。 + OnChanged(new MqttServiceRouteChangedEventArgs(newRoute, oldRoute)); } + /// + /// The RemoveByTopicAsync + /// + /// The topic + /// The endpoint + /// The public override async Task RemoveByTopicAsync(string topic, IEnumerable endpoint) { var routes = await GetRoutesAsync(); @@ -161,6 +239,26 @@ public override async Task RemoveByTopicAsync(string topic, IEnumerable + /// The RemveAddressAsync + /// + /// The Address + /// The + public override async Task RemveAddressAsync(IEnumerable Address) + { + var routes = await GetRoutesAsync(); + foreach (var route in routes) + { + route.MqttEndpoint = route.MqttEndpoint.Except(Address); + } + await base.SetRoutesAsync(routes); + } + + /// + /// The SetRoutesAsync + /// + /// The routes + /// The public override async Task SetRoutesAsync(IEnumerable routes) { var hostAddr = NetUtils.GetHostAddress(); @@ -188,33 +286,78 @@ public override async Task SetRoutesAsync(IEnumerable routes) await base.SetRoutesAsync(routes); } - private async Task RemoveExceptRoutesAsync(IEnumerable routes, AddressModel hostAddr) + /// + /// 设置mqtt服务路由。 + /// + /// 服务路由集合。 + /// 一个任务。 + protected override async Task SetRoutesAsync(IEnumerable routes) { - var path = _configInfo.MqttRoutePath; - if (!path.EndsWith("/")) - path += "/"; - routes = routes.ToArray(); + if (_logger.IsEnabled(LogLevel.Information)) + _logger.LogInformation("准备添加mqtt服务路由。"); var zooKeepers = await _zookeeperClientProvider.GetZooKeepers(); foreach (var zooKeeper in zooKeepers) { - if (_routes != null) + await CreateSubdirectory(zooKeeper, _configInfo.MqttRoutePath); + + var path = _configInfo.MqttRoutePath; + if (!path.EndsWith("/")) + path += "/"; + + routes = routes.ToArray(); + + foreach (var serviceRoute in routes) { - var oldRouteTopics = _routes.Select(i => i.MqttDescriptor.Topic).ToArray(); - var newRouteTopics = routes.Select(i => i.MqttDescriptor.Topic).ToArray(); - var deletedRouteTopics = oldRouteTopics.Except(newRouteTopics).ToArray(); - foreach (var deletedRouteTopic in deletedRouteTopics) + var nodePath = $"{path}{serviceRoute.MqttDescriptor.Topic}"; + var nodeData = _serializer.Serialize(serviceRoute); + if (await zooKeeper.Item2.existsAsync(nodePath) == null) { - var addresses = _routes.Where(p => p.MqttDescriptor.Topic == deletedRouteTopic).Select(p => p.MqttEndpoint).FirstOrDefault(); - if (addresses.Contains(hostAddr)) - { - var nodePath = $"{path}{deletedRouteTopic}"; - await zooKeeper.Item2.deleteAsync(nodePath); - } + if (_logger.IsEnabled(LogLevel.Debug)) + _logger.LogDebug($"节点:{nodePath}不存在将进行创建。"); + + await zooKeeper.Item2.createAsync(nodePath, nodeData, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + } + else + { + if (_logger.IsEnabled(LogLevel.Debug)) + _logger.LogDebug($"将更新节点:{nodePath}的数据。"); + + var onlineData = (await zooKeeper.Item2.getDataAsync(nodePath)).Data; + if (!DataEquals(nodeData, onlineData)) + await zooKeeper.Item2.setDataAsync(nodePath, nodeData); } } + if (_logger.IsEnabled(LogLevel.Information)) + _logger.LogInformation("mqtt服务路由添加成功。"); + } + } + + /// + /// The DataEquals + /// + /// The data1 + /// The data2 + /// The + private static bool DataEquals(IReadOnlyList data1, IReadOnlyList data2) + { + if (data1.Count != data2.Count) + return false; + for (var i = 0; i < data1.Count; i++) + { + var b1 = data1[i]; + var b2 = data2[i]; + if (b1 != b2) + return false; } + return true; } + /// + /// The CreateSubdirectory + /// + /// The zooKeeper + /// The path + /// The private async Task CreateSubdirectory((ManualResetEvent, ZooKeeper) zooKeeper, string path) { zooKeeper.Item1.WaitOne(); @@ -238,6 +381,38 @@ private async Task CreateSubdirectory((ManualResetEvent, ZooKeeper) zooKeeper, s } } + /// + /// The EnterRoutes + /// + /// The + private async Task EnterRoutes() + { + if (_routes != null) + return; + var zooKeeper = await GetZooKeeper(); + zooKeeper.Item1.WaitOne(); + var watcher = new ChildrenMonitorWatcher(GetZooKeeper, _configInfo.MqttRoutePath, + async (oldChildrens, newChildrens) => await ChildrenChange(oldChildrens, newChildrens)); + if (await zooKeeper.Item2.existsAsync(_configInfo.MqttRoutePath, watcher) != null) + { + var result = await zooKeeper.Item2.getChildrenAsync(_configInfo.MqttRoutePath, watcher); + var childrens = result.Children.ToArray(); + watcher.SetCurrentData(childrens); + _routes = await GetRoutes(childrens); + } + else + { + if (_logger.IsEnabled(LogLevel.Warning)) + _logger.LogWarning($"无法获取mqtt路由信息,因为节点:{_configInfo.RoutePath},不存在。"); + _routes = new MqttServiceRoute[0]; + } + } + + /// + /// The GetRoute + /// + /// The data + /// The private async Task GetRoute(byte[] data) { if (_logger.IsEnabled(LogLevel.Debug)) @@ -250,6 +425,11 @@ private async Task GetRoute(byte[] data) return (await _mqttServiceFactory.CreateMqttServiceRoutesAsync(new[] { descriptor })).First(); } + /// + /// The GetRoute + /// + /// The path + /// The private async Task GetRoute(string path) { MqttServiceRoute result = null; @@ -266,6 +446,11 @@ private async Task GetRoute(string path) return result; } + /// + /// The GetRoutes + /// + /// The childrens + /// The private async Task GetRoutes(IEnumerable childrens) { var rootPath = _configInfo.MqttRoutePath; @@ -289,119 +474,49 @@ private async Task GetRoutes(IEnumerable childrens) return routes.ToArray(); } - private async Task EnterRoutes() - { - if (_routes != null) - return; - var zooKeeper = await GetZooKeeper(); - zooKeeper.Item1.WaitOne(); - var watcher = new ChildrenMonitorWatcher(GetZooKeeper, _configInfo.MqttRoutePath, - async (oldChildrens, newChildrens) => await ChildrenChange(oldChildrens, newChildrens)); - if (await zooKeeper.Item2.existsAsync(_configInfo.MqttRoutePath, watcher) != null) - { - var result = await zooKeeper.Item2.getChildrenAsync(_configInfo.MqttRoutePath, watcher); - var childrens = result.Children.ToArray(); - watcher.SetCurrentData(childrens); - _routes = await GetRoutes(childrens); - } - else - { - if (_logger.IsEnabled(LogLevel.Warning)) - _logger.LogWarning($"无法获取mqtt路由信息,因为节点:{_configInfo.RoutePath},不存在。"); - _routes = new MqttServiceRoute[0]; - } - } - - private static bool DataEquals(IReadOnlyList data1, IReadOnlyList data2) - { - if (data1.Count != data2.Count) - return false; - for (var i = 0; i < data1.Count; i++) - { - var b1 = data1[i]; - var b2 = data2[i]; - if (b1 != b2) - return false; - } - return true; - } - - public async Task NodeChange(byte[] oldData, byte[] newData) + /// + /// The GetZooKeeper + /// + /// The + private async ValueTask<(ManualResetEvent, ZooKeeper)> GetZooKeeper() { - if (DataEquals(oldData, newData)) - return; - - var newRoute = await GetRoute(newData); - //得到旧的mqtt路由。 - var oldRoute = _routes.FirstOrDefault(i => i.MqttDescriptor.Topic == newRoute.MqttDescriptor.Topic); - - lock (_routes) - { - //删除旧mqtt路由,并添加上新的mqtt路由。 - _routes = - _routes - .Where(i => i.MqttDescriptor.Topic != newRoute.MqttDescriptor.Topic) - .Concat(new[] { newRoute }).ToArray(); - } - - //触发路由变更事件。 - OnChanged(new MqttServiceRouteChangedEventArgs(newRoute, oldRoute)); + var zooKeeper = await _zookeeperClientProvider.GetZooKeeper(); + return zooKeeper; } - public async Task ChildrenChange(string[] oldChildrens, string[] newChildrens) + /// + /// The RemoveExceptRoutesAsync + /// + /// The routes + /// The hostAddr + /// The + private async Task RemoveExceptRoutesAsync(IEnumerable routes, AddressModel hostAddr) { - if (_logger.IsEnabled(LogLevel.Debug)) - _logger.LogDebug($"最新的mqtt节点信息:{string.Join(",", newChildrens)}"); - - if (_logger.IsEnabled(LogLevel.Debug)) - _logger.LogDebug($"旧的mqtt节点信息:{string.Join(",", oldChildrens)}"); - - //计算出已被删除的节点。 - var deletedChildrens = oldChildrens.Except(newChildrens).ToArray(); - //计算出新增的节点。 - var createdChildrens = newChildrens.Except(oldChildrens).ToArray(); - - if (_logger.IsEnabled(LogLevel.Debug)) - _logger.LogDebug($"需要被删除的mqtt路由节点:{string.Join(",", deletedChildrens)}"); - if (_logger.IsEnabled(LogLevel.Debug)) - _logger.LogDebug($"需要被添加的mqtt路由节点:{string.Join(",", createdChildrens)}"); - - //获取新增的mqtt路由信息。 - var newRoutes = (await GetRoutes(createdChildrens)).ToArray(); - - var routes = _routes.ToArray(); - lock (_routes) + var path = _configInfo.MqttRoutePath; + if (!path.EndsWith("/")) + path += "/"; + routes = routes.ToArray(); + var zooKeepers = await _zookeeperClientProvider.GetZooKeepers(); + foreach (var zooKeeper in zooKeepers) { - _routes = _routes - //删除无效的节点路由。 - .Where(i => !deletedChildrens.Contains(i.MqttDescriptor.Topic)) - //连接上新的mqtt路由。 - .Concat(newRoutes) - .ToArray(); + if (_routes != null) + { + var oldRouteTopics = _routes.Select(i => i.MqttDescriptor.Topic).ToArray(); + var newRouteTopics = routes.Select(i => i.MqttDescriptor.Topic).ToArray(); + var deletedRouteTopics = oldRouteTopics.Except(newRouteTopics).ToArray(); + foreach (var deletedRouteTopic in deletedRouteTopics) + { + var addresses = _routes.Where(p => p.MqttDescriptor.Topic == deletedRouteTopic).Select(p => p.MqttEndpoint).FirstOrDefault(); + if (addresses.Contains(hostAddr)) + { + var nodePath = $"{path}{deletedRouteTopic}"; + await zooKeeper.Item2.deleteAsync(nodePath); + } + } + } } - //需要删除的Topic路由集合。 - var deletedRoutes = routes.Where(i => deletedChildrens.Contains(i.MqttDescriptor.Topic)).ToArray(); - //触发删除事件。 - OnRemoved(deletedRoutes.Select(route => new MqttServiceRouteEventArgs(route)).ToArray()); - - //触发路由被创建事件。 - OnCreated(newRoutes.Select(route => new MqttServiceRouteEventArgs(route)).ToArray()); - - if (_logger.IsEnabled(LogLevel.Information)) - _logger.LogInformation("mqtt路由数据更新成功。"); - } - - - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - public void Dispose() - { - } - - private async ValueTask<(ManualResetEvent, ZooKeeper)> GetZooKeeper() - { - var zooKeeper = await _zookeeperClientProvider.GetZooKeeper(); - return zooKeeper; } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/ZooKeeperServiceRouteManager.cs b/src/Surging.Core/Surging.Core.Zookeeper/ZooKeeperServiceRouteManager.cs index 4bdaceab9..f2d9dc2d3 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/ZooKeeperServiceRouteManager.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/ZooKeeperServiceRouteManager.cs @@ -19,15 +19,56 @@ namespace Surging.Core.Zookeeper { + /// + /// Defines the + /// public class ZooKeeperServiceRouteManager : ServiceRouteManagerBase, IDisposable - { + { + #region 字段 + + /// + /// Defines the _configInfo + /// private readonly ConfigInfo _configInfo; + + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the _serializer + /// private readonly ISerializer _serializer; + + /// + /// Defines the _serviceRouteFactory + /// private readonly IServiceRouteFactory _serviceRouteFactory; - private readonly ILogger _logger; - private ServiceRoute[] _routes; + + /// + /// Defines the _zookeeperClientProvider + /// private readonly IZookeeperClientProvider _zookeeperClientProvider; + /// + /// Defines the _routes + /// + private ServiceRoute[] _routes; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The configInfo + /// The serializer + /// The stringSerializer + /// The serviceRouteFactory + /// The logger + /// The zookeeperClientProvider public ZooKeeperServiceRouteManager(ConfigInfo configInfo, ISerializer serializer, ISerializer stringSerializer, IServiceRouteFactory serviceRouteFactory, ILogger logger, IZookeeperClientProvider zookeeperClientProvider) : base(stringSerializer) @@ -36,19 +77,61 @@ public ZooKeeperServiceRouteManager(ConfigInfo configInfo, ISerializer s _serializer = serializer; _serviceRouteFactory = serviceRouteFactory; _logger = logger; - _zookeeperClientProvider = zookeeperClientProvider; + _zookeeperClientProvider = zookeeperClientProvider; EnterRoutes().Wait(); } + #endregion 构造函数 + + #region 方法 /// - /// 获取所有可用的服务路由信息。 + /// The ChildrenChange /// - /// 服务路由集合。 - public override async Task> GetRoutesAsync() + /// The oldChildrens + /// The newChildrens + /// The + public async Task ChildrenChange(string[] oldChildrens, string[] newChildrens) { - await EnterRoutes(); - return _routes; + if (_logger.IsEnabled(LogLevel.Debug)) + _logger.LogDebug($"最新的节点信息:{string.Join(",", newChildrens)}"); + + if (_logger.IsEnabled(LogLevel.Debug)) + _logger.LogDebug($"旧的节点信息:{string.Join(",", oldChildrens)}"); + + //计算出已被删除的节点。 + var deletedChildrens = oldChildrens.Except(newChildrens).ToArray(); + //计算出新增的节点。 + var createdChildrens = newChildrens.Except(oldChildrens).ToArray(); + + if (_logger.IsEnabled(LogLevel.Debug)) + _logger.LogDebug($"需要被删除的路由节点:{string.Join(",", deletedChildrens)}"); + if (_logger.IsEnabled(LogLevel.Debug)) + _logger.LogDebug($"需要被添加的路由节点:{string.Join(",", createdChildrens)}"); + + //获取新增的路由信息。 + var newRoutes = (await GetRoutes(createdChildrens)).ToArray(); + + var routes = _routes.ToArray(); + lock (_routes) + { + _routes = _routes + //删除无效的节点路由。 + .Where(i => !deletedChildrens.Contains(i.ServiceDescriptor.Id)) + //连接上新的路由。 + .Concat(newRoutes) + .ToArray(); + } + //需要删除的路由集合。 + var deletedRoutes = routes.Where(i => deletedChildrens.Contains(i.ServiceDescriptor.Id)).ToArray(); + //触发删除事件。 + OnRemoved(deletedRoutes.Select(route => new ServiceRouteEventArgs(route)).ToArray()); + + //触发路由被创建事件。 + OnCreated(newRoutes.Select(route => new ServiceRouteEventArgs(route)).ToArray()); + + if (_logger.IsEnabled(LogLevel.Information)) + _logger.LogInformation("路由数据更新成功。"); } /// @@ -96,51 +179,55 @@ public override async Task ClearAsync() } /// - /// 设置服务路由。 + /// The Dispose /// - /// 服务路由集合。 - /// 一个任务。 - protected override async Task SetRoutesAsync(IEnumerable routes) + public void Dispose() { - if (_logger.IsEnabled(LogLevel.Information)) - _logger.LogInformation("准备添加服务路由。"); - var zooKeepers = await _zookeeperClientProvider.GetZooKeepers(); - foreach (var zooKeeper in zooKeepers) - { - await CreateSubdirectory(zooKeeper,_configInfo.RoutePath); - - var path = _configInfo.RoutePath; - if (!path.EndsWith("/")) - path += "/"; + } - routes = routes.ToArray(); + /// + /// 获取所有可用的服务路由信息。 + /// + /// 服务路由集合。 + public override async Task> GetRoutesAsync() + { + await EnterRoutes(); + return _routes; + } - foreach (var serviceRoute in routes) - { - var nodePath = $"{path}{serviceRoute.ServiceDescriptor.Id}"; - var nodeData = _serializer.Serialize(serviceRoute); - if (await zooKeeper.Item2.existsAsync(nodePath) == null) - { - if (_logger.IsEnabled(LogLevel.Debug)) - _logger.LogDebug($"节点:{nodePath}不存在将进行创建。"); + /// + /// The NodeChange + /// + /// The oldData + /// The newData + /// The + public async Task NodeChange(byte[] oldData, byte[] newData) + { + if (DataEquals(oldData, newData)) + return; - await zooKeeper.Item2.createAsync(nodePath, nodeData, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); - } - else - { - if (_logger.IsEnabled(LogLevel.Debug)) - _logger.LogDebug($"将更新节点:{nodePath}的数据。"); + var newRoute = await GetRoute(newData); + //得到旧的路由。 + var oldRoute = _routes.FirstOrDefault(i => i.ServiceDescriptor.Id == newRoute.ServiceDescriptor.Id); - var onlineData = (await zooKeeper.Item2.getDataAsync(nodePath)).Data; - if (!DataEquals(nodeData, onlineData)) - await zooKeeper.Item2.setDataAsync(nodePath, nodeData); - } - } - if (_logger.IsEnabled(LogLevel.Information)) - _logger.LogInformation("服务路由添加成功。"); + lock (_routes) + { + //删除旧路由,并添加上新的路由。 + _routes = + _routes + .Where(i => i.ServiceDescriptor.Id != newRoute.ServiceDescriptor.Id) + .Concat(new[] { newRoute }).ToArray(); } + + //触发路由变更事件。 + OnChanged(new ServiceRouteChangedEventArgs(newRoute, oldRoute)); } + /// + /// The RemveAddressAsync + /// + /// The Address + /// The public override async Task RemveAddressAsync(IEnumerable Address) { var routes = await GetRoutesAsync(); @@ -151,6 +238,11 @@ public override async Task RemveAddressAsync(IEnumerable Address) await base.SetRoutesAsync(routes); } + /// + /// The SetRoutesAsync + /// + /// The routes + /// The public override async Task SetRoutesAsync(IEnumerable routes) { var hostAddr = NetUtils.GetHostAddress(); @@ -178,34 +270,79 @@ public override async Task SetRoutesAsync(IEnumerable routes) await base.SetRoutesAsync(routes); } - private async Task RemoveExceptRoutesAsync(IEnumerable routes, AddressModel hostAddr) + /// + /// 设置服务路由。 + /// + /// 服务路由集合。 + /// 一个任务。 + protected override async Task SetRoutesAsync(IEnumerable routes) { - var path = _configInfo.RoutePath; - if (!path.EndsWith("/")) - path += "/"; - routes = routes.ToArray(); + if (_logger.IsEnabled(LogLevel.Information)) + _logger.LogInformation("准备添加服务路由。"); var zooKeepers = await _zookeeperClientProvider.GetZooKeepers(); foreach (var zooKeeper in zooKeepers) { - if (_routes != null) + await CreateSubdirectory(zooKeeper, _configInfo.RoutePath); + + var path = _configInfo.RoutePath; + if (!path.EndsWith("/")) + path += "/"; + + routes = routes.ToArray(); + + foreach (var serviceRoute in routes) { - var oldRouteIds = _routes.Select(i => i.ServiceDescriptor.Id).ToArray(); - var newRouteIds = routes.Select(i => i.ServiceDescriptor.Id).ToArray(); - var deletedRouteIds = oldRouteIds.Except(newRouteIds).ToArray(); - foreach (var deletedRouteId in deletedRouteIds) + var nodePath = $"{path}{serviceRoute.ServiceDescriptor.Id}"; + var nodeData = _serializer.Serialize(serviceRoute); + if (await zooKeeper.Item2.existsAsync(nodePath) == null) { - var addresses = _routes.Where(p => p.ServiceDescriptor.Id == deletedRouteId).Select(p => p.Address).FirstOrDefault(); - if (addresses.Contains(hostAddr)) - { - var nodePath = $"{path}{deletedRouteId}"; - await zooKeeper.Item2.deleteAsync(nodePath); - } + if (_logger.IsEnabled(LogLevel.Debug)) + _logger.LogDebug($"节点:{nodePath}不存在将进行创建。"); + + await zooKeeper.Item2.createAsync(nodePath, nodeData, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + } + else + { + if (_logger.IsEnabled(LogLevel.Debug)) + _logger.LogDebug($"将更新节点:{nodePath}的数据。"); + + var onlineData = (await zooKeeper.Item2.getDataAsync(nodePath)).Data; + if (!DataEquals(nodeData, onlineData)) + await zooKeeper.Item2.setDataAsync(nodePath, nodeData); } } + if (_logger.IsEnabled(LogLevel.Information)) + _logger.LogInformation("服务路由添加成功。"); + } + } + + /// + /// The DataEquals + /// + /// The data1 + /// The data2 + /// The + private static bool DataEquals(IReadOnlyList data1, IReadOnlyList data2) + { + if (data1.Count != data2.Count) + return false; + for (var i = 0; i < data1.Count; i++) + { + var b1 = data1[i]; + var b2 = data2[i]; + if (b1 != b2) + return false; } + return true; } - private async Task CreateSubdirectory((ManualResetEvent, ZooKeeper) zooKeeper, string path) + /// + /// The CreateSubdirectory + /// + /// The zooKeeper + /// The path + /// The + private async Task CreateSubdirectory((ManualResetEvent, ZooKeeper) zooKeeper, string path) { zooKeeper.Item1.WaitOne(); if (await zooKeeper.Item2.existsAsync(path) != null) @@ -228,6 +365,38 @@ private async Task CreateSubdirectory((ManualResetEvent, ZooKeeper) zooKeeper, } } + /// + /// The EnterRoutes + /// + /// The + private async Task EnterRoutes() + { + if (_routes != null) + return; + var zooKeeper = await GetZooKeeper(); + zooKeeper.Item1.WaitOne(); + var watcher = new ChildrenMonitorWatcher(GetZooKeeper, _configInfo.RoutePath, + async (oldChildrens, newChildrens) => await ChildrenChange(oldChildrens, newChildrens)); + if (await zooKeeper.Item2.existsAsync(_configInfo.RoutePath, watcher) != null) + { + var result = await zooKeeper.Item2.getChildrenAsync(_configInfo.RoutePath, watcher); + var childrens = result.Children.ToArray(); + watcher.SetCurrentData(childrens); + _routes = await GetRoutes(childrens); + } + else + { + if (_logger.IsEnabled(LogLevel.Warning)) + _logger.LogWarning($"无法获取路由信息,因为节点:{_configInfo.RoutePath},不存在。"); + _routes = new ServiceRoute[0]; + } + } + + /// + /// The GetRoute + /// + /// The data + /// The private async Task GetRoute(byte[] data) { if (_logger.IsEnabled(LogLevel.Debug)) @@ -240,6 +409,11 @@ private async Task GetRoute(byte[] data) return (await _serviceRouteFactory.CreateServiceRoutesAsync(new[] { descriptor })).First(); } + /// + /// The GetRoute + /// + /// The path + /// The private async Task GetRoute(string path) { ServiceRoute result = null; @@ -255,6 +429,11 @@ private async Task GetRoute(string path) return result; } + /// + /// The GetRoutes + /// + /// The childrens + /// The private async Task GetRoutes(IEnumerable childrens) { var rootPath = _configInfo.RoutePath; @@ -278,119 +457,49 @@ private async Task GetRoutes(IEnumerable childrens) return routes.ToArray(); } - private async Task EnterRoutes() - { - if (_routes != null) - return; - var zooKeeper = await GetZooKeeper(); - zooKeeper.Item1.WaitOne(); - var watcher = new ChildrenMonitorWatcher(GetZooKeeper, _configInfo.RoutePath, - async (oldChildrens, newChildrens) => await ChildrenChange(oldChildrens, newChildrens)); - if (await zooKeeper.Item2.existsAsync(_configInfo.RoutePath, watcher) != null) - { - var result = await zooKeeper.Item2.getChildrenAsync(_configInfo.RoutePath, watcher); - var childrens = result.Children.ToArray(); - watcher.SetCurrentData(childrens); - _routes = await GetRoutes(childrens); - } - else - { - if (_logger.IsEnabled(LogLevel.Warning)) - _logger.LogWarning($"无法获取路由信息,因为节点:{_configInfo.RoutePath},不存在。"); - _routes = new ServiceRoute[0]; - } - } - - private static bool DataEquals(IReadOnlyList data1, IReadOnlyList data2) - { - if (data1.Count != data2.Count) - return false; - for (var i = 0; i < data1.Count; i++) - { - var b1 = data1[i]; - var b2 = data2[i]; - if (b1 != b2) - return false; - } - return true; - } - - public async Task NodeChange(byte[] oldData, byte[] newData) + /// + /// The GetZooKeeper + /// + /// The + private async ValueTask<(ManualResetEvent, ZooKeeper)> GetZooKeeper() { - if (DataEquals(oldData, newData)) - return; - - var newRoute = await GetRoute(newData); - //得到旧的路由。 - var oldRoute = _routes.FirstOrDefault(i => i.ServiceDescriptor.Id == newRoute.ServiceDescriptor.Id); - - lock (_routes) - { - //删除旧路由,并添加上新的路由。 - _routes = - _routes - .Where(i => i.ServiceDescriptor.Id != newRoute.ServiceDescriptor.Id) - .Concat(new[] { newRoute }).ToArray(); - } - - //触发路由变更事件。 - OnChanged(new ServiceRouteChangedEventArgs(newRoute, oldRoute)); + var zooKeeper = await _zookeeperClientProvider.GetZooKeeper(); + return zooKeeper; } - public async Task ChildrenChange(string[] oldChildrens, string[] newChildrens) + /// + /// The RemoveExceptRoutesAsync + /// + /// The routes + /// The hostAddr + /// The + private async Task RemoveExceptRoutesAsync(IEnumerable routes, AddressModel hostAddr) { - if (_logger.IsEnabled(LogLevel.Debug)) - _logger.LogDebug($"最新的节点信息:{string.Join(",", newChildrens)}"); - - if (_logger.IsEnabled(LogLevel.Debug)) - _logger.LogDebug($"旧的节点信息:{string.Join(",", oldChildrens)}"); - - //计算出已被删除的节点。 - var deletedChildrens = oldChildrens.Except(newChildrens).ToArray(); - //计算出新增的节点。 - var createdChildrens = newChildrens.Except(oldChildrens).ToArray(); - - if (_logger.IsEnabled(LogLevel.Debug)) - _logger.LogDebug($"需要被删除的路由节点:{string.Join(",", deletedChildrens)}"); - if (_logger.IsEnabled(LogLevel.Debug)) - _logger.LogDebug($"需要被添加的路由节点:{string.Join(",", createdChildrens)}"); - - //获取新增的路由信息。 - var newRoutes = (await GetRoutes(createdChildrens)).ToArray(); - - var routes = _routes.ToArray(); - lock (_routes) + var path = _configInfo.RoutePath; + if (!path.EndsWith("/")) + path += "/"; + routes = routes.ToArray(); + var zooKeepers = await _zookeeperClientProvider.GetZooKeepers(); + foreach (var zooKeeper in zooKeepers) { - _routes = _routes - //删除无效的节点路由。 - .Where(i => !deletedChildrens.Contains(i.ServiceDescriptor.Id)) - //连接上新的路由。 - .Concat(newRoutes) - .ToArray(); + if (_routes != null) + { + var oldRouteIds = _routes.Select(i => i.ServiceDescriptor.Id).ToArray(); + var newRouteIds = routes.Select(i => i.ServiceDescriptor.Id).ToArray(); + var deletedRouteIds = oldRouteIds.Except(newRouteIds).ToArray(); + foreach (var deletedRouteId in deletedRouteIds) + { + var addresses = _routes.Where(p => p.ServiceDescriptor.Id == deletedRouteId).Select(p => p.Address).FirstOrDefault(); + if (addresses.Contains(hostAddr)) + { + var nodePath = $"{path}{deletedRouteId}"; + await zooKeeper.Item2.deleteAsync(nodePath); + } + } + } } - //需要删除的路由集合。 - var deletedRoutes = routes.Where(i => deletedChildrens.Contains(i.ServiceDescriptor.Id)).ToArray(); - //触发删除事件。 - OnRemoved(deletedRoutes.Select(route => new ServiceRouteEventArgs(route)).ToArray()); - - //触发路由被创建事件。 - OnCreated(newRoutes.Select(route => new ServiceRouteEventArgs(route)).ToArray()); - - if (_logger.IsEnabled(LogLevel.Information)) - _logger.LogInformation("路由数据更新成功。"); - } - - - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - public void Dispose() - { - } - - private async ValueTask<(ManualResetEvent, ZooKeeper)> GetZooKeeper() - { - var zooKeeper = await _zookeeperClientProvider.GetZooKeeper(); - return zooKeeper; } + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/ZooKeeperServiceSubscribeManager.cs b/src/Surging.Core/Surging.Core.Zookeeper/ZooKeeperServiceSubscribeManager.cs index fc2263a17..eb6c6731b 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/ZooKeeperServiceSubscribeManager.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/ZooKeeperServiceSubscribeManager.cs @@ -1,29 +1,70 @@ -using Surging.Core.CPlatform.Runtime.Client.Implementation; +using Microsoft.Extensions.Logging; +using org.apache.zookeeper; +using Surging.Core.CPlatform.Runtime.Client; +using Surging.Core.CPlatform.Runtime.Client.Implementation; +using Surging.Core.CPlatform.Serialization; +using Surging.Core.Zookeeper.Configurations; +using Surging.Core.Zookeeper.Internal; +using Surging.Core.Zookeeper.WatcherProvider; using System; using System.Collections.Generic; +using System.Linq; using System.Text; -using Surging.Core.CPlatform.Runtime.Client; -using System.Threading.Tasks; -using Surging.Core.Zookeeper.Configurations; -using org.apache.zookeeper; -using Surging.Core.CPlatform.Serialization; using System.Threading; -using Microsoft.Extensions.Logging; -using System.Linq; -using Surging.Core.Zookeeper.WatcherProvider; -using Surging.Core.Zookeeper.Internal; +using System.Threading.Tasks; namespace Surging.Core.Zookeeper { + /// + /// Defines the + /// public class ZooKeeperServiceSubscribeManager : ServiceSubscribeManagerBase, IDisposable - { + { + #region 字段 + + /// + /// Defines the _configInfo + /// private readonly ConfigInfo _configInfo; + + /// + /// Defines the _logger + /// + private readonly ILogger _logger; + + /// + /// Defines the _serializer + /// private readonly ISerializer _serializer; + + /// + /// Defines the _serviceSubscriberFactory + /// private readonly IServiceSubscriberFactory _serviceSubscriberFactory; - private ServiceSubscriber[] _subscribers; - private readonly ILogger _logger; + + /// + /// Defines the _zookeeperClientProvider + /// private readonly IZookeeperClientProvider _zookeeperClientProvider; + /// + /// Defines the _subscribers + /// + private ServiceSubscriber[] _subscribers; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The configInfo + /// The serializer + /// The stringSerializer + /// The serviceSubscriberFactory + /// The logger + /// The zookeeperClientProvider public ZooKeeperServiceSubscribeManager(ConfigInfo configInfo, ISerializer serializer, ISerializer stringSerializer, IServiceSubscriberFactory serviceSubscriberFactory, ILogger logger, IZookeeperClientProvider zookeeperClientProvider) : base(stringSerializer) @@ -35,16 +76,10 @@ public ZooKeeperServiceSubscribeManager(ConfigInfo configInfo, ISerializer - /// 获取所有可用的服务订阅者信息。 - /// - /// 服务路由集合。 - public override async Task> GetSubscribersAsync() - { - await EnterSubscribers(); - return _subscribers; - } + + #endregion 构造函数 + + #region 方法 /// /// 清空所有的服务订阅者。 @@ -90,10 +125,47 @@ public override async Task ClearAsync() } } + /// + /// The Dispose + /// + public void Dispose() + { + } + + /// + /// 获取所有可用的服务订阅者信息。 + /// + /// 服务路由集合。 + public override async Task> GetSubscribersAsync() + { + await EnterSubscribers(); + return _subscribers; + } + + /// + /// The SetSubscribersAsync + /// + /// The subscribers + /// The + public override async Task SetSubscribersAsync(IEnumerable subscribers) + { + var serviceSubscribers = await GetSubscribers(subscribers.Select(p => p.ServiceDescriptor.Id)); + foreach (var subscriber in subscribers) + { + var serviceSubscriber = serviceSubscribers.Where(p => p.ServiceDescriptor.Id == subscriber.ServiceDescriptor.Id).FirstOrDefault(); + if (serviceSubscriber != null) + { + subscriber.Address = subscriber.Address.Concat( + subscriber.Address.Except(serviceSubscriber.Address)); + } + } + await base.SetSubscribersAsync(subscribers); + } + /// /// 设置服务订阅者。 /// - /// 服务订阅者集合。 + /// The subscribers /// 一个任务。 protected override async Task SetSubscribersAsync(IEnumerable subscribers) { @@ -148,22 +220,32 @@ protected override async Task SetSubscribersAsync(IEnumerable subscribers) + /// + /// The DataEquals + /// + /// The data1 + /// The data2 + /// The + private static bool DataEquals(IReadOnlyList data1, IReadOnlyList data2) { - var serviceSubscribers = await GetSubscribers(subscribers.Select(p => p.ServiceDescriptor.Id)); - foreach (var subscriber in subscribers) + if (data1.Count != data2.Count) + return false; + for (var i = 0; i < data1.Count; i++) { - var serviceSubscriber = serviceSubscribers.Where(p => p.ServiceDescriptor.Id == subscriber.ServiceDescriptor.Id).FirstOrDefault(); - if (serviceSubscriber != null) - { - subscriber.Address = subscriber.Address.Concat( - subscriber.Address.Except(serviceSubscriber.Address)); - } + var b1 = data1[i]; + var b2 = data2[i]; + if (b1 != b2) + return false; } - await base.SetSubscribersAsync(subscribers); + return true; } - + /// + /// The CreateSubdirectory + /// + /// The zooKeeper + /// The path + /// The private async Task CreateSubdirectory((ManualResetEvent, ZooKeeper) zooKeeper, string path) { zooKeeper.Item1.WaitOne(); @@ -187,6 +269,36 @@ private async Task CreateSubdirectory((ManualResetEvent, ZooKeeper) zooKeeper, s } } + /// + /// The EnterSubscribers + /// + /// The + private async Task EnterSubscribers() + { + if (_subscribers != null) + return; + var zooKeeper = await GetZooKeeper(); + zooKeeper.Item1.WaitOne(); + + if (await zooKeeper.Item2.existsAsync(_configInfo.SubscriberPath) != null) + { + var result = await zooKeeper.Item2.getChildrenAsync(_configInfo.SubscriberPath); + var childrens = result.Children.ToArray(); + _subscribers = await GetSubscribers(childrens); + } + else + { + if (_logger.IsEnabled(LogLevel.Warning)) + _logger.LogWarning($"无法获取订阅者信息,因为节点:{_configInfo.SubscriberPath},不存在。"); + _subscribers = new ServiceSubscriber[0]; + } + } + + /// + /// The GetSubscriber + /// + /// The data + /// The private async Task GetSubscriber(byte[] data) { if (_logger.IsEnabled(LogLevel.Debug)) @@ -199,6 +311,11 @@ private async Task GetSubscriber(byte[] data) return (await _serviceSubscriberFactory.CreateServiceSubscribersAsync(new[] { descriptor })).First(); } + /// + /// The GetSubscriber + /// + /// The path + /// The private async Task GetSubscriber(string path) { ServiceSubscriber result = null; @@ -212,6 +329,11 @@ private async Task GetSubscriber(string path) return result; } + /// + /// The GetSubscribers + /// + /// The childrens + /// The private async Task GetSubscribers(IEnumerable childrens) { var rootPath = _configInfo.SubscriberPath; @@ -234,49 +356,16 @@ private async Task GetSubscribers(IEnumerable child return subscribers.ToArray(); } - private async Task EnterSubscribers() - { - if (_subscribers != null) - return; - var zooKeeper = await GetZooKeeper(); - zooKeeper.Item1.WaitOne(); - - if (await zooKeeper.Item2.existsAsync(_configInfo.SubscriberPath) != null) - { - var result = await zooKeeper.Item2.getChildrenAsync(_configInfo.SubscriberPath); - var childrens = result.Children.ToArray(); - _subscribers = await GetSubscribers(childrens); - } - else - { - if (_logger.IsEnabled(LogLevel.Warning)) - _logger.LogWarning($"无法获取订阅者信息,因为节点:{_configInfo.SubscriberPath},不存在。"); - _subscribers = new ServiceSubscriber[0]; - } - } - - private static bool DataEquals(IReadOnlyList data1, IReadOnlyList data2) - { - if (data1.Count != data2.Count) - return false; - for (var i = 0; i < data1.Count; i++) - { - var b1 = data1[i]; - var b2 = data2[i]; - if (b1 != b2) - return false; - } - return true; - } - - public void Dispose() - { - } - + /// + /// The GetZooKeeper + /// + /// The private async ValueTask<(ManualResetEvent, ZooKeeper)> GetZooKeeper() { var zooKeeper = await _zookeeperClientProvider.GetZooKeeper(); return zooKeeper; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/ZookeeperModule.cs b/src/Surging.Core/Surging.Core.Zookeeper/ZookeeperModule.cs index ae990d615..678e97feb 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/ZookeeperModule.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/ZookeeperModule.cs @@ -21,61 +21,146 @@ namespace Surging.Core.Zookeeper { + /// + /// Defines the + /// public class ZookeeperModule : EnginePartModule { + #region 方法 + + /// + /// The Initialize + /// + /// The context public override void Initialize(AppModuleContext context) { base.Initialize(context); } /// - /// Inject dependent third-party components + /// The UseCacheManager /// - /// - protected override void RegisterBuilder(ContainerBuilderWrapper builder) + /// The builder + /// The factory + /// The + public ContainerBuilderWrapper UseCacheManager(ContainerBuilderWrapper builder, Func factory) { - base.RegisterBuilder(builder); - var configInfo = new ConfigInfo(null); - UseZookeeperAddressSelector(builder) - .UseHealthCheck(builder) - .UseZookeeperClientProvider(builder, configInfo) - .UseZooKeeperRouteManager(builder, configInfo) - .UseZooKeeperCacheManager(builder, configInfo) - .UseZooKeeperMqttRouteManager(builder, configInfo) - .UseZooKeeperServiceSubscribeManager(builder, configInfo) - .UseZooKeeperCommandManager(builder, configInfo); + builder.RegisterAdapter(factory).InstancePerLifetimeScope(); + return builder; } - public ContainerBuilderWrapper UseRouteManager(ContainerBuilderWrapper builder, Func factory) + /// + /// The UseCommandManager + /// + /// The builder + /// The factory + /// The + public ContainerBuilderWrapper UseCommandManager(ContainerBuilderWrapper builder, Func factory) { builder.RegisterAdapter(factory).InstancePerLifetimeScope(); return builder; } - public ContainerBuilderWrapper UseSubscribeManager(ContainerBuilderWrapper builder, Func factory) + /// + /// The UseHealthCheck + /// + /// The builder + /// The + public ZookeeperModule UseHealthCheck(ContainerBuilderWrapper builder) { - builder.RegisterAdapter(factory).InstancePerLifetimeScope(); - return builder; + builder.RegisterType().As().SingleInstance(); + return this; } - public ContainerBuilderWrapper UseCommandManager(ContainerBuilderWrapper builder, Func factory) + /// + /// The UseMqttRouteManager + /// + /// The builder + /// The factory + /// The + public ContainerBuilderWrapper UseMqttRouteManager(ContainerBuilderWrapper builder, Func factory) { builder.RegisterAdapter(factory).InstancePerLifetimeScope(); return builder; } - public ContainerBuilderWrapper UseCacheManager(ContainerBuilderWrapper builder, Func factory) + /// + /// The UseRouteManager + /// + /// The builder + /// The factory + /// The + public ContainerBuilderWrapper UseRouteManager(ContainerBuilderWrapper builder, Func factory) { builder.RegisterAdapter(factory).InstancePerLifetimeScope(); return builder; } - public ContainerBuilderWrapper UseMqttRouteManager(ContainerBuilderWrapper builder, Func factory) + /// + /// The UseSubscribeManager + /// + /// The builder + /// The factory + /// The + public ContainerBuilderWrapper UseSubscribeManager(ContainerBuilderWrapper builder, Func factory) { builder.RegisterAdapter(factory).InstancePerLifetimeScope(); return builder; } + /// + /// The UseZookeeperAddressSelector + /// + /// The builder + /// The + public ZookeeperModule UseZookeeperAddressSelector(ContainerBuilderWrapper builder) + { + builder.RegisterType().As().SingleInstance(); + return this; + } + + /// + /// The UseZooKeeperCacheManager + /// + /// The builder + /// The configInfo + /// The + public ZookeeperModule UseZooKeeperCacheManager(ContainerBuilderWrapper builder, ConfigInfo configInfo) + { + UseCacheManager(builder, provider => + new ZookeeperServiceCacheManager( + GetConfigInfo(configInfo), + provider.GetRequiredService>(), + provider.GetRequiredService>(), + provider.GetRequiredService(), + provider.GetRequiredService>(), + provider.GetRequiredService())); + return this; + } + + /// + /// The UseZookeeperClientProvider + /// + /// The builder + /// The configInfo + /// The + public ZookeeperModule UseZookeeperClientProvider(ContainerBuilderWrapper builder, ConfigInfo configInfo) + { + UseZooKeeperClientProvider(builder, provider => + new DefaultZookeeperClientProvider( + GetConfigInfo(configInfo), + provider.GetRequiredService(), + provider.GetRequiredService(), + provider.GetRequiredService>())); + return this; + } + + /// + /// The UseZooKeeperClientProvider + /// + /// The builder + /// The factory + /// The public ContainerBuilderWrapper UseZooKeeperClientProvider(ContainerBuilderWrapper builder, Func factory) { builder.RegisterAdapter(factory).InstancePerLifetimeScope(); @@ -83,24 +168,34 @@ public ContainerBuilderWrapper UseZooKeeperClientProvider(ContainerBuilderWrappe } /// - /// 设置共享文件路由管理者。 + /// 设置服务命令管理者。 /// /// Rpc服务构建者。 /// ZooKeeper设置信息。 /// 服务构建者。 - public ZookeeperModule UseZooKeeperRouteManager(ContainerBuilderWrapper builder, ConfigInfo configInfo) + public ZookeeperModule UseZooKeeperCommandManager(ContainerBuilderWrapper builder, ConfigInfo configInfo) { - UseRouteManager(builder, provider => - new ZooKeeperServiceRouteManager( - GetConfigInfo(configInfo), - provider.GetRequiredService>(), - provider.GetRequiredService>(), - provider.GetRequiredService(), - provider.GetRequiredService>(), - provider.GetRequiredService())); + UseCommandManager(builder, provider => + { + var result = new ZookeeperServiceCommandManager( + GetConfigInfo(configInfo), + provider.GetRequiredService>(), + provider.GetRequiredService>(), + provider.GetRequiredService(), + provider.GetRequiredService(), + provider.GetRequiredService>(), + provider.GetRequiredService()); + return result; + }); return this; } + /// + /// The UseZooKeeperMqttRouteManager + /// + /// The builder + /// The configInfo + /// The public ZookeeperModule UseZooKeeperMqttRouteManager(ContainerBuilderWrapper builder, ConfigInfo configInfo) { UseMqttRouteManager(builder, provider => @@ -117,33 +212,34 @@ public ZookeeperModule UseZooKeeperMqttRouteManager(ContainerBuilderWrapper buil return this; } - /// - /// 设置服务命令管理者。 + /// 设置共享文件路由管理者。 /// /// Rpc服务构建者。 /// ZooKeeper设置信息。 /// 服务构建者。 - public ZookeeperModule UseZooKeeperCommandManager(ContainerBuilderWrapper builder, ConfigInfo configInfo) + public ZookeeperModule UseZooKeeperRouteManager(ContainerBuilderWrapper builder, ConfigInfo configInfo) { - UseCommandManager(builder, provider => - { - var result = new ZookeeperServiceCommandManager( - GetConfigInfo(configInfo), - provider.GetRequiredService>(), - provider.GetRequiredService>(), - provider.GetRequiredService(), - provider.GetRequiredService(), - provider.GetRequiredService>(), - provider.GetRequiredService()); - return result; - }); + UseRouteManager(builder, provider => + new ZooKeeperServiceRouteManager( + GetConfigInfo(configInfo), + provider.GetRequiredService>(), + provider.GetRequiredService>(), + provider.GetRequiredService(), + provider.GetRequiredService>(), + provider.GetRequiredService())); return this; } + /// + /// The UseZooKeeperServiceSubscribeManager + /// + /// The builder + /// The configInfo + /// The public ZookeeperModule UseZooKeeperServiceSubscribeManager(ContainerBuilderWrapper builder, ConfigInfo configInfo) { - UseSubscribeManager(builder,provider => + UseSubscribeManager(builder, provider => { var result = new ZooKeeperServiceSubscribeManager( GetConfigInfo(configInfo), @@ -157,42 +253,29 @@ public ZookeeperModule UseZooKeeperServiceSubscribeManager(ContainerBuilderWrapp return this; } - public ZookeeperModule UseZooKeeperCacheManager(ContainerBuilderWrapper builder, ConfigInfo configInfo) - { - UseCacheManager(builder, provider => - new ZookeeperServiceCacheManager( - GetConfigInfo(configInfo), - provider.GetRequiredService>(), - provider.GetRequiredService>(), - provider.GetRequiredService(), - provider.GetRequiredService>(), - provider.GetRequiredService())); - return this; - } - - public ZookeeperModule UseZookeeperClientProvider(ContainerBuilderWrapper builder, ConfigInfo configInfo) - { - UseZooKeeperClientProvider(builder, provider => - new DefaultZookeeperClientProvider( - GetConfigInfo(configInfo), - provider.GetRequiredService(), - provider.GetRequiredService(), - provider.GetRequiredService>())); - return this; - } - - public ZookeeperModule UseZookeeperAddressSelector(ContainerBuilderWrapper builder) - { - builder.RegisterType().As().SingleInstance(); - return this; - } - - public ZookeeperModule UseHealthCheck(ContainerBuilderWrapper builder) + /// + /// Inject dependent third-party components + /// + /// + protected override void RegisterBuilder(ContainerBuilderWrapper builder) { - builder.RegisterType().As().SingleInstance(); - return this; + base.RegisterBuilder(builder); + var configInfo = new ConfigInfo(null); + UseZookeeperAddressSelector(builder) + .UseHealthCheck(builder) + .UseZookeeperClientProvider(builder, configInfo) + .UseZooKeeperRouteManager(builder, configInfo) + .UseZooKeeperCacheManager(builder, configInfo) + .UseZooKeeperMqttRouteManager(builder, configInfo) + .UseZooKeeperServiceSubscribeManager(builder, configInfo) + .UseZooKeeperCommandManager(builder, configInfo); } + /// + /// The GetConfigInfo + /// + /// The config + /// The private static ConfigInfo GetConfigInfo(ConfigInfo config) { ZookeeperOption option = null; @@ -222,5 +305,7 @@ private static ConfigInfo GetConfigInfo(ConfigInfo config) } return config; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/ZookeeperServiceCacheManager.cs b/src/Surging.Core/Surging.Core.Zookeeper/ZookeeperServiceCacheManager.cs index 67c95550c..651bbacd7 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/ZookeeperServiceCacheManager.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/ZookeeperServiceCacheManager.cs @@ -15,16 +15,61 @@ namespace Surging.Core.Zookeeper { + /// + /// Defines the + /// public class ZookeeperServiceCacheManager : ServiceCacheManagerBase, IDisposable - { + { + #region 字段 + + /// + /// Defines the _configInfo + /// private readonly ConfigInfo _configInfo; - private readonly ISerializer _serializer; + + /// + /// Defines the _logger + /// private readonly ILogger _logger; - private ServiceCache[] _serviceCaches; + + /// + /// Defines the _serializer + /// + private readonly ISerializer _serializer; + + /// + /// Defines the _serviceCacheFactory + /// private readonly IServiceCacheFactory _serviceCacheFactory; + + /// + /// Defines the _stringSerializer + /// private readonly ISerializer _stringSerializer; + + /// + /// Defines the _zookeeperClientProvider + /// private readonly IZookeeperClientProvider _zookeeperClientProvider; + /// + /// Defines the _serviceCaches + /// + private ServiceCache[] _serviceCaches; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The configInfo + /// The serializer + /// The stringSerializer + /// The serviceCacheFactory + /// The logger + /// The zookeeperClientProvider public ZookeeperServiceCacheManager(ConfigInfo configInfo, ISerializer serializer, ISerializer stringSerializer, IServiceCacheFactory serviceCacheFactory, ILogger logger, IZookeeperClientProvider zookeeperClientProvider) : base(stringSerializer) @@ -38,6 +83,14 @@ public ZookeeperServiceCacheManager(ConfigInfo configInfo, ISerializer s EnterCaches().Wait(); } + #endregion 构造函数 + + #region 方法 + + /// + /// The ClearAsync + /// + /// The public override async Task ClearAsync() { if (_logger.IsEnabled(LogLevel.Information)) @@ -78,23 +131,28 @@ public override async Task ClearAsync() } } + /// + /// The Dispose + /// public void Dispose() { } - - public override async Task SetCachesAsync(IEnumerable caches) - { - var serviceCaches = await GetCaches(caches.Select(p => p.CacheDescriptor.Id)); - await RemoveCachesAsync(caches); - await base.SetCachesAsync(caches); - } - + + /// + /// The GetCachesAsync + /// + /// The public override async Task> GetCachesAsync() { await EnterCaches(); return _serviceCaches; } + /// + /// The RemveAddressAsync + /// + /// The endpoints + /// The public override async Task RemveAddressAsync(IEnumerable endpoints) { var caches = await GetCachesAsync(); @@ -112,6 +170,23 @@ public override async Task RemveAddressAsync(IEnumerable endpoint await base.SetCachesAsync(caches); } + /// + /// The SetCachesAsync + /// + /// The caches + /// The + public override async Task SetCachesAsync(IEnumerable caches) + { + var serviceCaches = await GetCaches(caches.Select(p => p.CacheDescriptor.Id)); + await RemoveCachesAsync(caches); + await base.SetCachesAsync(caches); + } + + /// + /// The SetCachesAsync + /// + /// The cacheDescriptors + /// The public override async Task SetCachesAsync(IEnumerable cacheDescriptors) { if (_logger.IsEnabled(LogLevel.Information)) @@ -152,28 +227,99 @@ public override async Task SetCachesAsync(IEnumerable ca } } - private async Task GetCaches(IEnumerable childrens) + /// + /// The DataEquals + /// + /// The data1 + /// The data2 + /// The + private static bool DataEquals(IReadOnlyList data1, IReadOnlyList data2) { - var rootPath = _configInfo.CachePath; - if (!rootPath.EndsWith("/")) - rootPath += "/"; + if (data1.Count != data2.Count) + return false; + for (var i = 0; i < data1.Count; i++) + { + var b1 = data1[i]; + var b2 = data2[i]; + if (b1 != b2) + return false; + } + return true; + } - childrens = childrens.ToArray(); - var caches = new List(childrens.Count()); + /// + /// The ChildrenChange + /// + /// The oldChildrens + /// The newChildrens + /// The + private async Task ChildrenChange(string[] oldChildrens, string[] newChildrens) + { + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + _logger.LogDebug($"最新的节点信息:{string.Join(",", newChildrens)}"); - foreach (var children in childrens) + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + _logger.LogDebug($"旧的节点信息:{string.Join(",", oldChildrens)}"); + + //计算出已被删除的节点。 + var deletedChildrens = oldChildrens.Except(newChildrens).ToArray(); + //计算出新增的节点。 + var createdChildrens = newChildrens.Except(oldChildrens).ToArray(); + + if (_logger.IsEnabled(LogLevel.Debug)) + _logger.LogDebug($"需要被删除的服务缓存节点:{string.Join(",", deletedChildrens)}"); + if (_logger.IsEnabled(LogLevel.Debug)) + _logger.LogDebug($"需要被添加的服务缓存节点:{string.Join(",", createdChildrens)}"); + + //获取新增的缓存信息。 + var newCaches = (await GetCaches(createdChildrens)).ToArray(); + + var caches = _serviceCaches.ToArray(); + lock (_serviceCaches) { - if (_logger.IsEnabled(LogLevel.Debug)) - _logger.LogDebug($"准备从节点:{children}中获取缓存信息。"); + _serviceCaches = _serviceCaches + //删除无效的缓存节点。 + .Where(i => !deletedChildrens.Contains(i.CacheDescriptor.Id)) + //连接上新的缓存。 + .Concat(newCaches) + .ToArray(); + } + //需要删除的缓存集合。 + var deletedCaches = caches.Where(i => deletedChildrens.Contains(i.CacheDescriptor.Id)).ToArray(); + //触发删除事件。 + OnRemoved(deletedCaches.Select(cache => new ServiceCacheEventArgs(cache)).ToArray()); - var nodePath = $"{rootPath}{children}"; - var cache = await GetCache(nodePath); - if (cache != null) - caches.Add(cache); + //触发缓存被创建事件。 + OnCreated(newCaches.Select(cache => new ServiceCacheEventArgs(cache)).ToArray()); + + if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information)) + _logger.LogInformation("缓存数据更新成功。"); + } + + /// + /// The ConvertPaths + /// + /// The datas + /// The + private async Task ConvertPaths(string[] datas) + { + List paths = new List(); + foreach (var data in datas) + { + var result = await GetCacheData(data); + var serviceId = result?.CacheDescriptor.Id; + if (!string.IsNullOrEmpty(serviceId)) + paths.Add(serviceId); } - return caches.ToArray(); + return paths.ToArray(); } + /// + /// The CreateSubdirectory + /// + /// The zooKeeper + /// The path + /// The private async Task CreateSubdirectory((ManualResetEvent, ZooKeeper) zooKeeper, string path) { zooKeeper.Item1.WaitOne(); @@ -197,44 +343,10 @@ private async Task CreateSubdirectory((ManualResetEvent, ZooKeeper) zooKeeper, s } } - private async Task GetCache(string path) - { - ServiceCache result = null; - var zooKeeper = await GetZooKeeper(); - var watcher = new NodeMonitorWatcher(GetZooKeeper, path, - async (oldData, newData) => await NodeChange(oldData, newData)); - if (await zooKeeper.Item2.existsAsync(path) != null) - { - var data = (await zooKeeper.Item2.getDataAsync(path, watcher)).Data; - watcher.SetCurrentData(data); - result = await GetCache(data); - } - return result; - } - - #region 私有方法 - private async Task RemoveCachesAsync(IEnumerable caches) - { - var path = _configInfo.CachePath; - if (!path.EndsWith("/")) - path += "/"; - caches = caches.ToArray(); - if (_serviceCaches != null) - { - var zooKeepers = await _zookeeperClientProvider.GetZooKeepers(); - foreach (var zooKeeper in zooKeepers) - { - - var deletedCacheIds = caches.Select(i => i.CacheDescriptor.Id).ToArray(); - foreach (var deletedCacheId in deletedCacheIds) - { - var nodePath = $"{path}{deletedCacheId}"; - await zooKeeper.Item2.deleteAsync(nodePath); - } - } - } - } - + /// + /// The EnterCaches + /// + /// The private async Task EnterCaches() { if (_serviceCaches != null) @@ -259,20 +371,11 @@ private async Task EnterCaches() } } - private static bool DataEquals(IReadOnlyList data1, IReadOnlyList data2) - { - if (data1.Count != data2.Count) - return false; - for (var i = 0; i < data1.Count; i++) - { - var b1 = data1[i]; - var b2 = data2[i]; - if (b1 != b2) - return false; - } - return true; - } - + /// + /// The GetCache + /// + /// The data + /// The private async Task GetCache(byte[] data) { if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) @@ -285,6 +388,31 @@ private async Task GetCache(byte[] data) return (await _serviceCacheFactory.CreateServiceCachesAsync(new[] { descriptor })).First(); } + /// + /// The GetCache + /// + /// The path + /// The + private async Task GetCache(string path) + { + ServiceCache result = null; + var zooKeeper = await GetZooKeeper(); + var watcher = new NodeMonitorWatcher(GetZooKeeper, path, + async (oldData, newData) => await NodeChange(oldData, newData)); + if (await zooKeeper.Item2.existsAsync(path) != null) + { + var data = (await zooKeeper.Item2.getDataAsync(path, watcher)).Data; + watcher.SetCurrentData(data); + result = await GetCache(data); + } + return result; + } + + /// + /// The GetCacheData + /// + /// The data + /// The private async Task GetCacheData(string data) { if (data == null) @@ -294,6 +422,11 @@ private async Task GetCacheData(string data) return (await _serviceCacheFactory.CreateServiceCachesAsync(new[] { descriptor })).First(); } + /// + /// The GetCacheDatas + /// + /// The caches + /// The private async Task GetCacheDatas(string[] caches) { List serviceCaches = new List(); @@ -305,19 +438,49 @@ private async Task GetCacheDatas(string[] caches) return serviceCaches.ToArray(); } - private async Task ConvertPaths(string[] datas) + /// + /// The GetCaches + /// + /// The childrens + /// The + private async Task GetCaches(IEnumerable childrens) { - List paths = new List(); - foreach (var data in datas) + var rootPath = _configInfo.CachePath; + if (!rootPath.EndsWith("/")) + rootPath += "/"; + + childrens = childrens.ToArray(); + var caches = new List(childrens.Count()); + + foreach (var children in childrens) { - var result = await GetCacheData(data); - var serviceId = result?.CacheDescriptor.Id; - if (!string.IsNullOrEmpty(serviceId)) - paths.Add(serviceId); + if (_logger.IsEnabled(LogLevel.Debug)) + _logger.LogDebug($"准备从节点:{children}中获取缓存信息。"); + + var nodePath = $"{rootPath}{children}"; + var cache = await GetCache(nodePath); + if (cache != null) + caches.Add(cache); } - return paths.ToArray(); + return caches.ToArray(); } + /// + /// The GetZooKeeper + /// + /// The + private async ValueTask<(ManualResetEvent, ZooKeeper)> GetZooKeeper() + { + var zooKeeper = await _zookeeperClientProvider.GetZooKeeper(); + return zooKeeper; + } + + /// + /// The NodeChange + /// + /// The oldData + /// The newData + /// The private async Task NodeChange(byte[] oldData, byte[] newData) { if (DataEquals(oldData, newData)) @@ -335,61 +498,37 @@ private async Task NodeChange(byte[] oldData, byte[] newData) .Where(i => i.CacheDescriptor.Id != newCache.CacheDescriptor.Id) .Concat(new[] { newCache }).ToArray(); } - + //触发缓存变更事件。 - OnChanged(new ServiceCacheChangedEventArgs(newCache, oldCache)); + OnChanged(new ServiceCacheChangedEventArgs(newCache, oldCache)); } - private async Task ChildrenChange(string[] oldChildrens, string[] newChildrens) + /// + /// The RemoveCachesAsync + /// + /// The caches + /// The + private async Task RemoveCachesAsync(IEnumerable caches) { - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) - _logger.LogDebug($"最新的节点信息:{string.Join(",", newChildrens)}"); - - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) - _logger.LogDebug($"旧的节点信息:{string.Join(",", oldChildrens)}"); - - //计算出已被删除的节点。 - var deletedChildrens = oldChildrens.Except(newChildrens).ToArray(); - //计算出新增的节点。 - var createdChildrens = newChildrens.Except(oldChildrens).ToArray(); - - if (_logger.IsEnabled(LogLevel.Debug)) - _logger.LogDebug($"需要被删除的服务缓存节点:{string.Join(",", deletedChildrens)}"); - if (_logger.IsEnabled(LogLevel.Debug)) - _logger.LogDebug($"需要被添加的服务缓存节点:{string.Join(",", createdChildrens)}"); - - //获取新增的缓存信息。 - var newCaches = (await GetCaches(createdChildrens)).ToArray(); - - var caches = _serviceCaches.ToArray(); - lock (_serviceCaches) + var path = _configInfo.CachePath; + if (!path.EndsWith("/")) + path += "/"; + caches = caches.ToArray(); + if (_serviceCaches != null) { - _serviceCaches = _serviceCaches - //删除无效的缓存节点。 - .Where(i => !deletedChildrens.Contains(i.CacheDescriptor.Id)) - //连接上新的缓存。 - .Concat(newCaches) - .ToArray(); + var zooKeepers = await _zookeeperClientProvider.GetZooKeepers(); + foreach (var zooKeeper in zooKeepers) + { + var deletedCacheIds = caches.Select(i => i.CacheDescriptor.Id).ToArray(); + foreach (var deletedCacheId in deletedCacheIds) + { + var nodePath = $"{path}{deletedCacheId}"; + await zooKeeper.Item2.deleteAsync(nodePath); + } + } } - //需要删除的缓存集合。 - var deletedCaches = caches.Where(i => deletedChildrens.Contains(i.CacheDescriptor.Id)).ToArray(); - //触发删除事件。 - OnRemoved(deletedCaches.Select(cache => new ServiceCacheEventArgs(cache)).ToArray()); - - //触发缓存被创建事件。 - OnCreated(newCaches.Select(cache => new ServiceCacheEventArgs(cache)).ToArray()); - - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information)) - _logger.LogInformation("缓存数据更新成功。"); - } - - private async ValueTask<(ManualResetEvent, ZooKeeper)> GetZooKeeper() - { - var zooKeeper = await _zookeeperClientProvider.GetZooKeeper(); - return zooKeeper; } - #endregion + #endregion 方法 } -} - +} \ No newline at end of file diff --git a/src/Surging.Core/Surging.Core.Zookeeper/ZookeeperServiceCommandManager.cs b/src/Surging.Core/Surging.Core.Zookeeper/ZookeeperServiceCommandManager.cs index 190cd9772..11375fdb8 100644 --- a/src/Surging.Core/Surging.Core.Zookeeper/ZookeeperServiceCommandManager.cs +++ b/src/Surging.Core/Surging.Core.Zookeeper/ZookeeperServiceCommandManager.cs @@ -18,15 +18,57 @@ namespace Surging.Core.Zookeeper { + /// + /// Defines the + /// public class ZookeeperServiceCommandManager : ServiceCommandManagerBase, IDisposable - { + { + #region 字段 + + /// + /// Defines the _configInfo + /// private readonly ConfigInfo _configInfo; - private readonly ISerializer _serializer; + + /// + /// Defines the _logger + /// private readonly ILogger _logger; - private ServiceCommandDescriptor[] _serviceCommands; - private readonly IServiceRouteManager _serviceRouteManager; + + /// + /// Defines the _serializer + /// + private readonly ISerializer _serializer; + + /// + /// Defines the _serviceRouteManager + /// + private readonly IServiceRouteManager _serviceRouteManager; + + /// + /// Defines the _zookeeperClientProvider + /// private readonly IZookeeperClientProvider _zookeeperClientProvider; + /// + /// Defines the _serviceCommands + /// + private ServiceCommandDescriptor[] _serviceCommands; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The configInfo + /// The serializer + /// The stringSerializer + /// The serviceRouteManager + /// The serviceEntryManager + /// The logger + /// The zookeeperClientProvider public ZookeeperServiceCommandManager(ConfigInfo configInfo, ISerializer serializer, ISerializer stringSerializer, IServiceRouteManager serviceRouteManager, IServiceEntryManager serviceEntryManager, ILogger logger, IZookeeperClientProvider zookeeperClientProvider) : base(stringSerializer, serviceEntryManager) @@ -36,19 +78,61 @@ public ZookeeperServiceCommandManager(ConfigInfo configInfo, ISerializer _serviceRouteManager = serviceRouteManager; _logger = logger; _zookeeperClientProvider = zookeeperClientProvider; - EnterServiceCommands().Wait(); + EnterServiceCommands().Wait(); _serviceRouteManager.Removed += ServiceRouteManager_Removed; } + #endregion 构造函数 + + #region 方法 /// - /// 获取所有可用的服务命令信息。 + /// The ChildrenChange /// - /// 服务命令集合。 - public override async Task> GetServiceCommandsAsync() + /// The oldChildrens + /// The newChildrens + /// The + public async Task ChildrenChange(string[] oldChildrens, string[] newChildrens) { - await EnterServiceCommands(); - return _serviceCommands; + if (_logger.IsEnabled(LogLevel.Debug)) + _logger.LogDebug($"最新的节点信息:{string.Join(",", newChildrens)}"); + + if (_logger.IsEnabled(LogLevel.Debug)) + _logger.LogDebug($"旧的节点信息:{string.Join(",", oldChildrens)}"); + + //计算出已被删除的节点。 + var deletedChildrens = oldChildrens.Except(newChildrens).ToArray(); + //计算出新增的节点。 + var createdChildrens = newChildrens.Except(oldChildrens).ToArray(); + + if (_logger.IsEnabled(LogLevel.Debug)) + _logger.LogDebug($"需要被删除的服务命令节点:{string.Join(",", deletedChildrens)}"); + if (_logger.IsEnabled(LogLevel.Debug)) + _logger.LogDebug($"需要被添加的服务命令节点:{string.Join(",", createdChildrens)}"); + + //获取新增的服务命令信息。 + var newCommands = (await GetServiceCommands(createdChildrens)).ToArray(); + + var routes = _serviceCommands.ToArray(); + lock (_serviceCommands) + { + _serviceCommands = _serviceCommands + //删除无效的节点服务命令。 + .Where(i => !deletedChildrens.Contains(i.ServiceId)) + //连接上新的服务命令。 + .Concat(newCommands) + .ToArray(); + } + //需要删除的服务命令集合。 + var deletedRoutes = routes.Where(i => deletedChildrens.Contains(i.ServiceId)).ToArray(); + //触发删除事件。 + OnRemoved(deletedRoutes.Select(command => new ServiceCommandEventArgs(command)).ToArray()); + + //触发服务命令被创建事件。 + OnCreated(newCommands.Select(command => new ServiceCommandEventArgs(command)).ToArray()); + + if (_logger.IsEnabled(LogLevel.Information)) + _logger.LogInformation("服务命令数据更新成功。"); } /// @@ -95,10 +179,75 @@ public override async Task ClearAsync() } } + /// + /// The Dispose + /// + public void Dispose() + { + } + + /// + /// 获取所有可用的服务命令信息。 + /// + /// 服务命令集合。 + public override async Task> GetServiceCommandsAsync() + { + await EnterServiceCommands(); + return _serviceCommands; + } + + /// + /// The NodeChange + /// + /// The oldData + /// The newData + public void NodeChange(byte[] oldData, byte[] newData) + { + if (DataEquals(oldData, newData)) + return; + + var newCommand = GetServiceCommand(newData); + //得到旧的服务命令。 + var oldCommand = _serviceCommands.FirstOrDefault(i => i.ServiceId == newCommand.ServiceId); + + lock (_serviceCommands) + { + //删除旧服务命令,并添加上新的服务命令。 + _serviceCommands = + _serviceCommands + .Where(i => i.ServiceId != newCommand.ServiceId) + .Concat(new[] { newCommand }).ToArray(); + } + //触发服务命令变更事件。 + OnChanged(new ServiceCommandChangedEventArgs(newCommand, oldCommand)); + } + + /// + /// The NodeChange + /// + /// The newCommand + public void NodeChange(ServiceCommandDescriptor newCommand) + { + //得到旧的服务命令。 + var oldCommand = _serviceCommands.FirstOrDefault(i => i.ServiceId == newCommand.ServiceId); + + lock (_serviceCommands) + { + //删除旧服务命令,并添加上新的服务命令。 + _serviceCommands = + _serviceCommands + .Where(i => i.ServiceId != newCommand.ServiceId) + .Concat(new[] { newCommand }).ToArray(); + } + + //触发服务命令变更事件。 + OnChanged(new ServiceCommandChangedEventArgs(newCommand, oldCommand)); + } + /// /// 设置服务命令。 /// - /// 服务命令集合。 + /// The serviceCommand /// 一个任务。 public override async Task SetServiceCommandsAsync(IEnumerable serviceCommand) { @@ -142,32 +291,46 @@ public override async Task SetServiceCommandsAsync(IEnumerable + /// The InitServiceCommandsAsync + /// + /// The serviceCommands + /// The protected override async Task InitServiceCommandsAsync(IEnumerable serviceCommands) { var commands = await GetServiceCommands(serviceCommands.Select(p => p.ServiceId)); if (commands.Count() == 0 || _configInfo.ReloadOnChange) - { + { await SetServiceCommandsAsync(serviceCommands); } } - private void ServiceRouteManager_Removed(object sender, ServiceRouteEventArgs e) + /// + /// The DataEquals + /// + /// The data1 + /// The data2 + /// The + private static bool DataEquals(IReadOnlyList data1, IReadOnlyList data2) { - var path = _configInfo.CommandPath; - if (!path.EndsWith("/")) - path += "/"; - var zooKeepers = _zookeeperClientProvider.GetZooKeepers().Result; - foreach (var zooKeeper in zooKeepers) + if (data1.Count != data2.Count) + return false; + for (var i = 0; i < data1.Count; i++) { - var nodePath = $"{path}{e.Route.ServiceDescriptor.Id}"; - if (zooKeeper.Item2.existsAsync(nodePath).Result != null) - { - zooKeeper.Item2.deleteAsync(nodePath).Wait(); - } + var b1 = data1[i]; + var b2 = data2[i]; + if (b1 != b2) + return false; } + return true; } - + /// + /// The CreateSubdirectory + /// + /// The zooKeeper + /// The path + /// The private async Task CreateSubdirectory((ManualResetEvent, ZooKeeper) zooKeeper, string path) { zooKeeper.Item1.WaitOne(); @@ -191,6 +354,39 @@ private async Task CreateSubdirectory((ManualResetEvent, ZooKeeper) zooKeeper, s } } + /// + /// The EnterServiceCommands + /// + /// The + private async Task EnterServiceCommands() + { + if (_serviceCommands != null) + return; + var zooKeeper = await GetZooKeeper(); + zooKeeper.Item1.WaitOne(); + var watcher = new ChildrenMonitorWatcher(GetZooKeeper, _configInfo.CommandPath, + async (oldChildrens, newChildrens) => await ChildrenChange(oldChildrens, newChildrens)); + if (await zooKeeper.Item2.existsAsync(_configInfo.CommandPath, watcher) != null) + { + var result = await zooKeeper.Item2.getChildrenAsync(_configInfo.CommandPath, watcher); + var childrens = result.Children.ToArray(); + + watcher.SetCurrentData(childrens); + _serviceCommands = await GetServiceCommands(childrens); + } + else + { + if (_logger.IsEnabled(LogLevel.Warning)) + _logger.LogWarning($"无法获取服务命令信息,因为节点:{_configInfo.CommandPath},不存在。"); + _serviceCommands = new ServiceCommandDescriptor[0]; + } + } + + /// + /// The GetServiceCommand + /// + /// The data + /// The private ServiceCommandDescriptor GetServiceCommand(byte[] data) { if (_logger.IsEnabled(LogLevel.Debug)) @@ -203,9 +399,14 @@ private ServiceCommandDescriptor GetServiceCommand(byte[] data) return descriptor; } + /// + /// The GetServiceCommand + /// + /// The path + /// The private async Task GetServiceCommand(string path) { - ServiceCommandDescriptor result = null; + ServiceCommandDescriptor result = null; var zooKeeper = await GetZooKeeper(); var watcher = new NodeMonitorWatcher(GetZooKeeper, path, (oldData, newData) => NodeChange(oldData, newData)); @@ -218,6 +419,11 @@ private async Task GetServiceCommand(string path) return result; } + /// + /// The GetServiceCommands + /// + /// The childrens + /// The private async Task GetServiceCommands(IEnumerable childrens) { var rootPath = _configInfo.CommandPath; @@ -240,137 +446,37 @@ private async Task GetServiceCommands(IEnumerable await ChildrenChange(oldChildrens, newChildrens)); - if (await zooKeeper.Item2.existsAsync(_configInfo.CommandPath, watcher) != null) - { - var result = await zooKeeper.Item2.getChildrenAsync(_configInfo.CommandPath, watcher); - var childrens = result.Children.ToArray(); - - watcher.SetCurrentData(childrens); - _serviceCommands = await GetServiceCommands(childrens); - } - else - { - if (_logger.IsEnabled(LogLevel.Warning)) - _logger.LogWarning($"无法获取服务命令信息,因为节点:{_configInfo.CommandPath},不存在。"); - _serviceCommands = new ServiceCommandDescriptor[0]; - } - } - - private static bool DataEquals(IReadOnlyList data1, IReadOnlyList data2) - { - if (data1.Count != data2.Count) - return false; - for (var i = 0; i < data1.Count; i++) - { - var b1 = data1[i]; - var b2 = data2[i]; - if (b1 != b2) - return false; - } - return true; - } - - public void NodeChange(ServiceCommandDescriptor newCommand) - { - //得到旧的服务命令。 - var oldCommand = _serviceCommands.FirstOrDefault(i => i.ServiceId == newCommand.ServiceId); - - lock (_serviceCommands) - { - //删除旧服务命令,并添加上新的服务命令。 - _serviceCommands = - _serviceCommands - .Where(i => i.ServiceId != newCommand.ServiceId) - .Concat(new[] { newCommand }).ToArray(); - } - - //触发服务命令变更事件。 - OnChanged(new ServiceCommandChangedEventArgs(newCommand, oldCommand)); - } - - public void NodeChange(byte[] oldData, byte[] newData) + /// + /// The GetZooKeeper + /// + /// The + private async ValueTask<(ManualResetEvent, ZooKeeper)> GetZooKeeper() { - if (DataEquals(oldData, newData)) - return; - - var newCommand = GetServiceCommand(newData); - //得到旧的服务命令。 - var oldCommand = _serviceCommands.FirstOrDefault(i => i.ServiceId == newCommand.ServiceId); - - lock (_serviceCommands) - { - //删除旧服务命令,并添加上新的服务命令。 - _serviceCommands = - _serviceCommands - .Where(i => i.ServiceId != newCommand.ServiceId) - .Concat(new[] { newCommand }).ToArray(); - } - //触发服务命令变更事件。 - OnChanged(new ServiceCommandChangedEventArgs(newCommand, oldCommand)); + var zooKeeper = await _zookeeperClientProvider.GetZooKeeper(); + return zooKeeper; } - public async Task ChildrenChange(string[] oldChildrens, string[] newChildrens) + /// + /// The ServiceRouteManager_Removed + /// + /// The sender + /// The e + private void ServiceRouteManager_Removed(object sender, ServiceRouteEventArgs e) { - if (_logger.IsEnabled(LogLevel.Debug)) - _logger.LogDebug($"最新的节点信息:{string.Join(",", newChildrens)}"); - - if (_logger.IsEnabled(LogLevel.Debug)) - _logger.LogDebug($"旧的节点信息:{string.Join(",", oldChildrens)}"); - - //计算出已被删除的节点。 - var deletedChildrens = oldChildrens.Except(newChildrens).ToArray(); - //计算出新增的节点。 - var createdChildrens = newChildrens.Except(oldChildrens).ToArray(); - - if (_logger.IsEnabled(LogLevel.Debug)) - _logger.LogDebug($"需要被删除的服务命令节点:{string.Join(",", deletedChildrens)}"); - if (_logger.IsEnabled(LogLevel.Debug)) - _logger.LogDebug($"需要被添加的服务命令节点:{string.Join(",", createdChildrens)}"); - - //获取新增的服务命令信息。 - var newCommands = (await GetServiceCommands(createdChildrens)).ToArray(); - - var routes = _serviceCommands.ToArray(); - lock (_serviceCommands) + var path = _configInfo.CommandPath; + if (!path.EndsWith("/")) + path += "/"; + var zooKeepers = _zookeeperClientProvider.GetZooKeepers().Result; + foreach (var zooKeeper in zooKeepers) { - _serviceCommands = _serviceCommands - //删除无效的节点服务命令。 - .Where(i => !deletedChildrens.Contains(i.ServiceId)) - //连接上新的服务命令。 - .Concat(newCommands) - .ToArray(); + var nodePath = $"{path}{e.Route.ServiceDescriptor.Id}"; + if (zooKeeper.Item2.existsAsync(nodePath).Result != null) + { + zooKeeper.Item2.deleteAsync(nodePath).Wait(); + } } - //需要删除的服务命令集合。 - var deletedRoutes = routes.Where(i => deletedChildrens.Contains(i.ServiceId)).ToArray(); - //触发删除事件。 - OnRemoved(deletedRoutes.Select(command => new ServiceCommandEventArgs(command)).ToArray()); - - //触发服务命令被创建事件。 - OnCreated(newCommands.Select(command => new ServiceCommandEventArgs(command)).ToArray()); - - if (_logger.IsEnabled(LogLevel.Information)) - _logger.LogInformation("服务命令数据更新成功。"); - } - - - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - public void Dispose() - { - } - - private async ValueTask<(ManualResetEvent, ZooKeeper)> GetZooKeeper() - { - var zooKeeper = await _zookeeperClientProvider.GetZooKeeper(); - return zooKeeper; } + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.IModuleServices/Surging.IModuleServices.Common/IChatService.cs b/src/Surging.IModuleServices/Surging.IModuleServices.Common/IChatService.cs index 9d9d57171..831045308 100644 --- a/src/Surging.IModuleServices/Surging.IModuleServices.Common/IChatService.cs +++ b/src/Surging.IModuleServices/Surging.IModuleServices.Common/IChatService.cs @@ -11,11 +11,28 @@ namespace Surging.IModuleServices.Common { + #region 接口 + + /// + /// Defines the + /// [ServiceBundle("Api/{Service}")] - [BehaviorContract(IgnoreExtensions =true)] - public interface IChatService: IServiceKey + [BehaviorContract(IgnoreExtensions = true)] + public interface IChatService : IServiceKey { - [Command( ShuntStrategy=AddressSelectorMode.HashAlgorithm)] - Task SendMessage(string name,string data); + #region 方法 + + /// + /// The SendMessage + /// + /// The name + /// The data + /// The + [Command(ShuntStrategy = AddressSelectorMode.HashAlgorithm)] + Task SendMessage(string name, string data); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.IModuleServices/Surging.IModuleServices.Common/IControllerService.cs b/src/Surging.IModuleServices/Surging.IModuleServices.Common/IControllerService.cs index eb0bbdb74..699ed51e8 100644 --- a/src/Surging.IModuleServices/Surging.IModuleServices.Common/IControllerService.cs +++ b/src/Surging.IModuleServices/Surging.IModuleServices.Common/IControllerService.cs @@ -11,13 +11,35 @@ namespace Surging.IModuleServices.Common { - [ServiceBundle("Device/{Service}")] + #region 接口 + + /// + /// Defines the + /// + [ServiceBundle("Device/{Service}")] public interface IControllerService : IServiceKey - { - [Command(ShuntStrategy = AddressSelectorMode.HashAlgorithm)] - Task Publish(string deviceId, WillMessage message); + { + #region 方法 + /// + /// The IsOnline + /// + /// The deviceId + /// The [Command(ShuntStrategy = AddressSelectorMode.HashAlgorithm)] Task IsOnline(string deviceId); + + /// + /// The Publish + /// + /// The deviceId + /// The message + /// The + [Command(ShuntStrategy = AddressSelectorMode.HashAlgorithm)] + Task Publish(string deviceId, WillMessage message); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.IModuleServices/Surging.IModuleServices.Common/IDnsService.cs b/src/Surging.IModuleServices/Surging.IModuleServices.Common/IDnsService.cs index 817bd48fa..a6e6223ad 100644 --- a/src/Surging.IModuleServices/Surging.IModuleServices.Common/IDnsService.cs +++ b/src/Surging.IModuleServices/Surging.IModuleServices.Common/IDnsService.cs @@ -6,8 +6,15 @@ namespace Surging.IModuleServices.Common { + #region 接口 + + /// + /// Defines the + /// [ServiceBundle("Dns/{Service}")] - public interface IDnsService : IServiceKey + public interface IDnsService : IServiceKey { } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.IModuleServices/Surging.IModuleServices.Common/IRoteMangeService.cs b/src/Surging.IModuleServices/Surging.IModuleServices.Common/IRoteMangeService.cs index 4cfa0f10d..3dbfd4bd1 100644 --- a/src/Surging.IModuleServices/Surging.IModuleServices.Common/IRoteMangeService.cs +++ b/src/Surging.IModuleServices/Surging.IModuleServices.Common/IRoteMangeService.cs @@ -7,11 +7,32 @@ namespace Surging.IModuleServices.Common { + #region 接口 + + /// + /// Defines the + /// [ServiceBundle("Api/{Service}")] public interface IRoteMangeService { + #region 方法 + + /// + /// The GetServiceById + /// + /// The serviceId + /// The Task GetServiceById(string serviceId); + /// + /// The SetRote + /// + /// The model + /// The Task SetRote(RoteModel model); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.IModuleServices/Surging.IModuleServices.Common/IUserService.cs b/src/Surging.IModuleServices/Surging.IModuleServices.Common/IUserService.cs index 890252646..5217f1824 100644 --- a/src/Surging.IModuleServices/Surging.IModuleServices.Common/IUserService.cs +++ b/src/Surging.IModuleServices/Surging.IModuleServices.Common/IUserService.cs @@ -17,12 +17,19 @@ namespace Surging.IModuleServices.Common { + #region 接口 + + /// + /// Defines the + /// [ServiceBundle("api/{Service}/{Method}")] //[ServiceBundle("api/{Service}")] //[ServiceBundle("api/{Service}/{Method}/test")] //[ServiceBundle("api/{Service}/{Method}/test",false)] - public interface IUserService: IServiceKey + public interface IUserService : IServiceKey { + #region 方法 + /// /// 用戶授权 /// @@ -31,12 +38,13 @@ public interface IUserService: IServiceKey Task Authentication(AuthenticationRequestData requestData); /// - /// 获取用户姓名 + /// 测试下载文件 /// - /// 用户编号 + /// 文件名 + /// Content-Type /// - [ServiceRoute("{id}")] - Task GetUserName(int id); + [ServiceRoute("{fileName}/{contentType}")] + Task DownFile(string fileName, string contentType); /// /// 判断是否存在 @@ -44,37 +52,44 @@ public interface IUserService: IServiceKey /// 用户编号 /// [ServiceRoute("{id}")] - // [ServiceBundle("api/{Service}/{id}", false)] + // [ServiceBundle("api/{Service}/{id}", false)] Task Exists(int id); /// - /// 报错用户 + /// 测试List参数调用 + /// + /// 用户列表 + /// 返回是否成功 + Task Get(List users); + + /// + /// The GetAllThings /// - /// 请求参数 /// - [Authorization(AuthType = AuthorizationType.JWT)] - - Task Save(IdentityUser requestData); + Task> GetAllThings(); /// - /// 根据用户名获取用户ID + /// 测试无参调用,返回泛型结果 /// - /// 用户名 /// - [Authorization(AuthType = AuthorizationType.JWT)] - [Command(Strategy = StrategyType.Injection, ShuntStrategy = AddressSelectorMode.HashAlgorithm, ExecutionTimeoutInMilliseconds = 1500, BreakerRequestVolumeThreshold = 3, Injection = @"return 1;", RequestCacheEnabled = false)] - [InterceptMethod(CachingMethod.Get, Key = "GetUser", CacheSectionType = SectionType.ddlCache, L2Key= "GetUserId_{0}", EnableL2Cache = true, Mode = CacheTargetType.Redis, Time = 480)] - [ServiceRoute("{userName}")] - Task GetUserId(string userName); + [Command(Strategy = StrategyType.Injection, ShuntStrategy = AddressSelectorMode.HashAlgorithm, ExecutionTimeoutInMilliseconds = 2500, BreakerRequestVolumeThreshold = 3, Injection = @"return null;", RequestCacheEnabled = false)] + Task> GetApiResult(); - Task Try(); + /// + /// 测试无参数调用 + /// + /// 返回是否成功 + [Command(Strategy = StrategyType.Injection, ShuntStrategy = AddressSelectorMode.Polling, ExecutionTimeoutInMilliseconds = 1500, BreakerRequestVolumeThreshold = 3, Injection = @"return false;", FallBackName = "GetDictionaryMethodBreaker", RequestCacheEnabled = false)] + [InterceptMethod(CachingMethod.Get, Key = "GetDictionary", CacheSectionType = SectionType.ddlCache, Mode = CacheTargetType.Redis, Time = 480)] + Task GetDictionary(); /// - /// 获取用户最后次sign时间 + /// 测试参数list参数 /// - /// 用户ID + /// list 类型参数 /// - Task GetUserLastSignInTime(int id); + [ServiceMetadata("IsOverload", true)] + Task GetUser(List idList); /// /// 获取用户 @@ -91,39 +106,38 @@ public interface IUserService: IServiceKey Task GetUser(UserModel user); /// - /// 更新用户 + /// 测序guid /// - /// 用户ID - /// 用户模型 + /// /// - [Authorization(AuthType = AuthorizationType.JWT)] - [Command(Strategy = StrategyType.FallBack,FallBackName = "UpdateFallBackName", RequestCacheEnabled = true, InjectionNamespaces = new string[] { "Surging.IModuleServices.Common" })] - [InterceptMethod(CachingMethod.Remove, "GetUser_id_{0}", "GetUserName_name_{0}", CacheSectionType = SectionType.ddlCache, Mode = CacheTargetType.Redis)] - Task Update(int id, UserModel model); + [ServiceRoute("{id}")] + Task GetUserById(Guid id); /// - /// 测试List参数调用 + /// 根据用户名获取用户ID /// - /// 用户列表 - /// 返回是否成功 - Task Get(List users); + /// 用户名 + /// + [Authorization(AuthType = AuthorizationType.JWT)] + [Command(Strategy = StrategyType.Injection, ShuntStrategy = AddressSelectorMode.HashAlgorithm, ExecutionTimeoutInMilliseconds = 1500, BreakerRequestVolumeThreshold = 3, Injection = @"return 1;", RequestCacheEnabled = false)] + [InterceptMethod(CachingMethod.Get, Key = "GetUser", CacheSectionType = SectionType.ddlCache, L2Key = "GetUserId_{0}", EnableL2Cache = true, Mode = CacheTargetType.Redis, Time = 480)] + [ServiceRoute("{userName}")] + Task GetUserId(string userName); /// - /// 测试无参数调用 + /// 获取用户最后次sign时间 /// - /// 返回是否成功 - [Command(Strategy = StrategyType.Injection,ShuntStrategy = AddressSelectorMode.Polling, ExecutionTimeoutInMilliseconds = 1500, BreakerRequestVolumeThreshold = 3, Injection = @"return false;",FallBackName = "GetDictionaryMethodBreaker", RequestCacheEnabled = false)] - [InterceptMethod(CachingMethod.Get, Key = "GetDictionary", CacheSectionType = SectionType.ddlCache, Mode = CacheTargetType.Redis, Time = 480)] - Task GetDictionary(); - - /// - /// 测试异常 - /// - /// - Task TryThrowException(); + /// 用户ID + /// + Task GetUserLastSignInTime(int id); - [ServiceRoute("{sex}")] - Task SetSex(Sex sex); + /// + /// 获取用户姓名 + /// + /// 用户编号 + /// + [ServiceRoute("{id}")] + Task GetUserName(int id); /// /// 测试基于eventbus 推送消息 @@ -133,48 +147,53 @@ public interface IUserService: IServiceKey Task PublishThroughEventBusAsync(IntegrationEvent evt1); /// - /// 测试无参调用,返回泛型结果 + /// 报错用户 /// + /// 请求参数 /// - [Command(Strategy = StrategyType.Injection, ShuntStrategy = AddressSelectorMode.HashAlgorithm, ExecutionTimeoutInMilliseconds = 2500, BreakerRequestVolumeThreshold = 3, Injection = @"return null;", RequestCacheEnabled = false)] - Task> GetApiResult(); + [Authorization(AuthType = AuthorizationType.JWT)] + Task Save(IdentityUser requestData); /// - /// 测试参数list参数 + /// The SetSex /// - /// list 类型参数 - /// - [ServiceMetadata("IsOverload", true)] - Task GetUser(List idList); + /// The sex + /// The + [ServiceRoute("{sex}")] + Task SetSex(Sex sex); /// - /// 测序guid + /// The Try /// - /// - /// - [ServiceRoute("{id}")] - Task GetUserById(Guid id); + /// The + Task Try(); /// - /// 测试上传文件 + /// 测试异常 /// - /// HttpFormCollection 类型参数 /// - Task UploadFile(HttpFormCollection form); + Task TryThrowException(); /// - /// 测试下载文件 + /// 更新用户 /// - /// 文件名 - /// Content-Type + /// 用户ID + /// 用户模型 /// - [ServiceRoute("{fileName}/{contentType}")] - Task DownFile(string fileName, string contentType); + [Authorization(AuthType = AuthorizationType.JWT)] + [Command(Strategy = StrategyType.FallBack, FallBackName = "UpdateFallBackName", RequestCacheEnabled = true, InjectionNamespaces = new string[] { "Surging.IModuleServices.Common" })] + [InterceptMethod(CachingMethod.Remove, "GetUser_id_{0}", "GetUserName_name_{0}", CacheSectionType = SectionType.ddlCache, Mode = CacheTargetType.Redis)] + Task Update(int id, UserModel model); /// - /// + /// 测试上传文件 /// + /// HttpFormCollection 类型参数 /// - Task> GetAllThings(); + Task UploadFile(HttpFormCollection form); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.IModuleServices/Surging.IModuleServices.Common/IntercepteModule.cs b/src/Surging.IModuleServices/Surging.IModuleServices.Common/IntercepteModule.cs index c2ae9f5cf..c62b0339c 100644 --- a/src/Surging.IModuleServices/Surging.IModuleServices.Common/IntercepteModule.cs +++ b/src/Surging.IModuleServices/Surging.IModuleServices.Common/IntercepteModule.cs @@ -8,8 +8,17 @@ namespace Surging.IModuleServices.Common { + /// + /// Defines the + /// public class IntercepteModule : SystemModule { + #region 方法 + + /// + /// The Initialize + /// + /// The context public override void Initialize(AppModuleContext context) { base.Initialize(context); @@ -25,6 +34,7 @@ protected override void RegisterBuilder(ContainerBuilderWrapper builder) //builder.AddClientIntercepted(typeof(CacheProviderInterceptor)); builder.AddClientIntercepted(typeof(LogProviderInterceptor)); } - } -} + #endregion 方法 + } +} \ No newline at end of file diff --git a/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/AuthenticationRequestData.cs b/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/AuthenticationRequestData.cs index f5814e94f..46a51b654 100644 --- a/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/AuthenticationRequestData.cs +++ b/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/AuthenticationRequestData.cs @@ -5,13 +5,26 @@ namespace Surging.IModuleServices.Common.Models { + /// + /// Defines the + /// [ProtoContract] public class AuthenticationRequestData { - [ProtoMember(1)] - public string UserName { get; set; } + #region 属性 + /// + /// Gets or sets the Password + /// [ProtoMember(2)] public string Password { get; set; } + + /// + /// Gets or sets the UserName + /// + [ProtoMember(1)] + public string UserName { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/BaseModel.cs b/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/BaseModel.cs index c4099f48a..113b1b503 100644 --- a/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/BaseModel.cs +++ b/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/BaseModel.cs @@ -6,10 +6,20 @@ namespace Surging.IModuleServices.Common.Models { + /// + /// Defines the + /// [ProtoContract] public class BaseModel { + #region 属性 + + /// + /// Gets the Id + /// [ProtoMember(1)] public Guid Id => Guid.NewGuid(); + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/Events/LogoutEvent.cs b/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/Events/LogoutEvent.cs index db3da6626..6943a44f9 100644 --- a/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/Events/LogoutEvent.cs +++ b/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/Events/LogoutEvent.cs @@ -5,13 +5,28 @@ namespace Surging.IModuleServices.Common.Models.Events { - public class LogoutEvent : IntegrationEvent + /// + /// Defines the + /// + public class LogoutEvent : IntegrationEvent { - public string UserId { get; set; } + #region 属性 + + /// + /// Gets or sets the Age + /// + public string Age { get; set; } + /// + /// Gets or sets the Name + /// public string Name { get; set; } - public string Age { get; set; } + /// + /// Gets or sets the UserId + /// + public string UserId { get; set; } + + #endregion 属性 } -} - \ No newline at end of file +} \ No newline at end of file diff --git a/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/Events/UserEvent.cs b/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/Events/UserEvent.cs index 5f4dd06f8..a4b0890d2 100644 --- a/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/Events/UserEvent.cs +++ b/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/Events/UserEvent.cs @@ -5,12 +5,28 @@ namespace Surging.IModuleServices.Common.Models.Events { + /// + /// Defines the + /// public class UserEvent : IntegrationEvent { - public int UserId { get; set; } + #region 属性 + + /// + /// Gets or sets the Age + /// + public int Age { get; set; } + /// + /// Gets or sets the Name + /// public string Name { get; set; } - public int Age { get; set; } + /// + /// Gets or sets the UserId + /// + public int UserId { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/IdentityUser.cs b/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/IdentityUser.cs index a2b80327b..8c360dbb3 100644 --- a/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/IdentityUser.cs +++ b/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/IdentityUser.cs @@ -6,10 +6,20 @@ namespace Surging.IModuleServices.Common.Models { + /// + /// Defines the + /// [ProtoContract] - public class IdentityUser:RequestData + public class IdentityUser : RequestData { + #region 属性 + + /// + /// Gets or sets the RoleId + /// [ProtoMember(1)] public string RoleId { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/RoteModel.cs b/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/RoteModel.cs index 6921bafeb..04b7da2d0 100644 --- a/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/RoteModel.cs +++ b/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/RoteModel.cs @@ -5,10 +5,20 @@ namespace Surging.IModuleServices.Common.Models { + /// + /// Defines the + /// [ProtoContract] public class RoteModel { + #region 属性 + + /// + /// Gets or sets the ServiceId + /// [ProtoMember(1)] public string ServiceId { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/UserMode.cs b/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/UserMode.cs index 0ace5d344..f9994b280 100644 --- a/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/UserMode.cs +++ b/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/UserMode.cs @@ -4,8 +4,23 @@ namespace Surging.IModuleServices.Common.Models { + #region 枚举 + + /// + /// Defines the Sex + /// public enum Sex { - Man, Woman + /// + /// Defines the Man + /// + Man, + + /// + /// Defines the Woman + /// + Woman } -} + + #endregion 枚举 +} \ No newline at end of file diff --git a/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/UserModel.cs b/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/UserModel.cs index daca0a447..cc362fcd7 100644 --- a/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/UserModel.cs +++ b/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/UserModel.cs @@ -4,22 +4,39 @@ namespace Surging.IModuleServices.Common.Models { + /// + /// Defines the + /// [ProtoContract] public class UserModel { + #region 属性 - [ProtoMember(1)] - [CacheKey(1)] - public int UserId { get; set; } + /// + /// Gets or sets the Age + /// + [ProtoMember(3)] + public int Age { get; set; } + /// + /// Gets or sets the Name + /// [ProtoMember(2)] public string Name { get; set; } - [ProtoMember(3)] - public int Age { get; set; } - + /// + /// Gets or sets the Sex + /// [ProtoMember(4)] public Sex Sex { get; set; } + /// + /// Gets or sets the UserId + /// + [ProtoMember(1)] + [CacheKey(1)] + public int UserId { get; set; } + + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/WillMessage.cs b/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/WillMessage.cs index 3aed9f303..d55bcfef9 100644 --- a/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/WillMessage.cs +++ b/src/Surging.IModuleServices/Surging.IModuleServices.Common/Models/WillMessage.cs @@ -4,15 +4,33 @@ namespace Surging.IModuleServices.Common.Models { + /// + /// Defines the + /// public class WillMessage { - public string Topic { get; set; } + #region 属性 + /// + /// Gets or sets the Message + /// public string Message { get; set; } + /// + /// Gets or sets the Qos + /// + public int Qos { get; set; } + /// + /// Gets or sets the Topic + /// + public string Topic { get; set; } + + /// + /// Gets or sets a value indicating whether WillRetain + /// public bool WillRetain { get; set; } - public int Qos { get; set; } + #endregion 属性 } -} +} \ No newline at end of file diff --git a/src/Surging.IModuleServices/Surging.IModuleServices.Manger/IManagerService.cs b/src/Surging.IModuleServices/Surging.IModuleServices.Manger/IManagerService.cs index ab0698601..16f5fe84f 100644 --- a/src/Surging.IModuleServices/Surging.IModuleServices.Manger/IManagerService.cs +++ b/src/Surging.IModuleServices/Surging.IModuleServices.Manger/IManagerService.cs @@ -1,6 +1,4 @@ - - -using Surging.Core.CPlatform.Ioc; +using Surging.Core.CPlatform.Ioc; using Surging.Core.CPlatform.Runtime.Client.Address.Resolvers.Implementation.Selectors.Implementation; using Surging.Core.CPlatform.Runtime.Server.Implementation.ServiceDiscovery.Attributes; using Surging.Core.CPlatform.Support; @@ -9,11 +7,26 @@ namespace Surging.IModuleServices.User { + #region 接口 + /// + /// Defines the + /// [ServiceBundle("api/{Service}")] public interface IManagerService : IServiceKey { + #region 方法 + + /// + /// The SayHello + /// + /// The name + /// The [Command(Strategy = StrategyType.Injection, ShuntStrategy = AddressSelectorMode.HashAlgorithm, ExecutionTimeoutInMilliseconds = 2500, BreakerRequestVolumeThreshold = 3, Injection = @"return 1;", RequestCacheEnabled = false)] Task SayHello(string name); + + #endregion 方法 } -} + + #endregion 接口 +} \ No newline at end of file diff --git a/src/Surging.Modules/Surging.Modules.Common/Domain/ChatService.cs b/src/Surging.Modules/Surging.Modules.Common/Domain/ChatService.cs index 3d12c6188..e6dff227d 100644 --- a/src/Surging.Modules/Surging.Modules.Common/Domain/ChatService.cs +++ b/src/Surging.Modules/Surging.Modules.Common/Domain/ChatService.cs @@ -13,14 +13,56 @@ namespace Surging.Modules.Common.Domain { + /// + /// Defines the + /// public class ChatService : WSServiceBase, IChatService { - private static readonly ConcurrentDictionary _users = new ConcurrentDictionary(); + #region 字段 + + /// + /// Defines the _clients + /// private static readonly ConcurrentDictionary _clients = new ConcurrentDictionary(); + + /// + /// Defines the _users + /// + private static readonly ConcurrentDictionary _users = new ConcurrentDictionary(); + + /// + /// Defines the _name + /// private string _name; + + /// + /// Defines the _to + /// private string _to; - + #endregion 字段 + + #region 方法 + + /// + /// The SendMessage + /// + /// The name + /// The data + /// The + public Task SendMessage(string name, string data) + { + if (_users.ContainsKey(name)) + { + this.GetClient().SendTo($"hello,{name},{data}", _users[name]); + } + return Task.CompletedTask; + } + + /// + /// The OnMessage + /// + /// The e protected override void OnMessage(MessageEventArgs e) { if (_clients.ContainsKey(ID)) @@ -30,10 +72,12 @@ protected override void OnMessage(MessageEventArgs e) model.Add("data", e.Data); var result = ServiceLocator.GetService() .Invoke(model, "api/chat/SendMessage").Result; - } } + /// + /// The OnOpen + /// protected override void OnOpen() { _name = Context.QueryString["name"]; @@ -44,13 +88,7 @@ protected override void OnOpen() _users[_name] = ID; } } - public Task SendMessage(string name, string data) - { - if (_users.ContainsKey(name)) - { - this.GetClient().SendTo($"hello,{name},{data}", _users[name]); - } - return Task.CompletedTask; - } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Modules/Surging.Modules.Common/Domain/ControllerService.cs b/src/Surging.Modules/Surging.Modules.Common/Domain/ControllerService.cs index 66742b407..3737092de 100644 --- a/src/Surging.Modules/Surging.Modules.Common/Domain/ControllerService.cs +++ b/src/Surging.Modules/Surging.Modules.Common/Domain/ControllerService.cs @@ -13,22 +13,44 @@ namespace Surging.Modules.Common.Domain { + /// + /// Defines the + /// public class ControllerService : MqttBehavior, IControllerService { + #region 方法 + + /// + /// The Authorized + /// + /// The username + /// The password + /// The public override async Task Authorized(string username, string password) { bool result = false; if (username == "admin" && password == "123456") - result= true; + result = true; return await Task.FromResult(result); } - public async Task IsOnline(string deviceId) + /// + /// The IsOnline + /// + /// The deviceId + /// The + public async Task IsOnline(string deviceId) { var text = await GetService().SayHello("fanly"); - return await base.GetDeviceIsOnine(deviceId); + return await base.GetDeviceIsOnine(deviceId); } + /// + /// The Publish + /// + /// The deviceId + /// The message + /// The public async Task Publish(string deviceId, WillMessage message) { var willMessage = new MqttWillMessage @@ -41,5 +63,7 @@ public async Task Publish(string deviceId, WillMessage message) await Publish(deviceId, willMessage); await RemotePublish(deviceId, willMessage); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Modules/Surging.Modules.Common/Domain/DnsService.cs b/src/Surging.Modules/Surging.Modules.Common/Domain/DnsService.cs index 13d2e3726..7ba75f42d 100644 --- a/src/Surging.Modules/Surging.Modules.Common/Domain/DnsService.cs +++ b/src/Surging.Modules/Surging.Modules.Common/Domain/DnsService.cs @@ -8,15 +8,27 @@ namespace Surging.Modules.Common.Domain { + /// + /// Defines the + /// public class DnsService : DnsBehavior, IDnsService { + #region 方法 + + /// + /// The Resolve + /// + /// The domainName + /// The public override Task Resolve(string domainName) { - if(domainName=="localhost") + if (domainName == "localhost") { return Task.FromResult(IPAddress.Parse("127.0.0.1")); } return Task.FromResult(null); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Modules/Surging.Modules.Common/Domain/PersonService.cs b/src/Surging.Modules/Surging.Modules.Common/Domain/PersonService.cs index e9f02884e..dee672a0a 100644 --- a/src/Surging.Modules/Surging.Modules.Common/Domain/PersonService.cs +++ b/src/Surging.Modules/Surging.Modules.Common/Domain/PersonService.cs @@ -1,127 +1,269 @@ -using Surging.IModuleServices.Common; +using Newtonsoft.Json.Linq; +using Surging.Core.Common; +using Surging.Core.CPlatform; +using Surging.Core.CPlatform.EventBus.Events; +using Surging.Core.CPlatform.Ioc; +using Surging.Core.KestrelHttpServer; +using Surging.Core.KestrelHttpServer.Internal; +using Surging.Core.ProxyGenerator; +using Surging.IModuleServices.Common; using Surging.IModuleServices.Common.Models; using Surging.Modules.Common.Repositories; using System; using System.Collections.Generic; +using System.IO; using System.Text; using System.Threading.Tasks; -using Surging.Core.CPlatform.EventBus.Events; -using Newtonsoft.Json.Linq; -using Surging.Core.CPlatform; -using Surging.Core.CPlatform.Ioc; -using Surging.Core.ProxyGenerator; -using Surging.Core.KestrelHttpServer.Internal; -using System.IO; -using Surging.Core.KestrelHttpServer; -using Surging.Core.Common; namespace Surging.Modules.Common.Domain { + /// + /// Defines the + /// [ModuleName("Person")] public class PersonService : ProxyServiceBase, IUserService { - #region Implementation of IUserService + #region 字段 + + /// + /// Defines the _repository + /// private readonly UserRepository _repository; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The repository public PersonService(UserRepository repository) { this._repository = repository; } - public Task GetUserName(int id) + #endregion 构造函数 + + #region 方法 + + /// + /// The Authentication + /// + /// The requestData + /// The + public Task Authentication(AuthenticationRequestData requestData) { - return GetService("User").GetUserName(id); + if (requestData.UserName == "admin" && requestData.Password == "admin") + { + return Task.FromResult(new UserModel()); + } + return Task.FromResult(null); } + /// + /// The DownFile + /// + /// The fileName + /// The contentType + /// The + public async Task DownFile(string fileName, string contentType) + { + string uploadPath = Path.Combine("C:", fileName); + if (File.Exists(uploadPath)) + { + using (var stream = new FileStream(uploadPath, FileMode.Open)) + { + var bytes = new Byte[stream.Length]; + await stream.WriteAsync(bytes, 0, bytes.Length); + return new FileContentResult(bytes, contentType, fileName); + } + } + else + { + throw new FileNotFoundException(fileName); + } + } + + /// + /// The Exists + /// + /// The id + /// The public Task Exists(int id) { return Task.FromResult(true); } - public Task GetUserId(string userName) + /// + /// The Get + /// + /// The users + /// The + public Task Get(List users) { - return Task.FromResult(1); + return Task.FromResult(true); } - public Task GetUserLastSignInTime(int id) + /// + /// The GetAllThings + /// + /// The + public async Task> GetAllThings() { - return Task.FromResult(DateTime.Now); + return await Task.FromResult(new Dictionary { { "aaa", 12 } }); } - public Task GetUser(UserModel user) + /// + /// The GetApiResult + /// + /// The + public Task> GetApiResult() { - return Task.FromResult(new UserModel - { - Name = "fanly", - Age = 18 - }); + return Task.FromResult(new ApiResult() { Value = new UserModel { Name = "fanly" }, StatusCode = 200 }); } - public Task Get(List users) + /// + /// The GetDictionary + /// + /// The + public Task GetDictionary() { - return Task.FromResult(true); + return Task.FromResult(true); } - public Task Update(int id, UserModel model) + /// + /// The GetUser + /// + /// The idList + /// The + public Task GetUser(List idList) { - return Task.FromResult(true); + return Task.FromResult("type is List"); } + /// + /// The GetUser + /// + /// The user + /// The + public Task GetUser(UserModel user) + { + return Task.FromResult(new UserModel + { + Name = "fanly", + Age = 18 + }); + } + /// + /// The GetUserById + /// + /// The id + /// The public Task GetUserById(Guid id) { return Task.FromResult(new UserModel { - }); } - public Task GetDictionary() + /// + /// The GetUserId + /// + /// The userName + /// The + public Task GetUserId(string userName) { - return Task.FromResult(true); + return Task.FromResult(1); } - - public async Task Try() + /// + /// The GetUserLastSignInTime + /// + /// The id + /// The + public Task GetUserLastSignInTime(int id) { - Console.WriteLine("start"); - await Task.Delay(5000); - Console.WriteLine("end"); + return Task.FromResult(DateTime.Now); } - public Task TryThrowException() + /// + /// The GetUserName + /// + /// The id + /// The + public Task GetUserName(int id) { - throw new Exception("用户Id非法!"); + return GetService("User").GetUserName(id); } + /// + /// The PublishThroughEventBusAsync + /// + /// The evt + /// The public async Task PublishThroughEventBusAsync(IntegrationEvent evt) { await Task.CompletedTask; } - public Task Authentication(AuthenticationRequestData requestData) + /// + /// The Save + /// + /// The requestData + /// The + public Task Save(IdentityUser requestData) { - if (requestData.UserName == "admin" && requestData.Password == "admin") - { - return Task.FromResult(new UserModel()); - } - return Task.FromResult(null); + return Task.FromResult(requestData); } - public Task Save(IdentityUser requestData) + /// + /// The SetSex + /// + /// The sex + /// The + public async Task SetSex(Sex sex) { - return Task.FromResult(requestData); + return await Task.FromResult(sex); } - public Task> GetApiResult() + /// + /// The Try + /// + /// The + public async Task Try() + { + Console.WriteLine("start"); + await Task.Delay(5000); + Console.WriteLine("end"); + } + + /// + /// The TryThrowException + /// + /// The + public Task TryThrowException() { - return Task.FromResult(new ApiResult() { Value = new UserModel { Name = "fanly" }, StatusCode=200 }); + throw new Exception("用户Id非法!"); } - public Task GetUser(List idList) + /// + /// The Update + /// + /// The id + /// The model + /// The + public Task Update(int id, UserModel model) { - return Task.FromResult("type is List"); + return Task.FromResult(true); } + /// + /// The UploadFile + /// + /// The form + /// The public async Task UploadFile(HttpFormCollection form) { var files = form.Files; @@ -135,36 +277,6 @@ public async Task UploadFile(HttpFormCollection form) return true; } - public async Task DownFile(string fileName, string contentType) - { - string uploadPath = Path.Combine("C:", fileName); - if (File.Exists(uploadPath)) - { - using (var stream = new FileStream(uploadPath, FileMode.Open)) - { - - var bytes = new Byte[stream.Length]; - await stream.WriteAsync(bytes, 0, bytes.Length); - return new FileContentResult(bytes, contentType, fileName); - } - } - else - { - throw new FileNotFoundException(fileName); - } - - } - - public async Task SetSex(Sex sex) - { - return await Task.FromResult(sex); - } - - public async Task> GetAllThings() - { - return await Task.FromResult(new Dictionary { { "aaa", 12 } }); - } - - #endregion Implementation of IUserService + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Modules/Surging.Modules.Common/Domain/RoteMangeService.cs b/src/Surging.Modules/Surging.Modules.Common/Domain/RoteMangeService.cs index 24bde337d..1fe81d54d 100644 --- a/src/Surging.Modules/Surging.Modules.Common/Domain/RoteMangeService.cs +++ b/src/Surging.Modules/Surging.Modules.Common/Domain/RoteMangeService.cs @@ -9,16 +9,33 @@ namespace Surging.Modules.Common.Domain { + /// + /// Defines the + /// public class RoteMangeService : ProxyServiceBase, IRoteMangeService { + #region 方法 + + /// + /// The GetServiceById + /// + /// The serviceId + /// The public Task GetServiceById(string serviceId) { return Task.FromResult(new UserModel()); } + /// + /// The SetRote + /// + /// The model + /// The public Task SetRote(RoteModel model) { return Task.FromResult(true); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Modules/Surging.Modules.Common/Domain/UserService.cs b/src/Surging.Modules/Surging.Modules.Common/Domain/UserService.cs index 027820264..58cd94c60 100644 --- a/src/Surging.Modules/Surging.Modules.Common/Domain/UserService.cs +++ b/src/Surging.Modules/Surging.Modules.Common/Domain/UserService.cs @@ -1,5 +1,4 @@ - -using Surging.Core.Common; +using Surging.Core.Common; using Surging.Core.CPlatform.EventBus.Events; using Surging.Core.CPlatform.EventBus.Implementation; using Surging.Core.CPlatform.Ioc; @@ -18,50 +17,137 @@ namespace Surging.Modules.Common.Domain { + /// + /// Defines the + /// [ModuleName("User")] public class UserService : ProxyServiceBase, IUserService { - #region Implementation of IUserService + #region 字段 + + /// + /// Defines the _repository + /// private readonly UserRepository _repository; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The repository public UserService(UserRepository repository) { this._repository = repository; } - public async Task GetUserName(int id) + #endregion 构造函数 + + #region 方法 + + /// + /// The Authentication + /// + /// The requestData + /// The + public Task Authentication(AuthenticationRequestData requestData) { - var text= await this.GetService().SayHello("fanly"); - return await Task.FromResult(text); + if (requestData.UserName == "admin" && requestData.Password == "admin") + { + return Task.FromResult(new UserModel() { UserId = 22, Name = "admin" }); + } + return Task.FromResult(null); + } + + /// + /// The DownFile + /// + /// The fileName + /// The contentType + /// The + public async Task DownFile(string fileName, string contentType) + { + string uploadPath = Path.Combine(AppContext.BaseDirectory, fileName); + if (File.Exists(uploadPath)) + { + using (var stream = new FileStream(uploadPath, FileMode.Open)) + { + var bytes = new Byte[stream.Length]; + await stream.ReadAsync(bytes, 0, bytes.Length); + stream.Dispose(); + return new FileContentResult(bytes, contentType, fileName); + } + } + else + { + throw new FileNotFoundException(fileName); + } } + /// + /// The Exists + /// + /// The id + /// The public Task Exists(int id) { return Task.FromResult(true); } - public Task GetUserById(Guid id) + /// + /// The Get + /// + /// The users + /// The + public Task Get(List users) { - return Task.FromResult(new UserModel { + return Task.FromResult(true); + } - }); + /// + /// The GetAllThings + /// + /// The + public async Task> GetAllThings() + { + return await Task.FromResult(new Dictionary { { "aaa", 12 } }); } - public Task GetUserId(string userName) + /// + /// The GetApiResult + /// + /// The + public Task> GetApiResult() { - var xid = RpcContext.GetContext().GetAttachment("xid"); - return Task.FromResult(1); + return Task.FromResult(new ApiResult() { Value = new UserModel { Name = "fanly" }, StatusCode = 200 }); } - public Task GetUserLastSignInTime(int id) + /// + /// The GetDictionary + /// + /// The + public Task GetDictionary() { - return Task.FromResult(new DateTime(DateTime.Now.Ticks)); + return Task.FromResult(true); } - public Task Get(List users) + /// + /// The GetUser + /// + /// The idList + /// The + public Task GetUser(List idList) { - return Task.FromResult(true); + return Task.FromResult("type is List"); } + /// + /// The GetUser + /// + /// The user + /// The public Task GetUser(UserModel user) { return Task.FromResult(new UserModel @@ -71,99 +157,130 @@ public Task GetUser(UserModel user) }); } - public Task Update(int id, UserModel model) + /// + /// The GetUserById + /// + /// The id + /// The + public Task GetUserById(Guid id) { - return Task.FromResult(true); + return Task.FromResult(new UserModel + { + }); } - public Task GetDictionary() + /// + /// The GetUserId + /// + /// The userName + /// The + public Task GetUserId(string userName) { - return Task.FromResult(true); + var xid = RpcContext.GetContext().GetAttachment("xid"); + return Task.FromResult(1); } - public async Task Try() + /// + /// The GetUserLastSignInTime + /// + /// The id + /// The + public Task GetUserLastSignInTime(int id) { - Console.WriteLine("start"); - await Task.Delay(5000); - Console.WriteLine("end"); + return Task.FromResult(new DateTime(DateTime.Now.Ticks)); } - public Task TryThrowException() + /// + /// The GetUserName + /// + /// The id + /// The + public async Task GetUserName(int id) { - throw new Exception("用户Id非法!"); + var text = await this.GetService().SayHello("fanly"); + return await Task.FromResult(text); } + /// + /// The PublishThroughEventBusAsync + /// + /// The evt + /// The public async Task PublishThroughEventBusAsync(IntegrationEvent evt) { Publish(evt); await Task.CompletedTask; } - public Task Authentication(AuthenticationRequestData requestData) - { - if (requestData.UserName == "admin" && requestData.Password == "admin") - { - return Task.FromResult(new UserModel() { UserId = 22, Name = "admin" }); - } - return Task.FromResult(null); - } - + /// + /// The Save + /// + /// The requestData + /// The public Task Save(IdentityUser requestData) { return Task.FromResult(requestData); } - public Task> GetApiResult() + /// + /// The SetSex + /// + /// The sex + /// The + public async Task SetSex(Sex sex) { - return Task.FromResult(new ApiResult() { Value = new UserModel { Name = "fanly" }, StatusCode = 200 }); + return await Task.FromResult(sex); } - public async Task UploadFile(HttpFormCollection form) + /// + /// The Try + /// + /// The + public async Task Try() { - var files = form.Files; - foreach (var file in files) - { - using (var stream = new FileStream(Path.Combine(AppContext.BaseDirectory, file.FileName), FileMode.OpenOrCreate)) - { - await stream.WriteAsync(file.File, 0, (int)file.Length); - } - } - return true; + Console.WriteLine("start"); + await Task.Delay(5000); + Console.WriteLine("end"); } - public Task GetUser(List idList) + /// + /// The TryThrowException + /// + /// The + public Task TryThrowException() { - return Task.FromResult("type is List"); + throw new Exception("用户Id非法!"); } - public async Task> GetAllThings() + /// + /// The Update + /// + /// The id + /// The model + /// The + public Task Update(int id, UserModel model) { - return await Task.FromResult(new Dictionary { { "aaa", 12 } }); + return Task.FromResult(true); } - public async Task DownFile(string fileName,string contentType) + /// + /// The UploadFile + /// + /// The form + /// The + public async Task UploadFile(HttpFormCollection form) { - string uploadPath = Path.Combine(AppContext.BaseDirectory, fileName); - if (File.Exists(uploadPath)) + var files = form.Files; + foreach (var file in files) { - using (var stream = new FileStream(uploadPath, FileMode.Open)) + using (var stream = new FileStream(Path.Combine(AppContext.BaseDirectory, file.FileName), FileMode.OpenOrCreate)) { - var bytes = new Byte[stream.Length]; - await stream.ReadAsync(bytes, 0, bytes.Length); - stream.Dispose(); - return new FileContentResult(bytes, contentType, fileName); + await stream.WriteAsync(file.File, 0, (int)file.Length); } } - else - { - throw new FileNotFoundException(fileName); - } + return true; } - public async Task SetSex(Sex sex) - { - return await Task.FromResult(sex); - } - #endregion Implementation of IUserService + #endregion 方法 } } \ No newline at end of file diff --git a/src/Surging.Modules/Surging.Modules.Common/IntegrationEvents/EventHandling/UserLoginDateChangeHandler.cs b/src/Surging.Modules/Surging.Modules.Common/IntegrationEvents/EventHandling/UserLoginDateChangeHandler.cs index 23e1c9ccb..1d78a43fb 100644 --- a/src/Surging.Modules/Surging.Modules.Common/IntegrationEvents/EventHandling/UserLoginDateChangeHandler.cs +++ b/src/Surging.Modules/Surging.Modules.Common/IntegrationEvents/EventHandling/UserLoginDateChangeHandler.cs @@ -10,14 +10,40 @@ namespace Surging.Modules.Common.IntegrationEvents.EventHandling { - [QueueConsumer("UserLoginDateChangeHandler",QueueConsumerMode.Normal,QueueConsumerMode.Fail)] - public class UserLoginDateChangeHandler : BaseIntegrationEventHandler + /// + /// Defines the + /// + [QueueConsumer("UserLoginDateChangeHandler", QueueConsumerMode.Normal, QueueConsumerMode.Fail)] + public class UserLoginDateChangeHandler : BaseIntegrationEventHandler { + #region 字段 + + /// + /// Defines the _userService + /// private readonly IUserService _userService; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public UserLoginDateChangeHandler() { _userService = ServiceLocator.GetService("User"); - } + } + + #endregion 构造函数 + + #region 方法 + + /// + /// The Handle + /// + /// The event + /// The public override async Task Handle(UserEvent @event) { Console.WriteLine($"消费1。"); @@ -31,11 +57,18 @@ public override async Task Handle(UserEvent @event) throw new Exception(); } + /// + /// The Handled + /// + /// The context + /// The public override Task Handled(EventContext context) { Console.WriteLine($"调用{context.Count}次。类型:{context.Type}"); var model = context.Content as UserEvent; return Task.CompletedTask; } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Modules/Surging.Modules.Common/IntegrationEvents/EventHandling/UserLogoutDataChangeHandler.cs b/src/Surging.Modules/Surging.Modules.Common/IntegrationEvents/EventHandling/UserLogoutDataChangeHandler.cs index b40ed7e2c..ba3d452d8 100644 --- a/src/Surging.Modules/Surging.Modules.Common/IntegrationEvents/EventHandling/UserLogoutDataChangeHandler.cs +++ b/src/Surging.Modules/Surging.Modules.Common/IntegrationEvents/EventHandling/UserLogoutDataChangeHandler.cs @@ -9,23 +9,50 @@ namespace Surging.Modules.Common.IntegrationEvents.EventHandling { + /// + /// Defines the + /// [QueueConsumer("UserLogoutDateChangeHandler")] public class UserLogoutDataChangeHandler : IIntegrationEventHandler { + #region 字段 + + /// + /// Defines the _userService + /// private readonly IUserService _userService; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public UserLogoutDataChangeHandler() { _userService = ServiceLocator.GetService("User"); } + + #endregion 构造函数 + + #region 方法 + + /// + /// The Handle + /// + /// The event + /// The public async Task Handle(LogoutEvent @event) { Console.WriteLine($"消费1。"); await _userService.Update(int.Parse(@event.UserId), new UserModel() { - }); Console.WriteLine($"消费1失败。"); throw new Exception(); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Modules/Surging.Modules.Common/Repositories/UserRepository.cs b/src/Surging.Modules/Surging.Modules.Common/Repositories/UserRepository.cs index 179397ce5..6cec1a742 100644 --- a/src/Surging.Modules/Surging.Modules.Common/Repositories/UserRepository.cs +++ b/src/Surging.Modules/Surging.Modules.Common/Repositories/UserRepository.cs @@ -5,7 +5,10 @@ namespace Surging.Modules.Common.Repositories { - public class UserRepository: BaseRepository + /// + /// Defines the + /// + public class UserRepository : BaseRepository { } -} +} \ No newline at end of file diff --git a/src/Surging.Modules/Surging.Modules.Manager/Domain/ManagerService.cs b/src/Surging.Modules/Surging.Modules.Manager/Domain/ManagerService.cs index 8365efb53..4c341791f 100644 --- a/src/Surging.Modules/Surging.Modules.Manager/Domain/ManagerService.cs +++ b/src/Surging.Modules/Surging.Modules.Manager/Domain/ManagerService.cs @@ -1,14 +1,26 @@ -using System.Threading.Tasks; -using Surging.Core.ProxyGenerator; +using Surging.Core.ProxyGenerator; using Surging.IModuleServices.User; +using System.Threading.Tasks; namespace Surging.Modules.Manager.Domain { + /// + /// Defines the + /// public class ManagerService : ProxyServiceBase, IManagerService { + #region 方法 + + /// + /// The SayHello + /// + /// The name + /// The public Task SayHello(string name) { - return Task.FromResult($"{name} say:hello"); + return Task.FromResult($"{name} say:hello"); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Services/Surging.Services.Client/Program.cs b/src/Surging.Services/Surging.Services.Client/Program.cs index bbd115900..993cfb0aa 100644 --- a/src/Surging.Services/Surging.Services.Client/Program.cs +++ b/src/Surging.Services/Surging.Services.Client/Program.cs @@ -21,19 +21,50 @@ using Surging.IModuleServices.Common; using System; using System.Diagnostics; -//using Surging.Core.Zookeeper; -//using Surging.Core.Zookeeper.Configurations; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Surging.Services.Client { + /// + /// Defines the + /// public class Program { + #region 字段 + + /// + /// Defines the _endedConnenctionCount + /// private static int _endedConnenctionCount = 0; + + /// + /// Defines the begintime + /// private static DateTime begintime; - static void Main(string[] args) + + #endregion 字段 + + #region 方法 + + /// + /// The Test + /// + /// The userProxy + /// The connectionCount + /// The + public static async Task Test(IUserService userProxy, int connectionCount) + { + var a = await userProxy.GetDictionary(); + IncreaseSuccessConnection(connectionCount); + } + + /// + /// The Main + /// + /// The args + internal static void Main(string[] args) { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); var host = new ServiceHostBuilder() @@ -67,16 +98,33 @@ static void Main(string[] args) Startup.Test(ServiceLocator.GetService()); //Startup.TestRabbitMq(ServiceLocator.GetService()); // Startup.TestForRoutePath(ServiceLocator.GetService()); - /// test Parallel + /// test Parallel //var connectionCount = 300000; //StartRequest(connectionCount); //Console.ReadLine(); } } + /// + /// The IncreaseSuccessConnection + /// + /// The connectionCount + private static void IncreaseSuccessConnection(int connectionCount) + { + Interlocked.Increment(ref _endedConnenctionCount); + if (_endedConnenctionCount == 1) + begintime = DateTime.Now; + if (_endedConnenctionCount >= connectionCount) + Console.WriteLine($"结束时间{(DateTime.Now - begintime).TotalMilliseconds}"); + } + + /// + /// The StartRequest + /// + /// The connectionCount private static void StartRequest(int connectionCount) { - // var service = ServiceLocator.GetService(); + // var service = ServiceLocator.GetService(); var sw = new Stopwatch(); sw.Start(); var userProxy = ServiceLocator.GetService().CreateProxy("User"); @@ -93,19 +141,6 @@ private static void StartRequest(int connectionCount) }); } - public static async Task Test(IUserService userProxy,int connectionCount) - { - var a =await userProxy.GetDictionary(); - IncreaseSuccessConnection(connectionCount); - } - - private static void IncreaseSuccessConnection(int connectionCount) - { - Interlocked.Increment(ref _endedConnenctionCount); - if (_endedConnenctionCount == 1) - begintime = DateTime.Now; - if (_endedConnenctionCount >= connectionCount) - Console.WriteLine($"结束时间{(DateTime.Now - begintime).TotalMilliseconds}"); - } + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Services/Surging.Services.Client/Startup.cs b/src/Surging.Services/Surging.Services.Client/Startup.cs index f9ebf4314..fb427f2a9 100644 --- a/src/Surging.Services/Surging.Services.Client/Startup.cs +++ b/src/Surging.Services/Surging.Services.Client/Startup.cs @@ -19,54 +19,35 @@ namespace Surging.Services.Client { + /// + /// Defines the + /// public class Startup { + #region 字段 + + /// + /// Defines the _builder + /// private ContainerBuilder _builder; - public Startup(IConfigurationBuilder config) - { - ConfigureEventBus(config); - ConfigureCache(config); - } - public IContainer ConfigureServices(ContainerBuilder builder) - { - var services = new ServiceCollection(); - ConfigureLogging(services); - builder.Populate(services); - _builder = builder; - ServiceLocator.Current = builder.Build(); - return ServiceLocator.Current; - } + #endregion 字段 - public void Configure(IContainer app) - { - - } + #region 构造函数 - #region 私有方法 /// - /// 配置日志服务 + /// Initializes a new instance of the class. /// - /// - private void ConfigureLogging(IServiceCollection services) + /// The config + public Startup(IConfigurationBuilder config) { - services.AddLogging(); + ConfigureEventBus(config); + ConfigureCache(config); } - private static void ConfigureEventBus(IConfigurationBuilder build) - { - build - .AddEventBusFile("eventBusSettings.json", optional: false); - } + #endregion 构造函数 - /// - /// 配置缓存服务 - /// - private void ConfigureCache(IConfigurationBuilder build) - { - build - .AddCacheFile("cacheSettings.json", optional: false); - } + #region 方法 /// /// 测试 @@ -76,7 +57,7 @@ public static void Test(IServiceProxyFactory serviceProxyFactory) { Task.Run(async () => { - RpcContext.GetContext().SetAttachment("xid",124); + RpcContext.GetContext().SetAttachment("xid", 124); var userProxy = serviceProxyFactory.CreateProxy("User"); var e = userProxy.SetSex(Sex.Woman).GetAwaiter().GetResult(); var v = userProxy.GetUserId("fanly").GetAwaiter().GetResult(); @@ -122,22 +103,14 @@ public static void Test(IServiceProxyFactory serviceProxyFactory) }).Wait(); } - public static void TestRabbitMq(IServiceProxyFactory serviceProxyFactory) - { - serviceProxyFactory.CreateProxy("User").PublishThroughEventBusAsync(new UserEvent() - { - Age = 18, - Name = "fanly", - UserId = 1 - }); - Console.WriteLine("Press any key to exit..."); - Console.ReadLine(); - } - + /// + /// The TestForRoutePath + /// + /// The serviceProxyProvider public static void TestForRoutePath(IServiceProxyProvider serviceProxyProvider) { Dictionary model = new Dictionary(); - model.Add("user", JsonConvert.SerializeObject( new + model.Add("user", JsonConvert.SerializeObject(new { Name = "fanly", Age = 18, @@ -152,7 +125,75 @@ public static void TestForRoutePath(IServiceProxyProvider serviceProxyProvider) Console.WriteLine("Press any key to exit..."); Console.ReadLine(); } - #endregion + /// + /// The TestRabbitMq + /// + /// The serviceProxyFactory + public static void TestRabbitMq(IServiceProxyFactory serviceProxyFactory) + { + serviceProxyFactory.CreateProxy("User").PublishThroughEventBusAsync(new UserEvent() + { + Age = 18, + Name = "fanly", + UserId = 1 + }); + Console.WriteLine("Press any key to exit..."); + Console.ReadLine(); + } + + /// + /// The Configure + /// + /// The app + public void Configure(IContainer app) + { + } + + /// + /// The ConfigureServices + /// + /// The builder + /// The + public IContainer ConfigureServices(ContainerBuilder builder) + { + var services = new ServiceCollection(); + ConfigureLogging(services); + builder.Populate(services); + _builder = builder; + ServiceLocator.Current = builder.Build(); + return ServiceLocator.Current; + } + + /// + /// The ConfigureEventBus + /// + /// The build + private static void ConfigureEventBus(IConfigurationBuilder build) + { + build + .AddEventBusFile("eventBusSettings.json", optional: false); + } + + /// + /// 配置缓存服务 + /// + /// The build + private void ConfigureCache(IConfigurationBuilder build) + { + build + .AddCacheFile("cacheSettings.json", optional: false); + } + + /// + /// 配置日志服务 + /// + /// + private void ConfigureLogging(IServiceCollection services) + { + services.AddLogging(); + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Services/Surging.Services.Server/Program.cs b/src/Surging.Services/Surging.Services.Server/Program.cs index cc7243671..08d216cc5 100644 --- a/src/Surging.Services/Surging.Services.Server/Program.cs +++ b/src/Surging.Services/Surging.Services.Server/Program.cs @@ -10,7 +10,6 @@ using Surging.Core.CPlatform.Utilities; using Surging.Core.DotNetty; using Surging.Core.EventBusKafka.Configurations; -//using Surging.Core.EventBusKafka; using Surging.Core.EventBusRabbitMQ; using Surging.Core.Log4net; using Surging.Core.Nlog; @@ -20,15 +19,22 @@ using Surging.Core.ServiceHosting.Internal.Implementation; using Surging.Core.Zookeeper.Configurations; using System; -//using Surging.Core.Zookeeper; -//using Surging.Core.Zookeeper.Configurations; using System.Text; namespace Surging.Services.Server { + /// + /// Defines the + /// public class Program { - static void Main(string[] args) + #region 方法 + + /// + /// The Main + /// + /// The args + internal static void Main(string[] args) { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); var host = new ServiceHostBuilder() @@ -39,7 +45,7 @@ static void Main(string[] args) option.AddServiceRuntime() .AddRelateService() .AddConfigurationWatch() - //option.UseZooKeeperManager(new ConfigInfo("127.0.0.1:2181")); + //option.UseZooKeeperManager(new ConfigInfo("127.0.0.1:2181")); .AddServiceEngine(typeof(SurgingServiceEngine)); builder.Register(p => new CPlatformContainer(ServiceLocator.Current)); }); @@ -49,10 +55,10 @@ static void Main(string[] args) logger.AddConfiguration( Core.CPlatform.AppConfig.GetSection("Logging")); }) - .UseServer(options =>{ }) + .UseServer(options => { }) .UseConsoleLifetime() .Configure(build => - build.AddCacheFile("${cachepath}|cacheSettings.json",basePath:AppContext.BaseDirectory, optional: false, reloadOnChange: true)) + build.AddCacheFile("${cachepath}|cacheSettings.json", basePath: AppContext.BaseDirectory, optional: false, reloadOnChange: true)) .Configure(build => build.AddCPlatformFile("${surgingpath}|surgingSettings.json", optional: false, reloadOnChange: true)) .UseStartup() @@ -63,5 +69,7 @@ static void Main(string[] args) Console.WriteLine($"服务端启动成功,{DateTime.Now}。"); } } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Services/Surging.Services.Server/Startup.cs b/src/Surging.Services/Surging.Services.Server/Startup.cs index d29a9f5cf..67c8171b5 100644 --- a/src/Surging.Services/Surging.Services.Server/Startup.cs +++ b/src/Surging.Services/Surging.Services.Server/Startup.cs @@ -8,14 +8,39 @@ namespace Surging.Services.Server { + /// + /// Defines the + /// public class Startup { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + /// The config public Startup(IConfigurationBuilder config) { - ConfigureEventBus(config); - // ConfigureCache(config); + ConfigureEventBus(config); + } + + #endregion 构造函数 + + #region 方法 + + /// + /// The Configure + /// + /// The app + public void Configure(IContainer app) + { } + /// + /// The ConfigureServices + /// + /// The builder + /// The public IContainer ConfigureServices(ContainerBuilder builder) { var services = new ServiceCollection(); @@ -25,21 +50,10 @@ public IContainer ConfigureServices(ContainerBuilder builder) return ServiceLocator.Current; } - public void Configure(IContainer app) - { - - } - - #region 私有方法 /// - /// 配置日志服务 + /// The ConfigureEventBus /// - /// - private void ConfigureLogging(IServiceCollection services) - { - // services.AddLogging(); - } - + /// The build private static void ConfigureEventBus(IConfigurationBuilder build) { build @@ -49,12 +63,21 @@ private static void ConfigureEventBus(IConfigurationBuilder build) /// /// 配置缓存服务 /// + /// The build private void ConfigureCache(IConfigurationBuilder build) { build .AddCacheFile("cacheSettings.json", optional: false); } - #endregion + /// + /// 配置日志服务 + /// + /// + private void ConfigureLogging(IServiceCollection services) + { + } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/Surging.Services/Surging.Services.Server/SurgingServiceEngine.cs b/src/Surging.Services/Surging.Services.Server/SurgingServiceEngine.cs index 97baba1cc..c9df46799 100644 --- a/src/Surging.Services/Surging.Services.Server/SurgingServiceEngine.cs +++ b/src/Surging.Services/Surging.Services.Server/SurgingServiceEngine.cs @@ -7,20 +7,26 @@ namespace Surging.Services.Server { - public class SurgingServiceEngine: VirtualPathProviderServiceEngine + /// + /// Defines the + /// + public class SurgingServiceEngine : VirtualPathProviderServiceEngine { + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// public SurgingServiceEngine() { - ModuleServiceLocationFormats = new[] { EnvironmentHelper.GetEnvironmentVariable("${ModulePath1}|Modules"), }; - ComponentServiceLocationFormats = new[] { + ComponentServiceLocationFormats = new[] { EnvironmentHelper.GetEnvironmentVariable("${ComponentPath1}|Components"), }; - //ModuleServiceLocationFormats = new[] { - // "" - //}; } + + #endregion 构造函数 } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/ByteOrder.cs b/src/WebSocket/WebSocketCore/ByteOrder.cs index d6e57bb12..7dec55af5 100644 --- a/src/WebSocket/WebSocketCore/ByteOrder.cs +++ b/src/WebSocket/WebSocketCore/ByteOrder.cs @@ -1,4 +1,3 @@ -#region License /* * ByteOrder.cs * @@ -24,24 +23,28 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; namespace WebSocketCore { - /// - /// Specifies the byte order. - /// - public enum ByteOrder - { - /// - /// Specifies Little-endian. - /// - Little, + #region ö + /// - /// Specifies Big-endian. + /// Specifies the byte order. /// - Big - } -} + public enum ByteOrder + { + /// + /// Specifies Little-endian. + /// + Little, + + /// + /// Specifies Big-endian. + /// + Big + } + + #endregion ö +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/CloseEventArgs.cs b/src/WebSocket/WebSocketCore/CloseEventArgs.cs index 479e6e2c7..09c5467dc 100644 --- a/src/WebSocket/WebSocketCore/CloseEventArgs.cs +++ b/src/WebSocket/WebSocketCore/CloseEventArgs.cs @@ -1,4 +1,3 @@ -#region License /* * CloseEventArgs.cs * @@ -24,119 +23,141 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; namespace WebSocketCore { - /// - /// Represents the event data for the event. - /// - /// - /// - /// That event occurs when the WebSocket connection has been closed. - /// - /// - /// If you would like to get the reason for the close, you should access - /// the or property. - /// - /// - public class CloseEventArgs : EventArgs - { - #region Private Fields - - private bool _clean; - private PayloadData _payloadData; - - #endregion - - #region Internal Constructors - - internal CloseEventArgs () - { - _payloadData = PayloadData.Empty; - } - - internal CloseEventArgs (ushort code) - : this (code, null) - { - } - - internal CloseEventArgs (CloseStatusCode code) - : this ((ushort) code, null) - { - } - - internal CloseEventArgs (PayloadData payloadData) - { - _payloadData = payloadData; - } - - internal CloseEventArgs (ushort code, string reason) - { - _payloadData = new PayloadData (code, reason); - } - - internal CloseEventArgs (CloseStatusCode code, string reason) - : this ((ushort) code, reason) - { - } - - #endregion - - #region Internal Properties - - internal PayloadData PayloadData { - get { - return _payloadData; - } - } - - #endregion - - #region Public Properties - /// - /// Gets the status code for the close. + /// Represents the event data for the event. /// - /// - /// A that represents the status code for the close if any. - /// - public ushort Code { - get { - return _payloadData.Code; - } - } - - /// - /// Gets the reason for the close. - /// - /// - /// A that represents the reason for the close if any. - /// - public string Reason { - get { - return _payloadData.Reason ?? String.Empty; - } - } - - /// - /// Gets a value indicating whether the connection has been closed cleanly. - /// - /// - /// true if the connection has been closed cleanly; otherwise, false. - /// - public bool WasClean { - get { - return _clean; - } - - internal set { - _clean = value; - } + public class CloseEventArgs : EventArgs + { + #region ֶ + + /// + /// Defines the _clean + /// + private bool _clean; + + /// + /// Defines the _payloadData + /// + private PayloadData _payloadData; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + internal CloseEventArgs() + { + _payloadData = PayloadData.Empty; + } + + /// + /// Initializes a new instance of the class. + /// + /// The code + internal CloseEventArgs(CloseStatusCode code) + : this((ushort)code, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The code + /// The reason + internal CloseEventArgs(CloseStatusCode code, string reason) + : this((ushort)code, reason) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The payloadData + internal CloseEventArgs(PayloadData payloadData) + { + _payloadData = payloadData; + } + + /// + /// Initializes a new instance of the class. + /// + /// The code + internal CloseEventArgs(ushort code) + : this(code, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The code + /// The reason + internal CloseEventArgs(ushort code, string reason) + { + _payloadData = new PayloadData(code, reason); + } + + #endregion 캯 + + #region + + /// + /// Gets the status code for the close. + /// + public ushort Code + { + get + { + return _payloadData.Code; + } + } + + /// + /// Gets the reason for the close. + /// + public string Reason + { + get + { + return _payloadData.Reason ?? String.Empty; + } + } + + /// + /// Gets or sets a value indicating whether WasClean + /// Gets a value indicating whether the connection has been closed cleanly. + /// + public bool WasClean + { + get + { + return _clean; + } + + internal set + { + _clean = value; + } + } + + /// + /// Gets the PayloadData + /// + internal PayloadData PayloadData + { + get + { + return _payloadData; + } + } + + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/CloseStatusCode.cs b/src/WebSocket/WebSocketCore/CloseStatusCode.cs index 1f3573e74..499fe3593 100644 --- a/src/WebSocket/WebSocketCore/CloseStatusCode.cs +++ b/src/WebSocket/WebSocketCore/CloseStatusCode.cs @@ -1,4 +1,3 @@ -#region License /* * CloseStatusCode.cs * @@ -24,97 +23,112 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; namespace WebSocketCore { - /// - /// Indicates the status code for the WebSocket connection close. - /// - /// - /// - /// The values of this enumeration are defined in - /// - /// Section 7.4 of RFC 6455. - /// - /// - /// "Reserved value" cannot be sent as a status code in - /// closing handshake by an endpoint. - /// - /// - public enum CloseStatusCode : ushort - { - /// - /// Equivalent to close status 1000. Indicates normal close. - /// - Normal = 1000, - /// - /// Equivalent to close status 1001. Indicates that an endpoint is - /// going away. - /// - Away = 1001, - /// - /// Equivalent to close status 1002. Indicates that an endpoint is - /// terminating the connection due to a protocol error. - /// - ProtocolError = 1002, - /// - /// Equivalent to close status 1003. Indicates that an endpoint is - /// terminating the connection because it has received a type of - /// data that it cannot accept. - /// - UnsupportedData = 1003, - /// - /// Equivalent to close status 1004. Still undefined. A Reserved value. - /// - Undefined = 1004, - /// - /// Equivalent to close status 1005. Indicates that no status code was - /// actually present. A Reserved value. - /// - NoStatus = 1005, - /// - /// Equivalent to close status 1006. Indicates that the connection was - /// closed abnormally. A Reserved value. - /// - Abnormal = 1006, - /// - /// Equivalent to close status 1007. Indicates that an endpoint is - /// terminating the connection because it has received a message that - /// contains data that is not consistent with the type of the message. - /// - InvalidData = 1007, - /// - /// Equivalent to close status 1008. Indicates that an endpoint is - /// terminating the connection because it has received a message that - /// violates its policy. - /// - PolicyViolation = 1008, - /// - /// Equivalent to close status 1009. Indicates that an endpoint is - /// terminating the connection because it has received a message that - /// is too big to process. - /// - TooBig = 1009, - /// - /// Equivalent to close status 1010. Indicates that a client is - /// terminating the connection because it has expected the server to - /// negotiate one or more extension, but the server did not return - /// them in the handshake response. - /// - MandatoryExtension = 1010, - /// - /// Equivalent to close status 1011. Indicates that a server is - /// terminating the connection because it has encountered an unexpected - /// condition that prevented it from fulfilling the request. - /// - ServerError = 1011, + #region ö + /// - /// Equivalent to close status 1015. Indicates that the connection was - /// closed due to a failure to perform a TLS handshake. A Reserved value. + /// Indicates the status code for the WebSocket connection close. /// - TlsHandshakeFailure = 1015 - } -} + /// + /// + /// The values of this enumeration are defined in + /// + /// Section 7.4 of RFC 6455. + /// + /// + /// "Reserved value" cannot be sent as a status code in + /// closing handshake by an endpoint. + /// + /// + public enum CloseStatusCode : ushort + { + /// + /// Equivalent to close status 1000. Indicates normal close. + /// + Normal = 1000, + + /// + /// Equivalent to close status 1001. Indicates that an endpoint is + /// going away. + /// + Away = 1001, + + /// + /// Equivalent to close status 1002. Indicates that an endpoint is + /// terminating the connection due to a protocol error. + /// + ProtocolError = 1002, + + /// + /// Equivalent to close status 1003. Indicates that an endpoint is + /// terminating the connection because it has received a type of + /// data that it cannot accept. + /// + UnsupportedData = 1003, + + /// + /// Equivalent to close status 1004. Still undefined. A Reserved value. + /// + Undefined = 1004, + + /// + /// Equivalent to close status 1005. Indicates that no status code was + /// actually present. A Reserved value. + /// + NoStatus = 1005, + + /// + /// Equivalent to close status 1006. Indicates that the connection was + /// closed abnormally. A Reserved value. + /// + Abnormal = 1006, + + /// + /// Equivalent to close status 1007. Indicates that an endpoint is + /// terminating the connection because it has received a message that + /// contains data that is not consistent with the type of the message. + /// + InvalidData = 1007, + + /// + /// Equivalent to close status 1008. Indicates that an endpoint is + /// terminating the connection because it has received a message that + /// violates its policy. + /// + PolicyViolation = 1008, + + /// + /// Equivalent to close status 1009. Indicates that an endpoint is + /// terminating the connection because it has received a message that + /// is too big to process. + /// + TooBig = 1009, + + /// + /// Equivalent to close status 1010. Indicates that a client is + /// terminating the connection because it has expected the server to + /// negotiate one or more extension, but the server did not return + /// them in the handshake response. + /// + MandatoryExtension = 1010, + + /// + /// Equivalent to close status 1011. Indicates that a server is + /// terminating the connection because it has encountered an unexpected + /// condition that prevented it from fulfilling the request. + /// + ServerError = 1011, + + /// + /// Equivalent to close status 1015. Indicates that the connection was + /// closed due to a failure to perform a TLS handshake. A Reserved value. + /// + TlsHandshakeFailure = 1015 + } + + #endregion ö +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/CompressionMethod.cs b/src/WebSocket/WebSocketCore/CompressionMethod.cs index 39e6e2d65..05e9e7659 100644 --- a/src/WebSocket/WebSocketCore/CompressionMethod.cs +++ b/src/WebSocket/WebSocketCore/CompressionMethod.cs @@ -1,4 +1,3 @@ -#region License /* * CompressionMethod.cs * @@ -24,29 +23,33 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; namespace WebSocketCore { - /// - /// Specifies the method for compression. - /// - /// - /// The methods are defined in - /// - /// Compression Extensions for WebSocket. - /// - public enum CompressionMethod : byte - { - /// - /// Specifies no compression. - /// - None, + #region ö + /// - /// Specifies DEFLATE. + /// Specifies the method for compression. /// - Deflate - } -} + /// + /// The methods are defined in + /// + /// Compression Extensions for WebSocket. + /// + public enum CompressionMethod : byte + { + /// + /// Specifies no compression. + /// + None, + + /// + /// Specifies DEFLATE. + /// + Deflate + } + + #endregion ö +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/ErrorEventArgs.cs b/src/WebSocket/WebSocketCore/ErrorEventArgs.cs index accd2b709..cec6cfa53 100644 --- a/src/WebSocket/WebSocketCore/ErrorEventArgs.cs +++ b/src/WebSocket/WebSocketCore/ErrorEventArgs.cs @@ -1,4 +1,3 @@ -#region License /* * ErrorEventArgs.cs * @@ -24,86 +23,83 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Contributors /* * Contributors: * - Frank Razenberg */ -#endregion using System; namespace WebSocketCore { - /// - /// Represents the event data for the event. - /// - /// - /// - /// That event occurs when the gets an error. - /// - /// - /// If you would like to get the error message, you should access - /// the property. - /// - /// - /// And if the error is due to an exception, you can get it by accessing - /// the property. - /// - /// - public class ErrorEventArgs : EventArgs - { - #region Private Fields + /// + /// Represents the event data for the event. + /// + public class ErrorEventArgs : EventArgs + { + #region ֶ - private Exception _exception; - private string _message; + /// + /// Defines the _exception + /// + private Exception _exception; - #endregion + /// + /// Defines the _message + /// + private string _message; - #region Internal Constructors + #endregion ֶ - internal ErrorEventArgs (string message) - : this (message, null) - { - } + #region 캯 - internal ErrorEventArgs (string message, Exception exception) - { - _message = message; - _exception = exception; - } + /// + /// Initializes a new instance of the class. + /// + /// The message + internal ErrorEventArgs(string message) + : this(message, null) + { + } - #endregion + /// + /// Initializes a new instance of the class. + /// + /// The message + /// The exception + internal ErrorEventArgs(string message, Exception exception) + { + _message = message; + _exception = exception; + } - #region Public Properties + #endregion 캯 - /// - /// Gets the exception that caused the error. - /// - /// - /// An instance that represents the cause of - /// the error if it is due to an exception; otherwise, . - /// - public Exception Exception { - get { - return _exception; - } - } + #region - /// - /// Gets the error message. - /// - /// - /// A that represents the error message. - /// - public string Message { - get { - return _message; - } - } + /// + /// Gets the exception that caused the error. + /// + public Exception Exception + { + get + { + return _exception; + } + } + + /// + /// Gets the error message. + /// + public string Message + { + get + { + return _message; + } + } - #endregion - } -} + #endregion + } +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Ext.cs b/src/WebSocket/WebSocketCore/Ext.cs index 9cfd9b122..121ba40b2 100644 --- a/src/WebSocket/WebSocketCore/Ext.cs +++ b/src/WebSocket/WebSocketCore/Ext.cs @@ -1,4 +1,3 @@ -#region License /* * Ext.cs * @@ -34,16 +33,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Contributors /* * Contributors: * - Liryna * - Nikola Kovacevic * - Chris Swiedler */ -#endregion using System; using System.Collections.Generic; @@ -59,1993 +55,2159 @@ namespace WebSocketCore { - /// - /// Provides a set of static methods for websocket-sharp. - /// - public static class Ext - { - #region Private Fields + /// + /// Provides a set of static methods for websocket-sharp. + /// + public static class Ext + { + #region - private static readonly byte[] _last = new byte[] { 0x00 }; - private static readonly int _retry = 5; - private const string _tspecials = "()<>@,;:\\\"/[]?={} \t"; + /// + /// Defines the _tspecials + /// + private const string _tspecials = "()<>@,;:\\\"/[]?={} \t"; - #endregion + #endregion - #region Private Methods + #region ֶ - private static byte[] compress (this byte[] data) - { - if (data.LongLength == 0) - //return new byte[] { 0x00, 0x00, 0x00, 0xff, 0xff }; - return data; + /// + /// Defines the _last + /// + private static readonly byte[] _last = new byte[] { 0x00 }; - using (var input = new MemoryStream (data)) - return input.compressToArray (); - } + /// + /// Defines the _retry + /// + private static readonly int _retry = 5; - private static MemoryStream compress (this Stream stream) - { - var output = new MemoryStream (); - if (stream.Length == 0) - return output; - - stream.Position = 0; - using (var ds = new DeflateStream (output, CompressionMode.Compress, true)) { - stream.CopyTo (ds, 1024); - ds.Close (); // BFINAL set to 1. - output.Write (_last, 0, 1); - output.Position = 0; - - return output; - } - } + #endregion ֶ - private static byte[] compressToArray (this Stream stream) - { - using (var output = stream.compress ()) { - output.Close (); - return output.ToArray (); - } - } + #region - private static byte[] decompress (this byte[] data) - { - if (data.LongLength == 0) - return data; + /// + /// Emits the specified EventHandler<TEventArgs> delegate if it isn't + /// . + /// + /// + /// The eventHandler + /// The sender + /// The e + public static void Emit( + this EventHandler eventHandler, object sender, TEventArgs e) + where TEventArgs : EventArgs + { + if (eventHandler != null) + eventHandler(sender, e); + } - using (var input = new MemoryStream (data)) - return input.decompressToArray (); - } + /// + /// Emits the specified delegate if it isn't . + /// + /// The eventHandler + /// The sender + /// The e + public static void Emit(this EventHandler eventHandler, object sender, EventArgs e) + { + if (eventHandler != null) + eventHandler(sender, e); + } - private static MemoryStream decompress (this Stream stream) - { - var output = new MemoryStream (); - if (stream.Length == 0) - return output; + /// + /// Gets the description of the specified HTTP status . + /// + /// The code + /// The + public static string GetDescription(this HttpStatusCode code) + { + return ((int)code).GetStatusDescription(); + } - stream.Position = 0; - using (var ds = new DeflateStream (stream, CompressionMode.Decompress, true)) { - ds.CopyTo (output, 1024); - output.Position = 0; + /// + /// Gets the description of the specified HTTP status . + /// + /// The code + /// The + public static string GetStatusDescription(this int code) + { + switch (code) + { + case 100: return "Continue"; + case 101: return "Switching Protocols"; + case 102: return "Processing"; + case 200: return "OK"; + case 201: return "Created"; + case 202: return "Accepted"; + case 203: return "Non-Authoritative Information"; + case 204: return "No Content"; + case 205: return "Reset Content"; + case 206: return "Partial Content"; + case 207: return "Multi-Status"; + case 300: return "Multiple Choices"; + case 301: return "Moved Permanently"; + case 302: return "Found"; + case 303: return "See Other"; + case 304: return "Not Modified"; + case 305: return "Use Proxy"; + case 307: return "Temporary Redirect"; + case 400: return "Bad Request"; + case 401: return "Unauthorized"; + case 402: return "Payment Required"; + case 403: return "Forbidden"; + case 404: return "Not Found"; + case 405: return "Method Not Allowed"; + case 406: return "Not Acceptable"; + case 407: return "Proxy Authentication Required"; + case 408: return "Request Timeout"; + case 409: return "Conflict"; + case 410: return "Gone"; + case 411: return "Length Required"; + case 412: return "Precondition Failed"; + case 413: return "Request Entity Too Large"; + case 414: return "Request-Uri Too Long"; + case 415: return "Unsupported Media Type"; + case 416: return "Requested Range Not Satisfiable"; + case 417: return "Expectation Failed"; + case 422: return "Unprocessable Entity"; + case 423: return "Locked"; + case 424: return "Failed Dependency"; + case 500: return "Internal Server Error"; + case 501: return "Not Implemented"; + case 502: return "Bad Gateway"; + case 503: return "Service Unavailable"; + case 504: return "Gateway Timeout"; + case 505: return "Http Version Not Supported"; + case 507: return "Insufficient Storage"; + } - return output; - } - } + return String.Empty; + } - private static byte[] decompressToArray (this Stream stream) - { - using (var output = stream.decompress ()) { - output.Close (); - return output.ToArray (); - } - } + /// + /// Determines whether the specified ushort is in the range of + /// the status code for the WebSocket connection close. + /// + /// The value + /// The + public static bool IsCloseStatusCode(this ushort value) + { + return value > 999 && value < 5000; + } - private static bool isHttpMethod (this string value) - { - return value == "GET" - || value == "HEAD" - || value == "POST" - || value == "PUT" - || value == "DELETE" - || value == "CONNECT" - || value == "OPTIONS" - || value == "TRACE"; - } + /// + /// Determines whether the specified string is enclosed in + /// the specified character. + /// + /// The value + /// The c + /// The + public static bool IsEnclosedIn(this string value, char c) + { + return value != null + && value.Length > 1 + && value[0] == c + && value[value.Length - 1] == c; + } - private static bool isHttpMethod10 (this string value) - { - return value == "GET" - || value == "HEAD" - || value == "POST"; - } + /// + /// Determines whether the specified byte order is host (this computer + /// architecture) byte order. + /// + /// The order + /// The + public static bool IsHostOrder(this ByteOrder order) + { + // true: !(true ^ true) or !(false ^ false) + // false: !(true ^ false) or !(false ^ true) + return !(BitConverter.IsLittleEndian ^ (order == ByteOrder.Little)); + } - private static void times (this ulong n, Action action) - { - for (ulong i = 0; i < n; i++) - action (); - } + /// + /// Determines whether the specified IP address is a local IP address. + /// + /// The address + /// The + public static bool IsLocal(this System.Net.IPAddress address) + { + if (address == null) + throw new ArgumentNullException("address"); + + if (address.Equals(System.Net.IPAddress.Any)) + return true; + + if (address.Equals(System.Net.IPAddress.Loopback)) + return true; + + if (Socket.OSSupportsIPv6) + { + if (address.Equals(System.Net.IPAddress.IPv6Any)) + return true; + + if (address.Equals(System.Net.IPAddress.IPv6Loopback)) + return true; + } - #endregion + var host = System.Net.Dns.GetHostName(); + var addrs = System.Net.Dns.GetHostAddresses(host); + foreach (var addr in addrs) + { + if (address.Equals(addr)) + return true; + } - #region Internal Methods + return false; + } - internal static byte[] Append (this ushort code, string reason) - { - var ret = code.InternalToByteArray (ByteOrder.Big); - if (reason != null && reason.Length > 0) { - var buff = new List (ret); - buff.AddRange (Encoding.UTF8.GetBytes (reason)); - ret = buff.ToArray (); - } - - return ret; - } + /// + /// Determines whether the specified string is or + /// an empty string. + /// + /// The value + /// The + public static bool IsNullOrEmpty(this string value) + { + return value == null || value.Length == 0; + } - internal static void Close (this HttpListenerResponse response, HttpStatusCode code) - { - response.StatusCode = (int) code; - response.OutputStream.Close (); - } + /// + /// Determines whether the specified string is a predefined scheme. + /// + /// The value + /// The + public static bool IsPredefinedScheme(this string value) + { + if (value == null || value.Length < 2) + return false; + + var c = value[0]; + if (c == 'h') + return value == "http" || value == "https"; + + if (c == 'w') + return value == "ws" || value == "wss"; + + if (c == 'f') + return value == "file" || value == "ftp"; + + if (c == 'g') + return value == "gopher"; + + if (c == 'm') + return value == "mailto"; + + if (c == 'n') + { + c = value[1]; + return c == 'e' + ? value == "news" || value == "net.pipe" || value == "net.tcp" + : value == "nntp"; + } - internal static void CloseWithAuthChallenge ( - this HttpListenerResponse response, string challenge) - { - response.Headers.InternalSet ("WWW-Authenticate", challenge, true); - response.Close (HttpStatusCode.Unauthorized); - } + return false; + } - internal static byte[] Compress (this byte[] data, CompressionMethod method) - { - return method == CompressionMethod.Deflate - ? data.compress () - : data; - } + /// + /// Determines whether the specified string is a URI string. + /// + /// The value + /// The + public static bool MaybeUri(this string value) + { + if (value == null || value.Length == 0) + return false; + + var idx = value.IndexOf(':'); + if (idx == -1) + return false; + + if (idx >= 10) + return false; + + var schm = value.Substring(0, idx); + return schm.IsPredefinedScheme(); + } - internal static Stream Compress (this Stream stream, CompressionMethod method) - { - return method == CompressionMethod.Deflate - ? stream.compress () - : stream; - } + /// + /// Retrieves a sub-array from the specified . A sub-array starts at + /// the specified element position in . + /// + /// + /// The array + /// The startIndex + /// The length + /// The + public static T[] SubArray(this T[] array, int startIndex, int length) + { + int len; + if (array == null || (len = array.Length) == 0) + return new T[0]; + + if (startIndex < 0 || length <= 0 || startIndex + length > len) + return new T[0]; + + if (startIndex == 0 && length == len) + return array; + + var subArray = new T[length]; + Array.Copy(array, startIndex, subArray, 0, length); + + return subArray; + } - internal static byte[] CompressToArray (this Stream stream, CompressionMethod method) - { - return method == CompressionMethod.Deflate - ? stream.compressToArray () - : stream.ToByteArray (); - } + /// + /// Retrieves a sub-array from the specified . A sub-array starts at + /// the specified element position in . + /// + /// + /// The array + /// The startIndex + /// The length + /// The + public static T[] SubArray(this T[] array, long startIndex, long length) + { + long len; + if (array == null || (len = array.LongLength) == 0) + return new T[0]; + + if (startIndex < 0 || length <= 0 || startIndex + length > len) + return new T[0]; + + if (startIndex == 0 && length == len) + return array; + + var subArray = new T[length]; + Array.Copy(array, startIndex, subArray, 0, length); + + return subArray; + } - /// - /// Determines whether the specified string contains any of characters in - /// the specified array of . - /// - /// - /// true if contains any of characters in - /// ; otherwise, false. - /// - /// - /// A to test. - /// - /// - /// An array of that contains one or more characters to - /// seek. - /// - internal static bool Contains (this string value, params char[] anyOf) - { - return anyOf != null && anyOf.Length > 0 - ? value.IndexOfAny (anyOf) > -1 - : false; - } + /// + /// Executes the specified delegate times. + /// + /// The n + /// The action + public static void Times(this int n, Action action) + { + if (n > 0 && action != null) + ((ulong)n).times(action); + } - internal static bool Contains ( - this NameValueCollection collection, string name - ) - { - return collection[name] != null; - } + /// + /// Executes the specified Action<int> delegate times. + /// + /// The n + /// The action + public static void Times(this int n, Action action) + { + if (n > 0 && action != null) + for (int i = 0; i < n; i++) + action(i); + } - internal static bool Contains ( - this NameValueCollection collection, - string name, - string value, - StringComparison comparisonTypeForValue - ) - { - var val = collection[name]; - if (val == null) - return false; + /// + /// Executes the specified delegate times. + /// + /// The n + /// The action + public static void Times(this long n, Action action) + { + if (n > 0 && action != null) + ((ulong)n).times(action); + } - foreach (var elm in val.Split (',')) { - if (elm.Trim ().Equals (value, comparisonTypeForValue)) - return true; - } + /// + /// Executes the specified Action<long> delegate times. + /// + /// The n + /// The action + public static void Times(this long n, Action action) + { + if (n > 0 && action != null) + for (long i = 0; i < n; i++) + action(i); + } - return false; - } + /// + /// Executes the specified delegate times. + /// + /// The n + /// The action + public static void Times(this uint n, Action action) + { + if (n > 0 && action != null) + ((ulong)n).times(action); + } - internal static bool Contains ( - this IEnumerable source, Func condition - ) - { - foreach (T elm in source) { - if (condition (elm)) - return true; - } + /// + /// Executes the specified Action<uint> delegate times. + /// + /// The n + /// The action + public static void Times(this uint n, Action action) + { + if (n > 0 && action != null) + for (uint i = 0; i < n; i++) + action(i); + } - return false; - } + /// + /// Executes the specified delegate times. + /// + /// The n + /// The action + public static void Times(this ulong n, Action action) + { + if (n > 0 && action != null) + n.times(action); + } - internal static bool ContainsTwice (this string[] values) - { - var len = values.Length; - var end = len - 1; + /// + /// Executes the specified Action<ulong> delegate times. + /// + /// The n + /// The action + public static void Times(this ulong n, Action action) + { + if (n > 0 && action != null) + for (ulong i = 0; i < n; i++) + action(i); + } - Func seek = null; - seek = idx => { - if (idx == end) - return false; + /// + /// Converts the specified array of to the specified type data. + /// + /// + /// The source + /// The sourceOrder + /// The + public static T To(this byte[] source, ByteOrder sourceOrder) + where T : struct + { + if (source == null) + throw new ArgumentNullException("source"); + + if (source.Length == 0) + return default(T); + + var type = typeof(T); + var buff = source.ToHostOrder(sourceOrder); + + return type == typeof(Boolean) + ? (T)(object)BitConverter.ToBoolean(buff, 0) + : type == typeof(Char) + ? (T)(object)BitConverter.ToChar(buff, 0) + : type == typeof(Double) + ? (T)(object)BitConverter.ToDouble(buff, 0) + : type == typeof(Int16) + ? (T)(object)BitConverter.ToInt16(buff, 0) + : type == typeof(Int32) + ? (T)(object)BitConverter.ToInt32(buff, 0) + : type == typeof(Int64) + ? (T)(object)BitConverter.ToInt64(buff, 0) + : type == typeof(Single) + ? (T)(object)BitConverter.ToSingle(buff, 0) + : type == typeof(UInt16) + ? (T)(object)BitConverter.ToUInt16(buff, 0) + : type == typeof(UInt32) + ? (T)(object)BitConverter.ToUInt32(buff, 0) + : type == typeof(UInt64) + ? (T)(object)BitConverter.ToUInt64(buff, 0) + : default(T); + } - var val = values[idx]; - for (var i = idx + 1; i < len; i++) { - if (values[i] == val) - return true; - } + /// + /// Converts the specified to an array of . + /// + /// + /// The value + /// The order + /// The + public static byte[] ToByteArray(this T value, ByteOrder order) + where T : struct + { + var type = typeof(T); + var bytes = type == typeof(Boolean) + ? BitConverter.GetBytes((Boolean)(object)value) + : type == typeof(Byte) + ? new byte[] { (Byte)(object)value } + : type == typeof(Char) + ? BitConverter.GetBytes((Char)(object)value) + : type == typeof(Double) + ? BitConverter.GetBytes((Double)(object)value) + : type == typeof(Int16) + ? BitConverter.GetBytes((Int16)(object)value) + : type == typeof(Int32) + ? BitConverter.GetBytes((Int32)(object)value) + : type == typeof(Int64) + ? BitConverter.GetBytes((Int64)(object)value) + : type == typeof(Single) + ? BitConverter.GetBytes((Single)(object)value) + : type == typeof(UInt16) + ? BitConverter.GetBytes((UInt16)(object)value) + : type == typeof(UInt32) + ? BitConverter.GetBytes((UInt32)(object)value) + : type == typeof(UInt64) + ? BitConverter.GetBytes((UInt64)(object)value) + : WebSocket.EmptyBytes; + + if (bytes.Length > 1 && !order.IsHostOrder()) + Array.Reverse(bytes); + + return bytes; + } - return seek (++idx); - }; + /// + /// Converts the order of elements in the specified byte array to + /// host (this computer architecture) byte order. + /// + /// The source + /// The sourceOrder + /// The + public static byte[] ToHostOrder(this byte[] source, ByteOrder sourceOrder) + { + if (source == null) + throw new ArgumentNullException("source"); + + if (source.Length < 2) + return source; + + return !sourceOrder.IsHostOrder() ? source.Reverse() : source; + } - return seek (0); - } + /// + /// Converts the specified array to a . + /// + /// + /// The array + /// The separator + /// The + public static string ToString(this T[] array, string separator) + { + if (array == null) + throw new ArgumentNullException("array"); - internal static T[] Copy (this T[] source, int length) - { - var dest = new T[length]; - Array.Copy (source, 0, dest, 0, length); + var len = array.Length; + if (len == 0) + return String.Empty; - return dest; - } + if (separator == null) + separator = String.Empty; - internal static T[] Copy (this T[] source, long length) - { - var dest = new T[length]; - Array.Copy (source, 0, dest, 0, length); + var buff = new StringBuilder(64); - return dest; - } + for (var i = 0; i < len - 1; i++) + buff.AppendFormat("{0}{1}", array[i], separator); - internal static void CopyTo (this Stream source, Stream destination, int bufferLength) - { - var buff = new byte[bufferLength]; - var nread = 0; - while ((nread = source.Read (buff, 0, bufferLength)) > 0) - destination.Write (buff, 0, nread); - } + buff.Append(array[len - 1].ToString()); + return buff.ToString(); + } - internal static void CopyToAsync ( - this Stream source, - Stream destination, - int bufferLength, - Action completed, - Action error) - { - var buff = new byte[bufferLength]; - - AsyncCallback callback = null; - callback = ar => { - try { - var nread = source.EndRead (ar); - if (nread <= 0) { - if (completed != null) - completed (); - - return; - } - - destination.Write (buff, 0, nread); - source.BeginRead (buff, 0, bufferLength, callback, null); - } - catch (Exception ex) { - if (error != null) - error (ex); - } - }; - - try { - source.BeginRead (buff, 0, bufferLength, callback, null); - } - catch (Exception ex) { - if (error != null) - error (ex); - } - } + /// + /// Converts the specified string to a . + /// + /// The value + /// The + public static Uri ToUri(this string value) + { + Uri ret; + Uri.TryCreate( + value, value.MaybeUri() ? UriKind.Absolute : UriKind.Relative, out ret + ); + + return ret; + } - internal static byte[] Decompress (this byte[] data, CompressionMethod method) - { - return method == CompressionMethod.Deflate - ? data.decompress () - : data; - } + /// + /// URL-decodes the specified . + /// + /// The value + /// The + public static string UrlDecode(this string value) + { + return value != null && value.Length > 0 + ? HttpUtility.UrlDecode(value) + : value; + } - internal static Stream Decompress (this Stream stream, CompressionMethod method) - { - return method == CompressionMethod.Deflate - ? stream.decompress () - : stream; - } + /// + /// URL-encodes the specified . + /// + /// The value + /// The + public static string UrlEncode(this string value) + { + return value != null && value.Length > 0 + ? HttpUtility.UrlEncode(value) + : value; + } - internal static byte[] DecompressToArray (this Stream stream, CompressionMethod method) - { - return method == CompressionMethod.Deflate - ? stream.decompressToArray () - : stream.ToByteArray (); - } + /// + /// Writes and sends the specified data with the specified + /// . + /// + /// The response + /// The content + public static void WriteContent(this HttpListenerResponse response, byte[] content) + { + if (response == null) + throw new ArgumentNullException("response"); + + if (content == null) + throw new ArgumentNullException("content"); + + var len = content.LongLength; + if (len == 0) + { + response.Close(); + return; + } - /// - /// Determines whether the specified equals the specified , - /// and invokes the specified Action<int> delegate at the same time. - /// - /// - /// true if equals ; - /// otherwise, false. - /// - /// - /// An to compare. - /// - /// - /// A to compare. - /// - /// - /// An Action<int> delegate that references the method(s) called - /// at the same time as comparing. An parameter to pass to - /// the method(s) is . - /// - internal static bool EqualsWith (this int value, char c, Action action) - { - action (value); - return value == c - 0; - } + response.ContentLength64 = len; + var output = response.OutputStream; + if (len <= Int32.MaxValue) + output.Write(content, 0, (int)len); + else + output.WriteBytes(content, 1024); - /// - /// Gets the absolute path from the specified . - /// - /// - /// A that represents the absolute path if it's successfully found; - /// otherwise, . - /// - /// - /// A that represents the URI to get the absolute path from. - /// - internal static string GetAbsolutePath (this Uri uri) - { - if (uri.IsAbsoluteUri) - return uri.AbsolutePath; + output.Close(); + } - var original = uri.OriginalString; - if (original[0] != '/') - return null; + /// + /// The Append + /// + /// The code + /// The reason + /// The + internal static byte[] Append(this ushort code, string reason) + { + var ret = code.InternalToByteArray(ByteOrder.Big); + if (reason != null && reason.Length > 0) + { + var buff = new List(ret); + buff.AddRange(Encoding.UTF8.GetBytes(reason)); + ret = buff.ToArray(); + } - var idx = original.IndexOfAny (new[] { '?', '#' }); - return idx > 0 ? original.Substring (0, idx) : original; - } + return ret; + } - internal static CookieCollection GetCookies ( - this NameValueCollection headers, bool response - ) - { - var val = headers[response ? "Set-Cookie" : "Cookie"]; - return val != null - ? CookieCollection.Parse (val, response) - : new CookieCollection (); - } + /// + /// The Close + /// + /// The response + /// The code + internal static void Close(this HttpListenerResponse response, HttpStatusCode code) + { + response.StatusCode = (int)code; + response.OutputStream.Close(); + } - internal static string GetDnsSafeHost (this Uri uri, bool bracketIPv6) - { - return bracketIPv6 && uri.HostNameType == UriHostNameType.IPv6 - ? uri.Host - : uri.DnsSafeHost; - } + /// + /// The CloseWithAuthChallenge + /// + /// The response + /// The challenge + internal static void CloseWithAuthChallenge( + this HttpListenerResponse response, string challenge) + { + response.Headers.InternalSet("WWW-Authenticate", challenge, true); + response.Close(HttpStatusCode.Unauthorized); + } - internal static string GetMessage (this CloseStatusCode code) - { - return code == CloseStatusCode.ProtocolError - ? "A WebSocket protocol error has occurred." - : code == CloseStatusCode.UnsupportedData - ? "Unsupported data has been received." - : code == CloseStatusCode.Abnormal - ? "An exception has occurred." - : code == CloseStatusCode.InvalidData - ? "Invalid data has been received." - : code == CloseStatusCode.PolicyViolation - ? "A policy violation has occurred." - : code == CloseStatusCode.TooBig - ? "A too big message has been received." - : code == CloseStatusCode.MandatoryExtension - ? "WebSocket client didn't receive expected extension(s)." - : code == CloseStatusCode.ServerError - ? "WebSocket server got an internal error." - : code == CloseStatusCode.TlsHandshakeFailure - ? "An error has occurred during a TLS handshake." - : String.Empty; - } + /// + /// The Compress + /// + /// The data + /// The method + /// The + internal static byte[] Compress(this byte[] data, CompressionMethod method) + { + return method == CompressionMethod.Deflate + ? data.compress() + : data; + } - /// - /// Gets the name from the specified string that contains a pair of - /// name and value separated by a character. - /// - /// - /// - /// A that represents the name. - /// - /// - /// if the name is not present. - /// - /// - /// - /// A that contains a pair of name and value. - /// - /// - /// A used to separate name and value. - /// - internal static string GetName (this string nameAndValue, char separator) - { - var idx = nameAndValue.IndexOf (separator); - return idx > 0 ? nameAndValue.Substring (0, idx).Trim () : null; - } + /// + /// The Compress + /// + /// The stream + /// The method + /// The + internal static Stream Compress(this Stream stream, CompressionMethod method) + { + return method == CompressionMethod.Deflate + ? stream.compress() + : stream; + } - /// - /// Gets the value from the specified string that contains a pair of - /// name and value separated by a character. - /// - /// - /// - /// A that represents the value. - /// - /// - /// if the value is not present. - /// - /// - /// - /// A that contains a pair of name and value. - /// - /// - /// A used to separate name and value. - /// - internal static string GetValue (this string nameAndValue, char separator) - { - return nameAndValue.GetValue (separator, false); - } + /// + /// The CompressToArray + /// + /// The stream + /// The method + /// The + internal static byte[] CompressToArray(this Stream stream, CompressionMethod method) + { + return method == CompressionMethod.Deflate + ? stream.compressToArray() + : stream.ToByteArray(); + } - /// - /// Gets the value from the specified string that contains a pair of - /// name and value separated by a character. - /// - /// - /// - /// A that represents the value. - /// - /// - /// if the value is not present. - /// - /// - /// - /// A that contains a pair of name and value. - /// - /// - /// A used to separate name and value. - /// - /// - /// A : true if unquotes the value; otherwise, - /// false. - /// - internal static string GetValue ( - this string nameAndValue, char separator, bool unquote + /// + /// The Contains + /// + /// + /// The source + /// The condition + /// The + internal static bool Contains( + this IEnumerable source, Func condition ) - { - var idx = nameAndValue.IndexOf (separator); - if (idx < 0 || idx == nameAndValue.Length - 1) - return null; + { + foreach (T elm in source) + { + if (condition(elm)) + return true; + } - var val = nameAndValue.Substring (idx + 1).Trim (); - return unquote ? val.Unquote () : val; - } + return false; + } - internal static byte[] InternalToByteArray (this ushort value, ByteOrder order) - { - var bytes = BitConverter.GetBytes (value); - if (!order.IsHostOrder ()) - Array.Reverse (bytes); + /// + /// The Contains + /// + /// The collection + /// The name + /// The + internal static bool Contains( + this NameValueCollection collection, string name + ) + { + return collection[name] != null; + } - return bytes; - } + /// + /// The Contains + /// + /// The collection + /// The name + /// The value + /// The comparisonTypeForValue + /// The + internal static bool Contains( + this NameValueCollection collection, + string name, + string value, + StringComparison comparisonTypeForValue + ) + { + var val = collection[name]; + if (val == null) + return false; + + foreach (var elm in val.Split(',')) + { + if (elm.Trim().Equals(value, comparisonTypeForValue)) + return true; + } - internal static byte[] InternalToByteArray (this ulong value, ByteOrder order) - { - var bytes = BitConverter.GetBytes (value); - if (!order.IsHostOrder ()) - Array.Reverse (bytes); + return false; + } - return bytes; - } + /// + /// Determines whether the specified string contains any of characters in + /// the specified array of . + /// + /// The value + /// The anyOf + /// The + internal static bool Contains(this string value, params char[] anyOf) + { + return anyOf != null && anyOf.Length > 0 + ? value.IndexOfAny(anyOf) > -1 + : false; + } - internal static bool IsCompressionExtension ( - this string value, CompressionMethod method - ) - { - return value.StartsWith (method.ToExtensionString ()); - } + /// + /// The ContainsTwice + /// + /// The values + /// The + internal static bool ContainsTwice(this string[] values) + { + var len = values.Length; + var end = len - 1; + + Func seek = null; + seek = idx => + { + if (idx == end) + return false; + + var val = values[idx]; + for (var i = idx + 1; i < len; i++) + { + if (values[i] == val) + return true; + } - internal static bool IsControl (this byte opcode) - { - return opcode > 0x7 && opcode < 0x10; - } + return seek(++idx); + }; - internal static bool IsControl (this Opcode opcode) - { - return opcode >= Opcode.Close; - } + return seek(0); + } - internal static bool IsData (this byte opcode) - { - return opcode == 0x1 || opcode == 0x2; - } + /// + /// The Copy + /// + /// + /// The source + /// The length + /// The + internal static T[] Copy(this T[] source, int length) + { + var dest = new T[length]; + Array.Copy(source, 0, dest, 0, length); + + return dest; + } - internal static bool IsData (this Opcode opcode) - { - return opcode == Opcode.Text || opcode == Opcode.Binary; - } + /// + /// The Copy + /// + /// + /// The source + /// The length + /// The + internal static T[] Copy(this T[] source, long length) + { + var dest = new T[length]; + Array.Copy(source, 0, dest, 0, length); + + return dest; + } - internal static bool IsHttpMethod (this string value, Version version) - { - return version == HttpVersion.Version10 - ? value.isHttpMethod10 () - : value.isHttpMethod (); - } + /// + /// The CopyTo + /// + /// The source + /// The destination + /// The bufferLength + internal static void CopyTo(this Stream source, Stream destination, int bufferLength) + { + var buff = new byte[bufferLength]; + var nread = 0; + while ((nread = source.Read(buff, 0, bufferLength)) > 0) + destination.Write(buff, 0, nread); + } - internal static bool IsPortNumber (this int value) - { - return value > 0 && value < 65536; - } + /// + /// The CopyToAsync + /// + /// The source + /// The destination + /// The bufferLength + /// The completed + /// The error + internal static void CopyToAsync( + this Stream source, + Stream destination, + int bufferLength, + Action completed, + Action error) + { + var buff = new byte[bufferLength]; + + AsyncCallback callback = null; + callback = ar => + { + try + { + var nread = source.EndRead(ar); + if (nread <= 0) + { + if (completed != null) + completed(); + + return; + } + + destination.Write(buff, 0, nread); + source.BeginRead(buff, 0, bufferLength, callback, null); + } + catch (Exception ex) + { + if (error != null) + error(ex); + } + }; - internal static bool IsReserved (this ushort code) - { - return code == 1004 - || code == 1005 - || code == 1006 - || code == 1015; - } + try + { + source.BeginRead(buff, 0, bufferLength, callback, null); + } + catch (Exception ex) + { + if (error != null) + error(ex); + } + } - internal static bool IsReserved (this CloseStatusCode code) - { - return code == CloseStatusCode.Undefined - || code == CloseStatusCode.NoStatus - || code == CloseStatusCode.Abnormal - || code == CloseStatusCode.TlsHandshakeFailure; - } + /// + /// The Decompress + /// + /// The data + /// The method + /// The + internal static byte[] Decompress(this byte[] data, CompressionMethod method) + { + return method == CompressionMethod.Deflate + ? data.decompress() + : data; + } - internal static bool IsSupported (this byte opcode) - { - return Enum.IsDefined (typeof (Opcode), opcode); - } + /// + /// The Decompress + /// + /// The stream + /// The method + /// The + internal static Stream Decompress(this Stream stream, CompressionMethod method) + { + return method == CompressionMethod.Deflate + ? stream.decompress() + : stream; + } - internal static bool IsText (this string value) - { - var len = value.Length; + /// + /// The DecompressToArray + /// + /// The stream + /// The method + /// The + internal static byte[] DecompressToArray(this Stream stream, CompressionMethod method) + { + return method == CompressionMethod.Deflate + ? stream.decompressToArray() + : stream.ToByteArray(); + } - for (var i = 0; i < len; i++) { - var c = value[i]; - if (c < 0x20) { - if ("\r\n\t".IndexOf (c) == -1) - return false; + /// + /// Determines whether the specified equals the specified , + /// and invokes the specified Action<int> delegate at the same time. + /// + /// The value + /// The c + /// The action + /// The + internal static bool EqualsWith(this int value, char c, Action action) + { + action(value); + return value == c - 0; + } - if (c == '\n') { - i++; - if (i == len) - break; + /// + /// Gets the absolute path from the specified . + /// + /// The uri + /// The + internal static string GetAbsolutePath(this Uri uri) + { + if (uri.IsAbsoluteUri) + return uri.AbsolutePath; + + var original = uri.OriginalString; + if (original[0] != '/') + return null; + + var idx = original.IndexOfAny(new[] { '?', '#' }); + return idx > 0 ? original.Substring(0, idx) : original; + } - c = value[i]; - if (" \t".IndexOf (c) == -1) - return false; - } + /// + /// The GetCookies + /// + /// The headers + /// The response + /// The + internal static CookieCollection GetCookies( + this NameValueCollection headers, bool response + ) + { + var val = headers[response ? "Set-Cookie" : "Cookie"]; + return val != null + ? CookieCollection.Parse(val, response) + : new CookieCollection(); + } - continue; + /// + /// The GetDnsSafeHost + /// + /// The uri + /// The bracketIPv6 + /// The + internal static string GetDnsSafeHost(this Uri uri, bool bracketIPv6) + { + return bracketIPv6 && uri.HostNameType == UriHostNameType.IPv6 + ? uri.Host + : uri.DnsSafeHost; } - if (c == 0x7f) - return false; - } + /// + /// The GetMessage + /// + /// The code + /// The + internal static string GetMessage(this CloseStatusCode code) + { + return code == CloseStatusCode.ProtocolError + ? "A WebSocket protocol error has occurred." + : code == CloseStatusCode.UnsupportedData + ? "Unsupported data has been received." + : code == CloseStatusCode.Abnormal + ? "An exception has occurred." + : code == CloseStatusCode.InvalidData + ? "Invalid data has been received." + : code == CloseStatusCode.PolicyViolation + ? "A policy violation has occurred." + : code == CloseStatusCode.TooBig + ? "A too big message has been received." + : code == CloseStatusCode.MandatoryExtension + ? "WebSocket client didn't receive expected extension(s)." + : code == CloseStatusCode.ServerError + ? "WebSocket server got an internal error." + : code == CloseStatusCode.TlsHandshakeFailure + ? "An error has occurred during a TLS handshake." + : String.Empty; + } - return true; - } + /// + /// Gets the name from the specified string that contains a pair of + /// name and value separated by a character. + /// + /// The nameAndValue + /// The separator + /// The + internal static string GetName(this string nameAndValue, char separator) + { + var idx = nameAndValue.IndexOf(separator); + return idx > 0 ? nameAndValue.Substring(0, idx).Trim() : null; + } - internal static bool IsToken (this string value) - { - foreach (var c in value) { - if (c < 0x20) - return false; + /// + /// Gets the value from the specified string that contains a pair of + /// name and value separated by a character. + /// + /// The nameAndValue + /// The separator + /// The unquote + /// The + internal static string GetValue( + this string nameAndValue, char separator, bool unquote + ) + { + var idx = nameAndValue.IndexOf(separator); + if (idx < 0 || idx == nameAndValue.Length - 1) + return null; - if (c >= 0x7f) - return false; + var val = nameAndValue.Substring(idx + 1).Trim(); + return unquote ? val.Unquote() : val; + } - if (_tspecials.IndexOf (c) > -1) - return false; - } + /// + /// Gets the value from the specified string that contains a pair of + /// name and value separated by a character. + /// + /// The nameAndValue + /// The separator + /// The + internal static string GetValue(this string nameAndValue, char separator) + { + return nameAndValue.GetValue(separator, false); + } - return true; - } + /// + /// The InternalToByteArray + /// + /// The value + /// The order + /// The + internal static byte[] InternalToByteArray(this ulong value, ByteOrder order) + { + var bytes = BitConverter.GetBytes(value); + if (!order.IsHostOrder()) + Array.Reverse(bytes); + + return bytes; + } - internal static bool KeepsAlive ( - this NameValueCollection headers, Version version + /// + /// The InternalToByteArray + /// + /// The value + /// The order + /// The + internal static byte[] InternalToByteArray(this ushort value, ByteOrder order) + { + var bytes = BitConverter.GetBytes(value); + if (!order.IsHostOrder()) + Array.Reverse(bytes); + + return bytes; + } + + /// + /// The IsCompressionExtension + /// + /// The value + /// The method + /// The + internal static bool IsCompressionExtension( + this string value, CompressionMethod method ) - { - var comparison = StringComparison.OrdinalIgnoreCase; - return version < HttpVersion.Version11 - ? headers.Contains ("Connection", "keep-alive", comparison) - : !headers.Contains ("Connection", "close", comparison); - } + { + return value.StartsWith(method.ToExtensionString()); + } - internal static string Quote (this string value) - { - return String.Format ("\"{0}\"", value.Replace ("\"", "\\\"")); - } + /// + /// The IsControl + /// + /// The opcode + /// The + internal static bool IsControl(this byte opcode) + { + return opcode > 0x7 && opcode < 0x10; + } - internal static byte[] ReadBytes (this Stream stream, int length) - { - var buff = new byte[length]; - var offset = 0; - try { - var nread = 0; - while (length > 0) { - nread = stream.Read (buff, offset, length); - if (nread == 0) - break; - - offset += nread; - length -= nread; - } - } - catch { - } - - return buff.SubArray (0, offset); - } + /// + /// The IsControl + /// + /// The opcode + /// The + internal static bool IsControl(this Opcode opcode) + { + return opcode >= Opcode.Close; + } - internal static byte[] ReadBytes (this Stream stream, long length, int bufferLength) - { - using (var dest = new MemoryStream ()) { - try { - var buff = new byte[bufferLength]; - var nread = 0; - while (length > 0) { - if (length < bufferLength) - bufferLength = (int) length; + /// + /// The IsData + /// + /// The opcode + /// The + internal static bool IsData(this byte opcode) + { + return opcode == 0x1 || opcode == 0x2; + } - nread = stream.Read (buff, 0, bufferLength); - if (nread == 0) - break; + /// + /// The IsData + /// + /// The opcode + /// The + internal static bool IsData(this Opcode opcode) + { + return opcode == Opcode.Text || opcode == Opcode.Binary; + } - dest.Write (buff, 0, nread); - length -= nread; - } + /// + /// The IsHttpMethod + /// + /// The value + /// The version + /// The + internal static bool IsHttpMethod(this string value, Version version) + { + return version == HttpVersion.Version10 + ? value.isHttpMethod10() + : value.isHttpMethod(); } - catch { + + /// + /// The IsPortNumber + /// + /// The value + /// The + internal static bool IsPortNumber(this int value) + { + return value > 0 && value < 65536; } - dest.Close (); - return dest.ToArray (); - } - } + /// + /// The IsReserved + /// + /// The code + /// The + internal static bool IsReserved(this CloseStatusCode code) + { + return code == CloseStatusCode.Undefined + || code == CloseStatusCode.NoStatus + || code == CloseStatusCode.Abnormal + || code == CloseStatusCode.TlsHandshakeFailure; + } - internal static void ReadBytesAsync ( - this Stream stream, int length, Action completed, Action error - ) - { - var buff = new byte[length]; - var offset = 0; - var retry = 0; - - AsyncCallback callback = null; - callback = - ar => { - try { - var nread = stream.EndRead (ar); - if (nread == 0 && retry < _retry) { - retry++; - stream.BeginRead (buff, offset, length, callback, null); - - return; - } + /// + /// The IsReserved + /// + /// The code + /// The + internal static bool IsReserved(this ushort code) + { + return code == 1004 + || code == 1005 + || code == 1006 + || code == 1015; + } + + /// + /// The IsSupported + /// + /// The opcode + /// The + internal static bool IsSupported(this byte opcode) + { + return Enum.IsDefined(typeof(Opcode), opcode); + } - if (nread == 0 || nread == length) { - if (completed != null) - completed (buff.SubArray (0, offset + nread)); + /// + /// The IsText + /// + /// The value + /// The + internal static bool IsText(this string value) + { + var len = value.Length; + + for (var i = 0; i < len; i++) + { + var c = value[i]; + if (c < 0x20) + { + if ("\r\n\t".IndexOf(c) == -1) + return false; + + if (c == '\n') + { + i++; + if (i == len) + break; + + c = value[i]; + if (" \t".IndexOf(c) == -1) + return false; + } + + continue; + } - return; + if (c == 0x7f) + return false; } - retry = 0; - - offset += nread; - length -= nread; - - stream.BeginRead (buff, offset, length, callback, null); - } - catch (Exception ex) { - if (error != null) - error (ex); - } - }; - - try { - stream.BeginRead (buff, offset, length, callback, null); - } - catch (Exception ex) { - if (error != null) - error (ex); - } - } + return true; + } - internal static void ReadBytesAsync ( - this Stream stream, - long length, - int bufferLength, - Action completed, - Action error + /// + /// The IsToken + /// + /// The value + /// The + internal static bool IsToken(this string value) + { + foreach (var c in value) + { + if (c < 0x20) + return false; + + if (c >= 0x7f) + return false; + + if (_tspecials.IndexOf(c) > -1) + return false; + } + + return true; + } + + /// + /// The KeepsAlive + /// + /// The headers + /// The version + /// The + internal static bool KeepsAlive( + this NameValueCollection headers, Version version ) - { - var dest = new MemoryStream (); - var buff = new byte[bufferLength]; - var retry = 0; - - Action read = null; - read = - len => { - if (len < bufferLength) - bufferLength = (int) len; - - stream.BeginRead ( - buff, - 0, - bufferLength, - ar => { - try { - var nread = stream.EndRead (ar); - if (nread > 0) - dest.Write (buff, 0, nread); - - if (nread == 0 && retry < _retry) { - retry++; - read (len); - - return; - } + { + var comparison = StringComparison.OrdinalIgnoreCase; + return version < HttpVersion.Version11 + ? headers.Contains("Connection", "keep-alive", comparison) + : !headers.Contains("Connection", "close", comparison); + } - if (nread == 0 || nread == len) { - if (completed != null) { - dest.Close (); - completed (dest.ToArray ()); - } + /// + /// The Quote + /// + /// The value + /// The + internal static string Quote(this string value) + { + return String.Format("\"{0}\"", value.Replace("\"", "\\\"")); + } - dest.Dispose (); - return; + /// + /// The ReadBytes + /// + /// The stream + /// The length + /// The + internal static byte[] ReadBytes(this Stream stream, int length) + { + var buff = new byte[length]; + var offset = 0; + try + { + var nread = 0; + while (length > 0) + { + nread = stream.Read(buff, offset, length); + if (nread == 0) + break; + + offset += nread; + length -= nread; } + } + catch + { + } - retry = 0; - read (len - nread); - } - catch (Exception ex) { - dest.Dispose (); - if (error != null) - error (ex); - } - }, - null - ); - }; - - try { - read (length); - } - catch (Exception ex) { - dest.Dispose (); - if (error != null) - error (ex); - } - } - - internal static T[] Reverse (this T[] array) - { - var len = array.Length; - var ret = new T[len]; + return buff.SubArray(0, offset); + } - var end = len - 1; - for (var i = 0; i <= end; i++) - ret[i] = array[end - i]; + /// + /// The ReadBytes + /// + /// The stream + /// The length + /// The bufferLength + /// The + internal static byte[] ReadBytes(this Stream stream, long length, int bufferLength) + { + using (var dest = new MemoryStream()) + { + try + { + var buff = new byte[bufferLength]; + var nread = 0; + while (length > 0) + { + if (length < bufferLength) + bufferLength = (int)length; + + nread = stream.Read(buff, 0, bufferLength); + if (nread == 0) + break; + + dest.Write(buff, 0, nread); + length -= nread; + } + } + catch + { + } - return ret; - } + dest.Close(); + return dest.ToArray(); + } + } - internal static IEnumerable SplitHeaderValue ( - this string value, params char[] separators + /// + /// The ReadBytesAsync + /// + /// The stream + /// The length + /// The completed + /// The error + internal static void ReadBytesAsync( + this Stream stream, int length, Action completed, Action error ) - { - var len = value.Length; - - var buff = new StringBuilder (32); - var end = len - 1; - var escaped = false; - var quoted = false; - - for (var i = 0; i <= end; i++) { - var c = value[i]; - buff.Append (c); + { + var buff = new byte[length]; + var offset = 0; + var retry = 0; + + AsyncCallback callback = null; + callback = + ar => + { + try + { + var nread = stream.EndRead(ar); + if (nread == 0 && retry < _retry) + { + retry++; + stream.BeginRead(buff, offset, length, callback, null); + + return; + } + + if (nread == 0 || nread == length) + { + if (completed != null) + completed(buff.SubArray(0, offset + nread)); + + return; + } + + retry = 0; + + offset += nread; + length -= nread; + + stream.BeginRead(buff, offset, length, callback, null); + } + catch (Exception ex) + { + if (error != null) + error(ex); + } + }; - if (c == '"') { - if (escaped) { - escaped = false; - continue; - } + try + { + stream.BeginRead(buff, offset, length, callback, null); + } + catch (Exception ex) + { + if (error != null) + error(ex); + } + } - quoted = !quoted; - continue; + /// + /// The ReadBytesAsync + /// + /// The stream + /// The length + /// The bufferLength + /// The completed + /// The error + internal static void ReadBytesAsync( + this Stream stream, + long length, + int bufferLength, + Action completed, + Action error + ) + { + var dest = new MemoryStream(); + var buff = new byte[bufferLength]; + var retry = 0; + + Action read = null; + read = + len => + { + if (len < bufferLength) + bufferLength = (int)len; + + stream.BeginRead( + buff, + 0, + bufferLength, + ar => + { + try + { + var nread = stream.EndRead(ar); + if (nread > 0) + dest.Write(buff, 0, nread); + + if (nread == 0 && retry < _retry) + { + retry++; + read(len); + + return; + } + + if (nread == 0 || nread == len) + { + if (completed != null) + { + dest.Close(); + completed(dest.ToArray()); + } + + dest.Dispose(); + return; + } + + retry = 0; + read(len - nread); + } + catch (Exception ex) + { + dest.Dispose(); + if (error != null) + error(ex); + } + }, + null + ); + }; + + try + { + read(length); + } + catch (Exception ex) + { + dest.Dispose(); + if (error != null) + error(ex); + } } - if (c == '\\') { - if (i == end) - break; + /// + /// The Reverse + /// + /// + /// The array + /// The + internal static T[] Reverse(this T[] array) + { + var len = array.Length; + var ret = new T[len]; + + var end = len - 1; + for (var i = 0; i <= end; i++) + ret[i] = array[end - i]; + + return ret; + } - if (value[i + 1] == '"') - escaped = true; + /// + /// The SplitHeaderValue + /// + /// The value + /// The separators + /// The + internal static IEnumerable SplitHeaderValue( + this string value, params char[] separators + ) + { + var len = value.Length; + + var buff = new StringBuilder(32); + var end = len - 1; + var escaped = false; + var quoted = false; + + for (var i = 0; i <= end; i++) + { + var c = value[i]; + buff.Append(c); + + if (c == '"') + { + if (escaped) + { + escaped = false; + continue; + } + + quoted = !quoted; + continue; + } - continue; - } + if (c == '\\') + { + if (i == end) + break; - if (Array.IndexOf (separators, c) > -1) { - if (quoted) - continue; + if (value[i + 1] == '"') + escaped = true; - buff.Length -= 1; - yield return buff.ToString (); + continue; + } - buff.Length = 0; - continue; - } - } + if (Array.IndexOf(separators, c) > -1) + { + if (quoted) + continue; - yield return buff.ToString (); - } + buff.Length -= 1; + yield return buff.ToString(); - internal static byte[] ToByteArray (this Stream stream) - { - using (var output = new MemoryStream ()) { - stream.Position = 0; - stream.CopyTo (output, 1024); - output.Close (); + buff.Length = 0; + continue; + } + } - return output.ToArray (); - } - } + yield return buff.ToString(); + } - internal static CompressionMethod ToCompressionMethod (this string value) - { - foreach (CompressionMethod method in Enum.GetValues (typeof (CompressionMethod))) - if (method.ToExtensionString () == value) - return method; + /// + /// The ToByteArray + /// + /// The stream + /// The + internal static byte[] ToByteArray(this Stream stream) + { + using (var output = new MemoryStream()) + { + stream.Position = 0; + stream.CopyTo(output, 1024); + output.Close(); + + return output.ToArray(); + } + } - return CompressionMethod.None; - } + /// + /// The ToCompressionMethod + /// + /// The value + /// The + internal static CompressionMethod ToCompressionMethod(this string value) + { + foreach (CompressionMethod method in Enum.GetValues(typeof(CompressionMethod))) + if (method.ToExtensionString() == value) + return method; + + return CompressionMethod.None; + } - internal static string ToExtensionString ( + /// + /// The ToExtensionString + /// + /// The method + /// The parameters + /// The + internal static string ToExtensionString( this CompressionMethod method, params string[] parameters ) - { - if (method == CompressionMethod.None) - return String.Empty; + { + if (method == CompressionMethod.None) + return String.Empty; - var name = String.Format ( - "permessage-{0}", method.ToString ().ToLower () - ); + var name = String.Format( + "permessage-{0}", method.ToString().ToLower() + ); - return parameters != null && parameters.Length > 0 - ? String.Format ("{0}; {1}", name, parameters.ToString ("; ")) - : name; - } + return parameters != null && parameters.Length > 0 + ? String.Format("{0}; {1}", name, parameters.ToString("; ")) + : name; + } - internal static System.Net.IPAddress ToIPAddress (this string value) - { - if (value == null || value.Length == 0) - return null; - - System.Net.IPAddress addr; - if (System.Net.IPAddress.TryParse (value, out addr)) - return addr; - - try { - var addrs = System.Net.Dns.GetHostAddresses (value); - return addrs[0]; - } - catch { - return null; - } - } + /// + /// The ToIPAddress + /// + /// The value + /// The + internal static System.Net.IPAddress ToIPAddress(this string value) + { + if (value == null || value.Length == 0) + return null; + + System.Net.IPAddress addr; + if (System.Net.IPAddress.TryParse(value, out addr)) + return addr; + + try + { + var addrs = System.Net.Dns.GetHostAddresses(value); + return addrs[0]; + } + catch + { + return null; + } + } - internal static List ToList (this IEnumerable source) - { - return new List (source); - } + /// + /// The ToList + /// + /// + /// The source + /// The + internal static List ToList(this IEnumerable source) + { + return new List(source); + } - internal static string ToString ( + /// + /// The ToString + /// + /// The address + /// The bracketIPv6 + /// The + internal static string ToString( this System.Net.IPAddress address, bool bracketIPv6 ) - { - return bracketIPv6 && address.AddressFamily == AddressFamily.InterNetworkV6 - ? String.Format ("[{0}]", address.ToString ()) - : address.ToString (); - } + { + return bracketIPv6 && address.AddressFamily == AddressFamily.InterNetworkV6 + ? String.Format("[{0}]", address.ToString()) + : address.ToString(); + } - internal static ushort ToUInt16 (this byte[] source, ByteOrder sourceOrder) - { - return BitConverter.ToUInt16 (source.ToHostOrder (sourceOrder), 0); - } + /// + /// The ToUInt16 + /// + /// The source + /// The sourceOrder + /// The + internal static ushort ToUInt16(this byte[] source, ByteOrder sourceOrder) + { + return BitConverter.ToUInt16(source.ToHostOrder(sourceOrder), 0); + } - internal static ulong ToUInt64 (this byte[] source, ByteOrder sourceOrder) - { - return BitConverter.ToUInt64 (source.ToHostOrder (sourceOrder), 0); - } + /// + /// The ToUInt64 + /// + /// The source + /// The sourceOrder + /// The + internal static ulong ToUInt64(this byte[] source, ByteOrder sourceOrder) + { + return BitConverter.ToUInt64(source.ToHostOrder(sourceOrder), 0); + } - internal static IEnumerable Trim (this IEnumerable source) - { - foreach (var elm in source) - yield return elm.Trim (); - } + /// + /// The Trim + /// + /// The source + /// The + internal static IEnumerable Trim(this IEnumerable source) + { + foreach (var elm in source) + yield return elm.Trim(); + } - internal static string TrimSlashFromEnd (this string value) - { - var ret = value.TrimEnd ('/'); - return ret.Length > 0 ? ret : "/"; - } + /// + /// The TrimSlashFromEnd + /// + /// The value + /// The + internal static string TrimSlashFromEnd(this string value) + { + var ret = value.TrimEnd('/'); + return ret.Length > 0 ? ret : "/"; + } - internal static string TrimSlashOrBackslashFromEnd (this string value) - { - var ret = value.TrimEnd ('/', '\\'); - return ret.Length > 0 ? ret : value[0].ToString (); - } + /// + /// The TrimSlashOrBackslashFromEnd + /// + /// The value + /// The + internal static string TrimSlashOrBackslashFromEnd(this string value) + { + var ret = value.TrimEnd('/', '\\'); + return ret.Length > 0 ? ret : value[0].ToString(); + } - internal static bool TryCreateVersion ( + /// + /// The TryCreateVersion + /// + /// The versionString + /// The result + /// The + internal static bool TryCreateVersion( this string versionString, out Version result ) - { - result = null; + { + result = null; - try { - result = new Version (versionString); - } - catch { - return false; - } + try + { + result = new Version(versionString); + } + catch + { + return false; + } - return true; - } + return true; + } - /// - /// Tries to create a new for WebSocket with - /// the specified . - /// - /// - /// true if the was successfully created; - /// otherwise, false. - /// - /// - /// A that represents a WebSocket URL to try. - /// - /// - /// When this method returns, a that - /// represents the WebSocket URL or - /// if is invalid. - /// - /// - /// When this method returns, a that - /// represents an error message or - /// if is valid. - /// - internal static bool TryCreateWebSocketUri ( + /// + /// Tries to create a new for WebSocket with + /// the specified . + /// + /// The uriString + /// The result + /// The message + /// The + internal static bool TryCreateWebSocketUri( this string uriString, out Uri result, out string message ) - { - result = null; - message = null; - - var uri = uriString.ToUri (); - if (uri == null) { - message = "An invalid URI string."; - return false; - } - - if (!uri.IsAbsoluteUri) { - message = "A relative URI."; - return false; - } - - var schm = uri.Scheme; - if (!(schm == "ws" || schm == "wss")) { - message = "The scheme part is not 'ws' or 'wss'."; - return false; - } - - var port = uri.Port; - if (port == 0) { - message = "The port part is zero."; - return false; - } - - if (uri.Fragment.Length > 0) { - message = "It includes the fragment component."; - return false; - } - - result = port != -1 - ? uri - : new Uri ( - String.Format ( - "{0}://{1}:{2}{3}", - schm, - uri.Host, - schm == "ws" ? 80 : 443, - uri.PathAndQuery - ) - ); - - return true; - } + { + result = null; + message = null; + + var uri = uriString.ToUri(); + if (uri == null) + { + message = "An invalid URI string."; + return false; + } - internal static bool TryGetUTF8DecodedString (this byte[] bytes, out string s) - { - s = null; + if (!uri.IsAbsoluteUri) + { + message = "A relative URI."; + return false; + } - try { - s = Encoding.UTF8.GetString (bytes); - } - catch { - return false; - } + var schm = uri.Scheme; + if (!(schm == "ws" || schm == "wss")) + { + message = "The scheme part is not 'ws' or 'wss'."; + return false; + } - return true; - } + var port = uri.Port; + if (port == 0) + { + message = "The port part is zero."; + return false; + } - internal static bool TryGetUTF8EncodedBytes (this string s, out byte[] bytes) - { - bytes = null; + if (uri.Fragment.Length > 0) + { + message = "It includes the fragment component."; + return false; + } - try { - bytes = Encoding.UTF8.GetBytes (s); - } - catch { - return false; - } + result = port != -1 + ? uri + : new Uri( + String.Format( + "{0}://{1}:{2}{3}", + schm, + uri.Host, + schm == "ws" ? 80 : 443, + uri.PathAndQuery + ) + ); + + return true; + } - return true; - } + /// + /// The TryGetUTF8DecodedString + /// + /// The bytes + /// The s + /// The + internal static bool TryGetUTF8DecodedString(this byte[] bytes, out string s) + { + s = null; + + try + { + s = Encoding.UTF8.GetString(bytes); + } + catch + { + return false; + } + + return true; + } + + /// + /// The TryGetUTF8EncodedBytes + /// + /// The s + /// The bytes + /// The + internal static bool TryGetUTF8EncodedBytes(this string s, out byte[] bytes) + { + bytes = null; + + try + { + bytes = Encoding.UTF8.GetBytes(s); + } + catch + { + return false; + } + + return true; + } - internal static bool TryOpenRead ( + /// + /// The TryOpenRead + /// + /// The fileInfo + /// The fileStream + /// The + internal static bool TryOpenRead( this FileInfo fileInfo, out FileStream fileStream ) - { - fileStream = null; + { + fileStream = null; - try { - fileStream = fileInfo.OpenRead (); - } - catch { - return false; - } + try + { + fileStream = fileInfo.OpenRead(); + } + catch + { + return false; + } - return true; - } + return true; + } - internal static string Unquote (this string value) - { - var start = value.IndexOf ('"'); - if (start == -1) - return value; - - var end = value.LastIndexOf ('"'); - if (end == start) - return value; - - var len = end - start - 1; - return len > 0 - ? value.Substring (start + 1, len).Replace ("\\\"", "\"") - : String.Empty; - } + /// + /// The Unquote + /// + /// The value + /// The + internal static string Unquote(this string value) + { + var start = value.IndexOf('"'); + if (start == -1) + return value; + + var end = value.LastIndexOf('"'); + if (end == start) + return value; + + var len = end - start - 1; + return len > 0 + ? value.Substring(start + 1, len).Replace("\\\"", "\"") + : String.Empty; + } - internal static bool Upgrades ( + /// + /// The Upgrades + /// + /// The headers + /// The protocol + /// The + internal static bool Upgrades( this NameValueCollection headers, string protocol ) - { - var comparison = StringComparison.OrdinalIgnoreCase; - return headers.Contains ("Upgrade", protocol, comparison) - && headers.Contains ("Connection", "Upgrade", comparison); - } + { + var comparison = StringComparison.OrdinalIgnoreCase; + return headers.Contains("Upgrade", protocol, comparison) + && headers.Contains("Connection", "Upgrade", comparison); + } - internal static string UTF8Decode (this byte[] bytes) - { - try { - return Encoding.UTF8.GetString (bytes); - } - catch { - return null; - } - } + /// + /// The UTF8Decode + /// + /// The bytes + /// The + internal static string UTF8Decode(this byte[] bytes) + { + try + { + return Encoding.UTF8.GetString(bytes); + } + catch + { + return null; + } + } - internal static byte[] UTF8Encode (this string s) - { - return Encoding.UTF8.GetBytes (s); - } + /// + /// The UTF8Encode + /// + /// The s + /// The + internal static byte[] UTF8Encode(this string s) + { + return Encoding.UTF8.GetBytes(s); + } - internal static void WriteBytes (this Stream stream, byte[] bytes, int bufferLength) - { - using (var input = new MemoryStream (bytes)) - input.CopyTo (stream, bufferLength); - } + /// + /// The WriteBytes + /// + /// The stream + /// The bytes + /// The bufferLength + internal static void WriteBytes(this Stream stream, byte[] bytes, int bufferLength) + { + using (var input = new MemoryStream(bytes)) + input.CopyTo(stream, bufferLength); + } - internal static void WriteBytesAsync ( + /// + /// The WriteBytesAsync + /// + /// The stream + /// The bytes + /// The bufferLength + /// The completed + /// The error + internal static void WriteBytesAsync( this Stream stream, byte[] bytes, int bufferLength, Action completed, Action error) - { - var input = new MemoryStream (bytes); - input.CopyToAsync ( - stream, - bufferLength, - () => { - if (completed != null) - completed (); - - input.Dispose (); - }, - ex => { - input.Dispose (); - if (error != null) - error (ex); - }); - } - - #endregion - - #region Public Methods - - /// - /// Emits the specified delegate if it isn't . - /// - /// - /// A to emit. - /// - /// - /// An from which emits this . - /// - /// - /// A that contains no event data. - /// - public static void Emit (this EventHandler eventHandler, object sender, EventArgs e) - { - if (eventHandler != null) - eventHandler (sender, e); - } - - /// - /// Emits the specified EventHandler<TEventArgs> delegate if it isn't - /// . - /// - /// - /// An EventHandler<TEventArgs> to emit. - /// - /// - /// An from which emits this . - /// - /// - /// A TEventArgs that represents the event data. - /// - /// - /// The type of the event data generated by the event. - /// - public static void Emit ( - this EventHandler eventHandler, object sender, TEventArgs e) - where TEventArgs : EventArgs - { - if (eventHandler != null) - eventHandler (sender, e); - } - - /// - /// Gets the description of the specified HTTP status . - /// - /// - /// A that represents the description of the HTTP status code. - /// - /// - /// One of enum values, indicates the HTTP status code. - /// - public static string GetDescription (this HttpStatusCode code) - { - return ((int) code).GetStatusDescription (); - } - - /// - /// Gets the description of the specified HTTP status . - /// - /// - /// A that represents the description of the HTTP status code. - /// - /// - /// An that represents the HTTP status code. - /// - public static string GetStatusDescription (this int code) - { - switch (code) { - case 100: return "Continue"; - case 101: return "Switching Protocols"; - case 102: return "Processing"; - case 200: return "OK"; - case 201: return "Created"; - case 202: return "Accepted"; - case 203: return "Non-Authoritative Information"; - case 204: return "No Content"; - case 205: return "Reset Content"; - case 206: return "Partial Content"; - case 207: return "Multi-Status"; - case 300: return "Multiple Choices"; - case 301: return "Moved Permanently"; - case 302: return "Found"; - case 303: return "See Other"; - case 304: return "Not Modified"; - case 305: return "Use Proxy"; - case 307: return "Temporary Redirect"; - case 400: return "Bad Request"; - case 401: return "Unauthorized"; - case 402: return "Payment Required"; - case 403: return "Forbidden"; - case 404: return "Not Found"; - case 405: return "Method Not Allowed"; - case 406: return "Not Acceptable"; - case 407: return "Proxy Authentication Required"; - case 408: return "Request Timeout"; - case 409: return "Conflict"; - case 410: return "Gone"; - case 411: return "Length Required"; - case 412: return "Precondition Failed"; - case 413: return "Request Entity Too Large"; - case 414: return "Request-Uri Too Long"; - case 415: return "Unsupported Media Type"; - case 416: return "Requested Range Not Satisfiable"; - case 417: return "Expectation Failed"; - case 422: return "Unprocessable Entity"; - case 423: return "Locked"; - case 424: return "Failed Dependency"; - case 500: return "Internal Server Error"; - case 501: return "Not Implemented"; - case 502: return "Bad Gateway"; - case 503: return "Service Unavailable"; - case 504: return "Gateway Timeout"; - case 505: return "Http Version Not Supported"; - case 507: return "Insufficient Storage"; - } - - return String.Empty; - } - - /// - /// Determines whether the specified ushort is in the range of - /// the status code for the WebSocket connection close. - /// - /// - /// - /// The ranges are the following: - /// - /// - /// - /// - /// 1000-2999: These numbers are reserved for definition by - /// the WebSocket protocol. - /// - /// - /// - /// - /// 3000-3999: These numbers are reserved for use by libraries, - /// frameworks, and applications. - /// - /// - /// - /// - /// 4000-4999: These numbers are reserved for private use. - /// - /// - /// - /// - /// - /// true if is in the range of - /// the status code for the close; otherwise, false. - /// - /// - /// A to test. - /// - public static bool IsCloseStatusCode (this ushort value) - { - return value > 999 && value < 5000; - } - - /// - /// Determines whether the specified string is enclosed in - /// the specified character. - /// - /// - /// true if is enclosed in - /// ; otherwise, false. - /// - /// - /// A to test. - /// - /// - /// A to find. - /// - public static bool IsEnclosedIn (this string value, char c) - { - return value != null - && value.Length > 1 - && value[0] == c - && value[value.Length - 1] == c; - } - - /// - /// Determines whether the specified byte order is host (this computer - /// architecture) byte order. - /// - /// - /// true if is host byte order; otherwise, - /// false. - /// - /// - /// One of the enum values to test. - /// - public static bool IsHostOrder (this ByteOrder order) - { - // true: !(true ^ true) or !(false ^ false) - // false: !(true ^ false) or !(false ^ true) - return !(BitConverter.IsLittleEndian ^ (order == ByteOrder.Little)); - } - - /// - /// Determines whether the specified IP address is a local IP address. - /// - /// - /// This local means NOT REMOTE for the current host. - /// - /// - /// true if is a local IP address; - /// otherwise, false. - /// - /// - /// A to test. - /// - /// - /// is . - /// - public static bool IsLocal (this System.Net.IPAddress address) - { - if (address == null) - throw new ArgumentNullException ("address"); - - if (address.Equals (System.Net.IPAddress.Any)) - return true; - - if (address.Equals (System.Net.IPAddress.Loopback)) - return true; - - if (Socket.OSSupportsIPv6) { - if (address.Equals (System.Net.IPAddress.IPv6Any)) - return true; - - if (address.Equals (System.Net.IPAddress.IPv6Loopback)) - return true; - } - - var host = System.Net.Dns.GetHostName (); - var addrs = System.Net.Dns.GetHostAddresses (host); - foreach (var addr in addrs) { - if (address.Equals (addr)) - return true; - } - - return false; - } - - /// - /// Determines whether the specified string is or - /// an empty string. - /// - /// - /// true if is or - /// an empty string; otherwise, false. - /// - /// - /// A to test. - /// - public static bool IsNullOrEmpty (this string value) - { - return value == null || value.Length == 0; - } - - /// - /// Determines whether the specified string is a predefined scheme. - /// - /// - /// true if is a predefined scheme; - /// otherwise, false. - /// - /// - /// A to test. - /// - public static bool IsPredefinedScheme (this string value) - { - if (value == null || value.Length < 2) - return false; - - var c = value[0]; - if (c == 'h') - return value == "http" || value == "https"; - - if (c == 'w') - return value == "ws" || value == "wss"; - - if (c == 'f') - return value == "file" || value == "ftp"; - - if (c == 'g') - return value == "gopher"; - - if (c == 'm') - return value == "mailto"; - - if (c == 'n') { - c = value[1]; - return c == 'e' - ? value == "news" || value == "net.pipe" || value == "net.tcp" - : value == "nntp"; - } - - return false; - } - - /// - /// Determines whether the specified string is a URI string. - /// - /// - /// true if may be a URI string; - /// otherwise, false. - /// - /// - /// A to test. - /// - public static bool MaybeUri (this string value) - { - if (value == null || value.Length == 0) - return false; - - var idx = value.IndexOf (':'); - if (idx == -1) - return false; - - if (idx >= 10) - return false; - - var schm = value.Substring (0, idx); - return schm.IsPredefinedScheme (); - } - - /// - /// Retrieves a sub-array from the specified . A sub-array starts at - /// the specified element position in . - /// - /// - /// An array of T that receives a sub-array, or an empty array of T if any problems with - /// the parameters. - /// - /// - /// An array of T from which to retrieve a sub-array. - /// - /// - /// An that represents the zero-based starting position of - /// a sub-array in . - /// - /// - /// An that represents the number of elements to retrieve. - /// - /// - /// The type of elements in . - /// - public static T[] SubArray (this T[] array, int startIndex, int length) - { - int len; - if (array == null || (len = array.Length) == 0) - return new T[0]; - - if (startIndex < 0 || length <= 0 || startIndex + length > len) - return new T[0]; - - if (startIndex == 0 && length == len) - return array; - - var subArray = new T[length]; - Array.Copy (array, startIndex, subArray, 0, length); - - return subArray; - } - - /// - /// Retrieves a sub-array from the specified . A sub-array starts at - /// the specified element position in . - /// - /// - /// An array of T that receives a sub-array, or an empty array of T if any problems with - /// the parameters. - /// - /// - /// An array of T from which to retrieve a sub-array. - /// - /// - /// A that represents the zero-based starting position of - /// a sub-array in . - /// - /// - /// A that represents the number of elements to retrieve. - /// - /// - /// The type of elements in . - /// - public static T[] SubArray (this T[] array, long startIndex, long length) - { - long len; - if (array == null || (len = array.LongLength) == 0) - return new T[0]; - - if (startIndex < 0 || length <= 0 || startIndex + length > len) - return new T[0]; - - if (startIndex == 0 && length == len) - return array; - - var subArray = new T[length]; - Array.Copy (array, startIndex, subArray, 0, length); - - return subArray; - } - - /// - /// Executes the specified delegate times. - /// - /// - /// An is the number of times to execute. - /// - /// - /// An delegate that references the method(s) to execute. - /// - public static void Times (this int n, Action action) - { - if (n > 0 && action != null) - ((ulong) n).times (action); - } - - /// - /// Executes the specified delegate times. - /// - /// - /// A is the number of times to execute. - /// - /// - /// An delegate that references the method(s) to execute. - /// - public static void Times (this long n, Action action) - { - if (n > 0 && action != null) - ((ulong) n).times (action); - } - - /// - /// Executes the specified delegate times. - /// - /// - /// A is the number of times to execute. - /// - /// - /// An delegate that references the method(s) to execute. - /// - public static void Times (this uint n, Action action) - { - if (n > 0 && action != null) - ((ulong) n).times (action); - } - - /// - /// Executes the specified delegate times. - /// - /// - /// A is the number of times to execute. - /// - /// - /// An delegate that references the method(s) to execute. - /// - public static void Times (this ulong n, Action action) - { - if (n > 0 && action != null) - n.times (action); - } - - /// - /// Executes the specified Action<int> delegate times. - /// - /// - /// An is the number of times to execute. - /// - /// - /// An Action<int> delegate that references the method(s) to execute. - /// An parameter to pass to the method(s) is the zero-based count of - /// iteration. - /// - public static void Times (this int n, Action action) - { - if (n > 0 && action != null) - for (int i = 0; i < n; i++) - action (i); - } - - /// - /// Executes the specified Action<long> delegate times. - /// - /// - /// A is the number of times to execute. - /// - /// - /// An Action<long> delegate that references the method(s) to execute. - /// A parameter to pass to the method(s) is the zero-based count of - /// iteration. - /// - public static void Times (this long n, Action action) - { - if (n > 0 && action != null) - for (long i = 0; i < n; i++) - action (i); - } - - /// - /// Executes the specified Action<uint> delegate times. - /// - /// - /// A is the number of times to execute. - /// - /// - /// An Action<uint> delegate that references the method(s) to execute. - /// A parameter to pass to the method(s) is the zero-based count of - /// iteration. - /// - public static void Times (this uint n, Action action) - { - if (n > 0 && action != null) - for (uint i = 0; i < n; i++) - action (i); - } - - /// - /// Executes the specified Action<ulong> delegate times. - /// - /// - /// A is the number of times to execute. - /// - /// - /// An Action<ulong> delegate that references the method(s) to execute. - /// A parameter to pass to this method(s) is the zero-based count of - /// iteration. - /// - public static void Times (this ulong n, Action action) - { - if (n > 0 && action != null) - for (ulong i = 0; i < n; i++) - action (i); - } - - /// - /// Converts the specified array of to the specified type data. - /// - /// - /// A T converted from , or a default value of - /// T if is an empty array of or - /// if the type of T isn't , , , - /// , , , , - /// , , or . - /// - /// - /// An array of to convert. - /// - /// - /// One of the enum values, specifies the byte order of - /// . - /// - /// - /// The type of the return. The T must be a value type. - /// - /// - /// is . - /// - public static T To (this byte[] source, ByteOrder sourceOrder) - where T : struct - { - if (source == null) - throw new ArgumentNullException ("source"); - - if (source.Length == 0) - return default (T); - - var type = typeof (T); - var buff = source.ToHostOrder (sourceOrder); - - return type == typeof (Boolean) - ? (T)(object) BitConverter.ToBoolean (buff, 0) - : type == typeof (Char) - ? (T)(object) BitConverter.ToChar (buff, 0) - : type == typeof (Double) - ? (T)(object) BitConverter.ToDouble (buff, 0) - : type == typeof (Int16) - ? (T)(object) BitConverter.ToInt16 (buff, 0) - : type == typeof (Int32) - ? (T)(object) BitConverter.ToInt32 (buff, 0) - : type == typeof (Int64) - ? (T)(object) BitConverter.ToInt64 (buff, 0) - : type == typeof (Single) - ? (T)(object) BitConverter.ToSingle (buff, 0) - : type == typeof (UInt16) - ? (T)(object) BitConverter.ToUInt16 (buff, 0) - : type == typeof (UInt32) - ? (T)(object) BitConverter.ToUInt32 (buff, 0) - : type == typeof (UInt64) - ? (T)(object) BitConverter.ToUInt64 (buff, 0) - : default (T); - } - - /// - /// Converts the specified to an array of . - /// - /// - /// An array of converted from . - /// - /// - /// A T to convert. - /// - /// - /// One of the enum values, specifies the byte order of the return. - /// - /// - /// The type of . The T must be a value type. - /// - public static byte[] ToByteArray (this T value, ByteOrder order) - where T : struct - { - var type = typeof (T); - var bytes = type == typeof (Boolean) - ? BitConverter.GetBytes ((Boolean)(object) value) - : type == typeof (Byte) - ? new byte[] { (Byte)(object) value } - : type == typeof (Char) - ? BitConverter.GetBytes ((Char)(object) value) - : type == typeof (Double) - ? BitConverter.GetBytes ((Double)(object) value) - : type == typeof (Int16) - ? BitConverter.GetBytes ((Int16)(object) value) - : type == typeof (Int32) - ? BitConverter.GetBytes ((Int32)(object) value) - : type == typeof (Int64) - ? BitConverter.GetBytes ((Int64)(object) value) - : type == typeof (Single) - ? BitConverter.GetBytes ((Single)(object) value) - : type == typeof (UInt16) - ? BitConverter.GetBytes ((UInt16)(object) value) - : type == typeof (UInt32) - ? BitConverter.GetBytes ((UInt32)(object) value) - : type == typeof (UInt64) - ? BitConverter.GetBytes ((UInt64)(object) value) - : WebSocket.EmptyBytes; - - if (bytes.Length > 1 && !order.IsHostOrder ()) - Array.Reverse (bytes); - - return bytes; - } - - /// - /// Converts the order of elements in the specified byte array to - /// host (this computer architecture) byte order. - /// - /// - /// - /// An array of converted from - /// . - /// - /// - /// Or if the number of elements in it - /// is less than 2 or is same as - /// host byte order. - /// - /// - /// - /// An array of to convert. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It specifies the order of elements in . - /// - /// - /// - /// is . - /// - public static byte[] ToHostOrder (this byte[] source, ByteOrder sourceOrder) - { - if (source == null) - throw new ArgumentNullException ("source"); - - if (source.Length < 2) - return source; - - return !sourceOrder.IsHostOrder () ? source.Reverse () : source; - } - - /// - /// Converts the specified array to a . - /// - /// - /// - /// A converted by concatenating each element of - /// across . - /// - /// - /// An empty string if is an empty array. - /// - /// - /// - /// An array of T to convert. - /// - /// - /// A used to separate each element of - /// . - /// - /// - /// The type of elements in . - /// - /// - /// is . - /// - public static string ToString (this T[] array, string separator) - { - if (array == null) - throw new ArgumentNullException ("array"); + { + var input = new MemoryStream(bytes); + input.CopyToAsync( + stream, + bufferLength, + () => + { + if (completed != null) + completed(); + + input.Dispose(); + }, + ex => + { + input.Dispose(); + if (error != null) + error(ex); + }); + } - var len = array.Length; - if (len == 0) - return String.Empty; + /// + /// The compress + /// + /// The data + /// The + private static byte[] compress(this byte[] data) + { + if (data.LongLength == 0) + //return new byte[] { 0x00, 0x00, 0x00, 0xff, 0xff }; + return data; + + using (var input = new MemoryStream(data)) + return input.compressToArray(); + } - if (separator == null) - separator = String.Empty; + /// + /// The compress + /// + /// The stream + /// The + private static MemoryStream compress(this Stream stream) + { + var output = new MemoryStream(); + if (stream.Length == 0) + return output; + + stream.Position = 0; + using (var ds = new DeflateStream(output, CompressionMode.Compress, true)) + { + stream.CopyTo(ds, 1024); + ds.Close(); // BFINAL set to 1. + output.Write(_last, 0, 1); + output.Position = 0; + + return output; + } + } - var buff = new StringBuilder (64); + /// + /// The compressToArray + /// + /// The stream + /// The + private static byte[] compressToArray(this Stream stream) + { + using (var output = stream.compress()) + { + output.Close(); + return output.ToArray(); + } + } - for (var i = 0; i < len - 1; i++) - buff.AppendFormat ("{0}{1}", array[i], separator); + /// + /// The decompress + /// + /// The data + /// The + private static byte[] decompress(this byte[] data) + { + if (data.LongLength == 0) + return data; + + using (var input = new MemoryStream(data)) + return input.decompressToArray(); + } - buff.Append (array[len - 1].ToString ()); - return buff.ToString (); - } + /// + /// The decompress + /// + /// The stream + /// The + private static MemoryStream decompress(this Stream stream) + { + var output = new MemoryStream(); + if (stream.Length == 0) + return output; + + stream.Position = 0; + using (var ds = new DeflateStream(stream, CompressionMode.Decompress, true)) + { + ds.CopyTo(output, 1024); + output.Position = 0; + + return output; + } + } - /// - /// Converts the specified string to a . - /// - /// - /// - /// A converted from . - /// - /// - /// if the conversion has failed. - /// - /// - /// - /// A to convert. - /// - public static Uri ToUri (this string value) - { - Uri ret; - Uri.TryCreate ( - value, value.MaybeUri () ? UriKind.Absolute : UriKind.Relative, out ret - ); + /// + /// The decompressToArray + /// + /// The stream + /// The + private static byte[] decompressToArray(this Stream stream) + { + using (var output = stream.decompress()) + { + output.Close(); + return output.ToArray(); + } + } - return ret; - } + /// + /// The isHttpMethod + /// + /// The value + /// The + private static bool isHttpMethod(this string value) + { + return value == "GET" + || value == "HEAD" + || value == "POST" + || value == "PUT" + || value == "DELETE" + || value == "CONNECT" + || value == "OPTIONS" + || value == "TRACE"; + } - /// - /// URL-decodes the specified . - /// - /// - /// A that receives the decoded string or - /// if it is or empty. - /// - /// - /// A to decode. - /// - public static string UrlDecode (this string value) - { - return value != null && value.Length > 0 - ? HttpUtility.UrlDecode (value) - : value; - } + /// + /// The isHttpMethod10 + /// + /// The value + /// The + private static bool isHttpMethod10(this string value) + { + return value == "GET" + || value == "HEAD" + || value == "POST"; + } - /// - /// URL-encodes the specified . - /// - /// - /// A that receives the encoded string or - /// if it is or empty. - /// - /// - /// A to encode. - /// - public static string UrlEncode (this string value) - { - return value != null && value.Length > 0 - ? HttpUtility.UrlEncode (value) - : value; - } + /// + /// The times + /// + /// The n + /// The action + private static void times(this ulong n, Action action) + { + for (ulong i = 0; i < n; i++) + action(); + } - /// - /// Writes and sends the specified data with the specified - /// . - /// - /// - /// A that represents the HTTP response used to - /// send the content data. - /// - /// - /// An array of that represents the content data to send. - /// - /// - /// - /// is . - /// - /// - /// -or- - /// - /// - /// is . - /// - /// - public static void WriteContent (this HttpListenerResponse response, byte[] content) - { - if (response == null) - throw new ArgumentNullException ("response"); - - if (content == null) - throw new ArgumentNullException ("content"); - - var len = content.LongLength; - if (len == 0) { - response.Close (); - return; - } - - response.ContentLength64 = len; - var output = response.OutputStream; - if (len <= Int32.MaxValue) - output.Write (content, 0, (int) len); - else - output.WriteBytes (content, 1024); - - output.Close (); + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Fin.cs b/src/WebSocket/WebSocketCore/Fin.cs index 497a57162..ac79498ae 100644 --- a/src/WebSocket/WebSocketCore/Fin.cs +++ b/src/WebSocket/WebSocketCore/Fin.cs @@ -1,4 +1,3 @@ -#region License /* * Fin.cs * @@ -24,28 +23,32 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; namespace WebSocketCore { - /// - /// Indicates whether a WebSocket frame is the final frame of a message. - /// - /// - /// The values of this enumeration are defined in - /// Section 5.2 of RFC 6455. - /// - internal enum Fin : byte - { - /// - /// Equivalent to numeric value 0. Indicates more frames of a message follow. - /// - More = 0x0, + #region ö + /// - /// Equivalent to numeric value 1. Indicates the final frame of a message. + /// Indicates whether a WebSocket frame is the final frame of a message. /// - Final = 0x1 - } -} + /// + /// The values of this enumeration are defined in + /// Section 5.2 of RFC 6455. + /// + internal enum Fin : byte + { + /// + /// Equivalent to numeric value 0. Indicates more frames of a message follow. + /// + More = 0x0, + + /// + /// Equivalent to numeric value 1. Indicates the final frame of a message. + /// + Final = 0x1 + } + + #endregion ö +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/HttpBase.cs b/src/WebSocket/WebSocketCore/HttpBase.cs index f22ce385e..db971200b 100644 --- a/src/WebSocket/WebSocketCore/HttpBase.cs +++ b/src/WebSocket/WebSocketCore/HttpBase.cs @@ -1,4 +1,3 @@ -#region License /* * HttpBase.cs * @@ -24,7 +23,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; using System.Collections.Generic; @@ -36,173 +34,232 @@ namespace WebSocketCore { - internal abstract class HttpBase - { - #region Private Fields - - private NameValueCollection _headers; - private const int _headersMaxLength = 8192; - private Version _version; - - #endregion - - #region Internal Fields - - internal byte[] EntityBodyData; - - #endregion - - #region Protected Fields - - protected const string CrLf = "\r\n"; - - #endregion - - #region Protected Constructors - - protected HttpBase (Version version, NameValueCollection headers) + /// + /// Defines the + /// + internal abstract class HttpBase { - _version = version; - _headers = headers; - } - - #endregion - - #region Public Properties + #region + + /// + /// Defines the CrLf + /// + protected const string CrLf = "\r\n"; + + /// + /// Defines the _headersMaxLength + /// + private const int _headersMaxLength = 8192; + + #endregion + + #region ֶ + + /// + /// Defines the EntityBodyData + /// + internal byte[] EntityBodyData; + + /// + /// Defines the _headers + /// + private NameValueCollection _headers; + + /// + /// Defines the _version + /// + private Version _version; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The version + /// The headers + protected HttpBase(Version version, NameValueCollection headers) + { + _version = version; + _headers = headers; + } - public string EntityBody { - get { - if (EntityBodyData == null || EntityBodyData.LongLength == 0) - return String.Empty; + #endregion 캯 - Encoding enc = null; + #region - var contentType = _headers["Content-Type"]; - if (contentType != null && contentType.Length > 0) - enc = HttpUtility.GetEncoding (contentType); + /// + /// Gets the EntityBody + /// + public string EntityBody + { + get + { + if (EntityBodyData == null || EntityBodyData.LongLength == 0) + return String.Empty; - return (enc ?? Encoding.UTF8).GetString (EntityBodyData); - } - } + Encoding enc = null; - public NameValueCollection Headers { - get { - return _headers; - } - } + var contentType = _headers["Content-Type"]; + if (contentType != null && contentType.Length > 0) + enc = HttpUtility.GetEncoding(contentType); - public Version ProtocolVersion { - get { - return _version; - } - } - - #endregion - - #region Private Methods - - private static byte[] readEntityBody (Stream stream, string length) - { - long len; - if (!Int64.TryParse (length, out len)) - throw new ArgumentException ("Cannot be parsed.", "length"); - - if (len < 0) - throw new ArgumentOutOfRangeException ("length", "Less than zero."); - - return len > 1024 - ? stream.ReadBytes (len, 1024) - : len > 0 - ? stream.ReadBytes ((int) len) - : null; - } + return (enc ?? Encoding.UTF8).GetString(EntityBodyData); + } + } - private static string[] readHeaders (Stream stream, int maxLength) - { - var buff = new List (); - var cnt = 0; - Action add = i => { - if (i == -1) - throw new EndOfStreamException ("The header cannot be read from the data source."); - - buff.Add ((byte) i); - cnt++; - }; - - var read = false; - while (cnt < maxLength) { - if (stream.ReadByte ().EqualsWith ('\r', add) && - stream.ReadByte ().EqualsWith ('\n', add) && - stream.ReadByte ().EqualsWith ('\r', add) && - stream.ReadByte ().EqualsWith ('\n', add)) { - read = true; - break; + /// + /// Gets the Headers + /// + public NameValueCollection Headers + { + get + { + return _headers; + } } - } - if (!read) - throw new WebSocketException ("The length of header part is greater than the max length."); + /// + /// Gets the ProtocolVersion + /// + public Version ProtocolVersion + { + get + { + return _version; + } + } - return Encoding.UTF8.GetString (buff.ToArray ()) - .Replace (CrLf + " ", " ") - .Replace (CrLf + "\t", " ") - .Split (new[] { CrLf }, StringSplitOptions.RemoveEmptyEntries); - } + #endregion - #endregion + #region - #region Protected Methods + /// + /// The ToByteArray + /// + /// The + public byte[] ToByteArray() + { + return Encoding.UTF8.GetBytes(ToString()); + } - protected static T Read (Stream stream, Func parser, int millisecondsTimeout) + /// + /// The Read + /// + /// + /// The stream + /// The parser + /// The millisecondsTimeout + /// The + protected static T Read(Stream stream, Func parser, int millisecondsTimeout) where T : HttpBase - { - var timeout = false; - var timer = new Timer ( - state => { - timeout = true; - stream.Close (); - }, - null, - millisecondsTimeout, - -1); - - T http = null; - Exception exception = null; - try { - http = parser (readHeaders (stream, _headersMaxLength)); - var contentLen = http.Headers["Content-Length"]; - if (contentLen != null && contentLen.Length > 0) - http.EntityBodyData = readEntityBody (stream, contentLen); - } - catch (Exception ex) { - exception = ex; - } - finally { - timer.Change (-1, -1); - timer.Dispose (); - } - - var msg = timeout - ? "A timeout has occurred while reading an HTTP request/response." - : exception != null - ? "An exception has occurred while reading an HTTP request/response." - : null; - - if (msg != null) - throw new WebSocketException (msg, exception); - - return http; - } + { + var timeout = false; + var timer = new Timer( + state => + { + timeout = true; + stream.Close(); + }, + null, + millisecondsTimeout, + -1); + + T http = null; + Exception exception = null; + try + { + http = parser(readHeaders(stream, _headersMaxLength)); + var contentLen = http.Headers["Content-Length"]; + if (contentLen != null && contentLen.Length > 0) + http.EntityBodyData = readEntityBody(stream, contentLen); + } + catch (Exception ex) + { + exception = ex; + } + finally + { + timer.Change(-1, -1); + timer.Dispose(); + } + + var msg = timeout + ? "A timeout has occurred while reading an HTTP request/response." + : exception != null + ? "An exception has occurred while reading an HTTP request/response." + : null; + + if (msg != null) + throw new WebSocketException(msg, exception); + + return http; + } - #endregion + /// + /// The readEntityBody + /// + /// The stream + /// The length + /// The + private static byte[] readEntityBody(Stream stream, string length) + { + long len; + if (!Int64.TryParse(length, out len)) + throw new ArgumentException("Cannot be parsed.", "length"); + + if (len < 0) + throw new ArgumentOutOfRangeException("length", "Less than zero."); + + return len > 1024 + ? stream.ReadBytes(len, 1024) + : len > 0 + ? stream.ReadBytes((int)len) + : null; + } - #region Public Methods + /// + /// The readHeaders + /// + /// The stream + /// The maxLength + /// The + private static string[] readHeaders(Stream stream, int maxLength) + { + var buff = new List(); + var cnt = 0; + Action add = i => + { + if (i == -1) + throw new EndOfStreamException("The header cannot be read from the data source."); + + buff.Add((byte)i); + cnt++; + }; + + var read = false; + while (cnt < maxLength) + { + if (stream.ReadByte().EqualsWith('\r', add) && + stream.ReadByte().EqualsWith('\n', add) && + stream.ReadByte().EqualsWith('\r', add) && + stream.ReadByte().EqualsWith('\n', add)) + { + read = true; + break; + } + } + + if (!read) + throw new WebSocketException("The length of header part is greater than the max length."); + + return Encoding.UTF8.GetString(buff.ToArray()) + .Replace(CrLf + " ", " ") + .Replace(CrLf + "\t", " ") + .Split(new[] { CrLf }, StringSplitOptions.RemoveEmptyEntries); + } - public byte[] ToByteArray () - { - return Encoding.UTF8.GetBytes (ToString ()); + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/HttpRequest.cs b/src/WebSocket/WebSocketCore/HttpRequest.cs index 932c2dffd..81591ea29 100644 --- a/src/WebSocket/WebSocketCore/HttpRequest.cs +++ b/src/WebSocket/WebSocketCore/HttpRequest.cs @@ -1,4 +1,3 @@ -#region License /* * HttpRequest.cs * @@ -24,14 +23,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Contributors /* * Contributors: * - David Burhans */ -#endregion using System; using System.Collections.Specialized; @@ -41,177 +37,256 @@ namespace WebSocketCore { - internal class HttpRequest : HttpBase - { - #region Private Fields - - private CookieCollection _cookies; - private string _method; - private string _uri; - - #endregion - - #region Private Constructors - - private HttpRequest (string method, string uri, Version version, NameValueCollection headers) - : base (version, headers) - { - _method = method; - _uri = uri; - } - - #endregion - - #region Internal Constructors - - internal HttpRequest (string method, string uri) - : this (method, uri, HttpVersion.Version11, new NameValueCollection ()) - { - Headers["User-Agent"] = "websocket-sharp/1.0"; - } - - #endregion - - #region Public Properties - - public AuthenticationResponse AuthenticationResponse { - get { - var res = Headers["Authorization"]; - return res != null && res.Length > 0 - ? AuthenticationResponse.Parse (res) - : null; - } - } - - public CookieCollection Cookies { - get { - if (_cookies == null) - _cookies = Headers.GetCookies (false); - - return _cookies; - } - } - - public string HttpMethod { - get { - return _method; - } - } - - public bool IsWebSocketRequest { - get { - return _method == "GET" - && ProtocolVersion > HttpVersion.Version10 - && Headers.Upgrades ("websocket"); - } - } - - public string RequestUri { - get { - return _uri; - } - } - - #endregion - - #region Internal Methods - - internal static HttpRequest CreateConnectRequest (Uri uri) + /// + /// Defines the + /// + internal class HttpRequest : HttpBase { - var host = uri.DnsSafeHost; - var port = uri.Port; - var authority = String.Format ("{0}:{1}", host, port); - var req = new HttpRequest ("CONNECT", authority); - req.Headers["Host"] = port == 80 ? host : authority; - - return req; + #region ֶ + + /// + /// Defines the _cookies + /// + private CookieCollection _cookies; + + /// + /// Defines the _method + /// + private string _method; + + /// + /// Defines the _uri + /// + private string _uri; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The method + /// The uri + internal HttpRequest(string method, string uri) + : this(method, uri, HttpVersion.Version11, new NameValueCollection()) + { + Headers["User-Agent"] = "websocket-sharp/1.0"; + } + + /// + /// Prevents a default instance of the class from being created. + /// + /// The method + /// The uri + /// The version + /// The headers + private HttpRequest(string method, string uri, Version version, NameValueCollection headers) + : base(version, headers) + { + _method = method; + _uri = uri; + } + + #endregion 캯 + + #region + + /// + /// Gets the AuthenticationResponse + /// + public AuthenticationResponse AuthenticationResponse + { + get + { + var res = Headers["Authorization"]; + return res != null && res.Length > 0 + ? AuthenticationResponse.Parse(res) + : null; + } + } + + /// + /// Gets the Cookies + /// + public CookieCollection Cookies + { + get + { + if (_cookies == null) + _cookies = Headers.GetCookies(false); + + return _cookies; + } + } + + /// + /// Gets the HttpMethod + /// + public string HttpMethod + { + get + { + return _method; + } + } + + /// + /// Gets a value indicating whether IsWebSocketRequest + /// + public bool IsWebSocketRequest + { + get + { + return _method == "GET" + && ProtocolVersion > HttpVersion.Version10 + && Headers.Upgrades("websocket"); + } + } + + /// + /// Gets the RequestUri + /// + public string RequestUri + { + get + { + return _uri; + } + } + + #endregion + + #region + + /// + /// The SetCookies + /// + /// The cookies + public void SetCookies(CookieCollection cookies) + { + if (cookies == null || cookies.Count == 0) + return; + + var buff = new StringBuilder(64); + foreach (var cookie in cookies.Sorted) + if (!cookie.Expired) + buff.AppendFormat("{0}; ", cookie.ToString()); + + var len = buff.Length; + if (len > 2) + { + buff.Length = len - 2; + Headers["Cookie"] = buff.ToString(); + } + } + + /// + /// The ToString + /// + /// The + public override string ToString() + { + var output = new StringBuilder(64); + output.AppendFormat("{0} {1} HTTP/{2}{3}", _method, _uri, ProtocolVersion, CrLf); + + var headers = Headers; + foreach (var key in headers.AllKeys) + output.AppendFormat("{0}: {1}{2}", key, headers[key], CrLf); + + output.Append(CrLf); + + var entity = EntityBody; + if (entity.Length > 0) + output.Append(entity); + + return output.ToString(); + } + + /// + /// The CreateConnectRequest + /// + /// The uri + /// The + internal static HttpRequest CreateConnectRequest(Uri uri) + { + var host = uri.DnsSafeHost; + var port = uri.Port; + var authority = String.Format("{0}:{1}", host, port); + var req = new HttpRequest("CONNECT", authority); + req.Headers["Host"] = port == 80 ? host : authority; + + return req; + } + + /// + /// The CreateWebSocketRequest + /// + /// The uri + /// The + internal static HttpRequest CreateWebSocketRequest(Uri uri) + { + var req = new HttpRequest("GET", uri.PathAndQuery); + var headers = req.Headers; + + // Only includes a port number in the Host header value if it's non-default. + // See: https://tools.ietf.org/html/rfc6455#page-17 + var port = uri.Port; + var schm = uri.Scheme; + headers["Host"] = (port == 80 && schm == "ws") || (port == 443 && schm == "wss") + ? uri.DnsSafeHost + : uri.Authority; + + headers["Upgrade"] = "websocket"; + headers["Connection"] = "Upgrade"; + + return req; + } + + /// + /// The Parse + /// + /// The headerParts + /// The + internal static HttpRequest Parse(string[] headerParts) + { + var requestLine = headerParts[0].Split(new[] { ' ' }, 3); + if (requestLine.Length != 3) + throw new ArgumentException("Invalid request line: " + headerParts[0]); + + var headers = new WebHeaderCollection(); + for (int i = 1; i < headerParts.Length; i++) + headers.InternalSet(headerParts[i], false); + + return new HttpRequest( + requestLine[0], requestLine[1], new Version(requestLine[2].Substring(5)), headers); + } + + /// + /// The Read + /// + /// The stream + /// The millisecondsTimeout + /// The + internal static HttpRequest Read(Stream stream, int millisecondsTimeout) + { + return Read(stream, Parse, millisecondsTimeout); + } + + /// + /// The GetResponse + /// + /// The stream + /// The millisecondsTimeout + /// The + internal HttpResponse GetResponse(Stream stream, int millisecondsTimeout) + { + var buff = ToByteArray(); + stream.Write(buff, 0, buff.Length); + + return Read(stream, HttpResponse.Parse, millisecondsTimeout); + } + + #endregion } - - internal static HttpRequest CreateWebSocketRequest (Uri uri) - { - var req = new HttpRequest ("GET", uri.PathAndQuery); - var headers = req.Headers; - - // Only includes a port number in the Host header value if it's non-default. - // See: https://tools.ietf.org/html/rfc6455#page-17 - var port = uri.Port; - var schm = uri.Scheme; - headers["Host"] = (port == 80 && schm == "ws") || (port == 443 && schm == "wss") - ? uri.DnsSafeHost - : uri.Authority; - - headers["Upgrade"] = "websocket"; - headers["Connection"] = "Upgrade"; - - return req; - } - - internal HttpResponse GetResponse (Stream stream, int millisecondsTimeout) - { - var buff = ToByteArray (); - stream.Write (buff, 0, buff.Length); - - return Read (stream, HttpResponse.Parse, millisecondsTimeout); - } - - internal static HttpRequest Parse (string[] headerParts) - { - var requestLine = headerParts[0].Split (new[] { ' ' }, 3); - if (requestLine.Length != 3) - throw new ArgumentException ("Invalid request line: " + headerParts[0]); - - var headers = new WebHeaderCollection (); - for (int i = 1; i < headerParts.Length; i++) - headers.InternalSet (headerParts[i], false); - - return new HttpRequest ( - requestLine[0], requestLine[1], new Version (requestLine[2].Substring (5)), headers); - } - - internal static HttpRequest Read (Stream stream, int millisecondsTimeout) - { - return Read (stream, Parse, millisecondsTimeout); - } - - #endregion - - #region Public Methods - - public void SetCookies (CookieCollection cookies) - { - if (cookies == null || cookies.Count == 0) - return; - - var buff = new StringBuilder (64); - foreach (var cookie in cookies.Sorted) - if (!cookie.Expired) - buff.AppendFormat ("{0}; ", cookie.ToString ()); - - var len = buff.Length; - if (len > 2) { - buff.Length = len - 2; - Headers["Cookie"] = buff.ToString (); - } - } - - public override string ToString () - { - var output = new StringBuilder (64); - output.AppendFormat ("{0} {1} HTTP/{2}{3}", _method, _uri, ProtocolVersion, CrLf); - - var headers = Headers; - foreach (var key in headers.AllKeys) - output.AppendFormat ("{0}: {1}{2}", key, headers[key], CrLf); - - output.Append (CrLf); - - var entity = EntityBody; - if (entity.Length > 0) - output.Append (entity); - - return output.ToString (); - } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/HttpResponse.cs b/src/WebSocket/WebSocketCore/HttpResponse.cs index 7de8c3d57..2ad881b1e 100644 --- a/src/WebSocket/WebSocketCore/HttpResponse.cs +++ b/src/WebSocket/WebSocketCore/HttpResponse.cs @@ -1,4 +1,3 @@ -#region License /* * HttpResponse.cs * @@ -24,7 +23,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; using System.Collections.Specialized; @@ -34,176 +32,267 @@ namespace WebSocketCore { - internal class HttpResponse : HttpBase - { - #region Private Fields - - private string _code; - private string _reason; - - #endregion - - #region Private Constructors - - private HttpResponse (string code, string reason, Version version, NameValueCollection headers) - : base (version, headers) - { - _code = code; - _reason = reason; - } - - #endregion - - #region Internal Constructors - - internal HttpResponse (HttpStatusCode code) - : this (code, code.GetDescription ()) + /// + /// Defines the + /// + internal class HttpResponse : HttpBase { + #region ֶ + + /// + /// Defines the _code + /// + private string _code; + + /// + /// Defines the _reason + /// + private string _reason; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The code + internal HttpResponse(HttpStatusCode code) + : this(code, code.GetDescription()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The code + /// The reason + internal HttpResponse(HttpStatusCode code, string reason) + : this(((int)code).ToString(), reason, HttpVersion.Version11, new NameValueCollection()) + { + Headers["Server"] = "websocket-sharp/1.0"; + } + + /// + /// Prevents a default instance of the class from being created. + /// + /// The code + /// The reason + /// The version + /// The headers + private HttpResponse(string code, string reason, Version version, NameValueCollection headers) + : base(version, headers) + { + _code = code; + _reason = reason; + } + + #endregion 캯 + + #region + + /// + /// Gets the Cookies + /// + public CookieCollection Cookies + { + get + { + return Headers.GetCookies(true); + } + } + + /// + /// Gets a value indicating whether HasConnectionClose + /// + public bool HasConnectionClose + { + get + { + var comparison = StringComparison.OrdinalIgnoreCase; + return Headers.Contains("Connection", "close", comparison); + } + } + + /// + /// Gets a value indicating whether IsProxyAuthenticationRequired + /// + public bool IsProxyAuthenticationRequired + { + get + { + return _code == "407"; + } + } + + /// + /// Gets a value indicating whether IsRedirect + /// + public bool IsRedirect + { + get + { + return _code == "301" || _code == "302"; + } + } + + /// + /// Gets a value indicating whether IsUnauthorized + /// + public bool IsUnauthorized + { + get + { + return _code == "401"; + } + } + + /// + /// Gets a value indicating whether IsWebSocketResponse + /// + public bool IsWebSocketResponse + { + get + { + return ProtocolVersion > HttpVersion.Version10 + && _code == "101" + && Headers.Upgrades("websocket"); + } + } + + /// + /// Gets the Reason + /// + public string Reason + { + get + { + return _reason; + } + } + + /// + /// Gets the StatusCode + /// + public string StatusCode + { + get + { + return _code; + } + } + + #endregion + + #region + + /// + /// The SetCookies + /// + /// The cookies + public void SetCookies(CookieCollection cookies) + { + if (cookies == null || cookies.Count == 0) + return; + + var headers = Headers; + foreach (var cookie in cookies.Sorted) + headers.Add("Set-Cookie", cookie.ToResponseString()); + } + + /// + /// The ToString + /// + /// The + public override string ToString() + { + var output = new StringBuilder(64); + output.AppendFormat("HTTP/{0} {1} {2}{3}", ProtocolVersion, _code, _reason, CrLf); + + var headers = Headers; + foreach (var key in headers.AllKeys) + output.AppendFormat("{0}: {1}{2}", key, headers[key], CrLf); + + output.Append(CrLf); + + var entity = EntityBody; + if (entity.Length > 0) + output.Append(entity); + + return output.ToString(); + } + + /// + /// The CreateCloseResponse + /// + /// The code + /// The + internal static HttpResponse CreateCloseResponse(HttpStatusCode code) + { + var res = new HttpResponse(code); + res.Headers["Connection"] = "close"; + + return res; + } + + /// + /// The CreateUnauthorizedResponse + /// + /// The challenge + /// The + internal static HttpResponse CreateUnauthorizedResponse(string challenge) + { + var res = new HttpResponse(HttpStatusCode.Unauthorized); + res.Headers["WWW-Authenticate"] = challenge; + + return res; + } + + /// + /// The CreateWebSocketResponse + /// + /// The + internal static HttpResponse CreateWebSocketResponse() + { + var res = new HttpResponse(HttpStatusCode.SwitchingProtocols); + + var headers = res.Headers; + headers["Upgrade"] = "websocket"; + headers["Connection"] = "Upgrade"; + + return res; + } + + /// + /// The Parse + /// + /// The headerParts + /// The + internal static HttpResponse Parse(string[] headerParts) + { + var statusLine = headerParts[0].Split(new[] { ' ' }, 3); + if (statusLine.Length != 3) + throw new ArgumentException("Invalid status line: " + headerParts[0]); + + var headers = new WebHeaderCollection(); + for (int i = 1; i < headerParts.Length; i++) + headers.InternalSet(headerParts[i], true); + + return new HttpResponse( + statusLine[1], statusLine[2], new Version(statusLine[0].Substring(5)), headers); + } + + /// + /// The Read + /// + /// The stream + /// The millisecondsTimeout + /// The + internal static HttpResponse Read(Stream stream, int millisecondsTimeout) + { + return Read(stream, Parse, millisecondsTimeout); + } + + #endregion } - - internal HttpResponse (HttpStatusCode code, string reason) - : this (((int) code).ToString (), reason, HttpVersion.Version11, new NameValueCollection ()) - { - Headers["Server"] = "websocket-sharp/1.0"; - } - - #endregion - - #region Public Properties - - public CookieCollection Cookies { - get { - return Headers.GetCookies (true); - } - } - - public bool HasConnectionClose { - get { - var comparison = StringComparison.OrdinalIgnoreCase; - return Headers.Contains ("Connection", "close", comparison); - } - } - - public bool IsProxyAuthenticationRequired { - get { - return _code == "407"; - } - } - - public bool IsRedirect { - get { - return _code == "301" || _code == "302"; - } - } - - public bool IsUnauthorized { - get { - return _code == "401"; - } - } - - public bool IsWebSocketResponse { - get { - return ProtocolVersion > HttpVersion.Version10 - && _code == "101" - && Headers.Upgrades ("websocket"); - } - } - - public string Reason { - get { - return _reason; - } - } - - public string StatusCode { - get { - return _code; - } - } - - #endregion - - #region Internal Methods - - internal static HttpResponse CreateCloseResponse (HttpStatusCode code) - { - var res = new HttpResponse (code); - res.Headers["Connection"] = "close"; - - return res; - } - - internal static HttpResponse CreateUnauthorizedResponse (string challenge) - { - var res = new HttpResponse (HttpStatusCode.Unauthorized); - res.Headers["WWW-Authenticate"] = challenge; - - return res; - } - - internal static HttpResponse CreateWebSocketResponse () - { - var res = new HttpResponse (HttpStatusCode.SwitchingProtocols); - - var headers = res.Headers; - headers["Upgrade"] = "websocket"; - headers["Connection"] = "Upgrade"; - - return res; - } - - internal static HttpResponse Parse (string[] headerParts) - { - var statusLine = headerParts[0].Split (new[] { ' ' }, 3); - if (statusLine.Length != 3) - throw new ArgumentException ("Invalid status line: " + headerParts[0]); - - var headers = new WebHeaderCollection (); - for (int i = 1; i < headerParts.Length; i++) - headers.InternalSet (headerParts[i], true); - - return new HttpResponse ( - statusLine[1], statusLine[2], new Version (statusLine[0].Substring (5)), headers); - } - - internal static HttpResponse Read (Stream stream, int millisecondsTimeout) - { - return Read (stream, Parse, millisecondsTimeout); - } - - #endregion - - #region Public Methods - - public void SetCookies (CookieCollection cookies) - { - if (cookies == null || cookies.Count == 0) - return; - - var headers = Headers; - foreach (var cookie in cookies.Sorted) - headers.Add ("Set-Cookie", cookie.ToResponseString ()); - } - - public override string ToString () - { - var output = new StringBuilder (64); - output.AppendFormat ("HTTP/{0} {1} {2}{3}", ProtocolVersion, _code, _reason, CrLf); - - var headers = Headers; - foreach (var key in headers.AllKeys) - output.AppendFormat ("{0}: {1}{2}", key, headers[key], CrLf); - - output.Append (CrLf); - - var entity = EntityBody; - if (entity.Length > 0) - output.Append (entity); - - return output.ToString (); - } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/LogData.cs b/src/WebSocket/WebSocketCore/LogData.cs index ceb01e403..6c79e9920 100644 --- a/src/WebSocket/WebSocketCore/LogData.cs +++ b/src/WebSocket/WebSocketCore/LogData.cs @@ -1,4 +1,3 @@ -#region License /* * LogData.cs * @@ -24,7 +23,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; using System.Diagnostics; @@ -32,118 +30,133 @@ namespace WebSocketCore { - /// - /// Represents a log data used by the class. - /// - public class LogData - { - #region Private Fields - - private StackFrame _caller; - private DateTime _date; - private LogLevel _level; - private string _message; - - #endregion - - #region Internal Constructors - - internal LogData (LogLevel level, StackFrame caller, string message) - { - _level = level; - _caller = caller; - _message = message ?? String.Empty; - _date = DateTime.Now; - } - - #endregion - - #region Public Properties - /// - /// Gets the information of the logging method caller. + /// Represents a log data used by the class. /// - /// - /// A that provides the information of the logging method caller. - /// - public StackFrame Caller { - get { - return _caller; - } - } - - /// - /// Gets the date and time when the log data was created. - /// - /// - /// A that represents the date and time when the log data was created. - /// - public DateTime Date { - get { - return _date; - } - } - - /// - /// Gets the logging level of the log data. - /// - /// - /// One of the enum values, indicates the logging level of the log data. - /// - public LogLevel Level { - get { - return _level; - } - } - - /// - /// Gets the message of the log data. - /// - /// - /// A that represents the message of the log data. - /// - public string Message { - get { - return _message; - } - } - - #endregion - - #region Public Methods - - /// - /// Returns a that represents the current . - /// - /// - /// A that represents the current . - /// - public override string ToString () + public class LogData { - var header = String.Format ("{0}|{1,-5}|", _date, _level); - var method = _caller.GetMethod (); - var type = method.DeclaringType; + #region ֶ + + /// + /// Defines the _caller + /// + private StackFrame _caller; + + /// + /// Defines the _date + /// + private DateTime _date; + + /// + /// Defines the _level + /// + private LogLevel _level; + + /// + /// Defines the _message + /// + private string _message; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The level + /// The caller + /// The message + internal LogData(LogLevel level, StackFrame caller, string message) + { + _level = level; + _caller = caller; + _message = message ?? String.Empty; + _date = DateTime.Now; + } + + #endregion 캯 + + #region + + /// + /// Gets the information of the logging method caller. + /// + public StackFrame Caller + { + get + { + return _caller; + } + } + + /// + /// Gets the date and time when the log data was created. + /// + public DateTime Date + { + get + { + return _date; + } + } + + /// + /// Gets the logging level of the log data. + /// + public LogLevel Level + { + get + { + return _level; + } + } + + /// + /// Gets the message of the log data. + /// + public string Message + { + get + { + return _message; + } + } + + #endregion + + #region + + /// + /// Returns a that represents the current . + /// + /// The + public override string ToString() + { + var header = String.Format("{0}|{1,-5}|", _date, _level); + var method = _caller.GetMethod(); + var type = method.DeclaringType; #if DEBUG - var lineNum = _caller.GetFileLineNumber (); - var headerAndCaller = - String.Format ("{0}{1}.{2}:{3}|", header, type.Name, method.Name, lineNum); + var lineNum = _caller.GetFileLineNumber(); + var headerAndCaller = + String.Format("{0}{1}.{2}:{3}|", header, type.Name, method.Name, lineNum); #else var headerAndCaller = String.Format ("{0}{1}.{2}|", header, type.Name, method.Name); #endif - var msgs = _message.Replace ("\r\n", "\n").TrimEnd ('\n').Split ('\n'); - if (msgs.Length <= 1) - return String.Format ("{0}{1}", headerAndCaller, _message); + var msgs = _message.Replace("\r\n", "\n").TrimEnd('\n').Split('\n'); + if (msgs.Length <= 1) + return String.Format("{0}{1}", headerAndCaller, _message); - var buff = new StringBuilder (String.Format ("{0}{1}\n", headerAndCaller, msgs[0]), 64); + var buff = new StringBuilder(String.Format("{0}{1}\n", headerAndCaller, msgs[0]), 64); - var fmt = String.Format ("{{0,{0}}}{{1}}\n", header.Length); - for (var i = 1; i < msgs.Length; i++) - buff.AppendFormat (fmt, "", msgs[i]); + var fmt = String.Format("{{0,{0}}}{{1}}\n", header.Length); + for (var i = 1; i < msgs.Length; i++) + buff.AppendFormat(fmt, "", msgs[i]); - buff.Length--; - return buff.ToString (); - } + buff.Length--; + return buff.ToString(); + } - #endregion - } -} + #endregion + } +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/LogLevel.cs b/src/WebSocket/WebSocketCore/LogLevel.cs index 8b66100c9..2c4363697 100644 --- a/src/WebSocket/WebSocketCore/LogLevel.cs +++ b/src/WebSocket/WebSocketCore/LogLevel.cs @@ -1,4 +1,3 @@ -#region License /* * LogLevel.cs * @@ -24,40 +23,48 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; namespace WebSocketCore { - /// - /// Specifies the logging level. - /// - public enum LogLevel - { - /// - /// Specifies the bottom logging level. - /// - Trace, - /// - /// Specifies the 2nd logging level from the bottom. - /// - Debug, - /// - /// Specifies the 3rd logging level from the bottom. - /// - Info, - /// - /// Specifies the 3rd logging level from the top. - /// - Warn, - /// - /// Specifies the 2nd logging level from the top. - /// - Error, + #region ö + /// - /// Specifies the top logging level. + /// Specifies the logging level. /// - Fatal - } -} + public enum LogLevel + { + /// + /// Specifies the bottom logging level. + /// + Trace, + + /// + /// Specifies the 2nd logging level from the bottom. + /// + Debug, + + /// + /// Specifies the 3rd logging level from the bottom. + /// + Info, + + /// + /// Specifies the 3rd logging level from the top. + /// + Warn, + + /// + /// Specifies the 2nd logging level from the top. + /// + Error, + + /// + /// Specifies the top logging level. + /// + Fatal + } + + #endregion ö +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Logger.cs b/src/WebSocket/WebSocketCore/Logger.cs index 56670475f..a77be43d6 100644 --- a/src/WebSocket/WebSocketCore/Logger.cs +++ b/src/WebSocket/WebSocketCore/Logger.cs @@ -1,4 +1,3 @@ -#region License /* * Logger.cs * @@ -24,7 +23,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; using System.Diagnostics; @@ -32,299 +30,257 @@ namespace WebSocketCore { - /// - /// Provides a set of methods and properties for logging. - /// - /// - /// - /// If you output a log with lower than the value of the property, - /// it cannot be outputted. - /// - /// - /// The default output action writes a log to the standard output stream and the log file - /// if the property has a valid path to it. - /// - /// - /// If you would like to use the custom output action, you should set - /// the property to any Action<LogData, string> - /// delegate. - /// - /// - public class Logger - { - #region Private Fields - - private volatile string _file; - private volatile LogLevel _level; - private Action _output; - private object _sync; - - #endregion - - #region Public Constructors - - /// - /// Initializes a new instance of the class. - /// - /// - /// This constructor initializes the current logging level with . - /// - public Logger () - : this (LogLevel.Error, null, null) - { - } - - /// - /// Initializes a new instance of the class with - /// the specified logging . - /// - /// - /// One of the enum values. - /// - public Logger (LogLevel level) - : this (level, null, null) - { - } - /// - /// Initializes a new instance of the class with - /// the specified logging , path to the log , - /// and action. + /// Provides a set of methods and properties for logging. /// - /// - /// One of the enum values. - /// - /// - /// A that represents the path to the log file. - /// - /// - /// An Action<LogData, string> delegate that references the method(s) used to - /// output a log. A parameter passed to this delegate is - /// . - /// - public Logger (LogLevel level, string file, Action output) + public class Logger { - _level = level; - _file = file; - _output = output ?? defaultOutput; - _sync = new object (); - } + #region ֶ + + /// + /// Defines the _file + /// + private volatile string _file; + + /// + /// Defines the _level + /// + private volatile LogLevel _level; + + /// + /// Defines the _output + /// + private Action _output; + + /// + /// Defines the _sync + /// + private object _sync; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + public Logger() + : this(LogLevel.Error, null, null) + { + } - #endregion + /// + /// Initializes a new instance of the class. + /// + /// The level + public Logger(LogLevel level) + : this(level, null, null) + { + } - #region Public Properties + /// + /// Initializes a new instance of the class. + /// + /// The level + /// The file + /// The output + public Logger(LogLevel level, string file, Action output) + { + _level = level; + _file = file; + _output = output ?? defaultOutput; + _sync = new object(); + } - /// - /// Gets or sets the current path to the log file. - /// - /// - /// A that represents the current path to the log file if any. - /// - public string File { - get { - return _file; - } - - set { - lock (_sync) { - _file = value; - Warn ( - String.Format ("The current path to the log file has been changed to {0}.", _file)); + #endregion 캯 + + #region + + /// + /// Gets or sets the current path to the log file. + /// + public string File + { + get + { + return _file; + } + + set + { + lock (_sync) + { + _file = value; + Warn( + String.Format("The current path to the log file has been changed to {0}.", _file)); + } + } } - } - } - /// - /// Gets or sets the current logging level. - /// - /// - /// A log with lower than the value of this property cannot be outputted. - /// - /// - /// One of the enum values, specifies the current logging level. - /// - public LogLevel Level { - get { - return _level; - } - - set { - lock (_sync) { - _level = value; - Warn (String.Format ("The current logging level has been changed to {0}.", _level)); + /// + /// Gets or sets the current logging level. + /// + public LogLevel Level + { + get + { + return _level; + } + + set + { + lock (_sync) + { + _level = value; + Warn(String.Format("The current logging level has been changed to {0}.", _level)); + } + } } - } - } - /// - /// Gets or sets the current output action used to output a log. - /// - /// - /// - /// An Action<LogData, string> delegate that references the method(s) used to - /// output a log. A parameter passed to this delegate is the value of - /// the property. - /// - /// - /// If the value to set is , the current output action is changed to - /// the default output action. - /// - /// - public Action Output { - get { - return _output; - } - - set { - lock (_sync) { - _output = value ?? defaultOutput; - Warn ("The current output action has been changed."); + /// + /// Gets or sets the current output action used to output a log. + /// + public Action Output + { + get + { + return _output; + } + + set + { + lock (_sync) + { + _output = value ?? defaultOutput; + Warn("The current output action has been changed."); + } + } } - } - } - #endregion + #endregion - #region Private Methods + #region - private static void defaultOutput (LogData data, string path) - { - var log = data.ToString (); - Console.WriteLine (log); - if (path != null && path.Length > 0) - writeToFile (log, path); - } + /// + /// Outputs as a log with . + /// + /// The message + public void Debug(string message) + { + if (_level > LogLevel.Debug) + return; - private void output (string message, LogLevel level) - { - lock (_sync) { - if (_level > level) - return; - - LogData data = null; - try { - data = new LogData (level, new StackFrame (2, true), message); - _output (data, _file); - } - catch (Exception ex) { - data = new LogData (LogLevel.Fatal, new StackFrame (0, true), ex.Message); - Console.WriteLine (data.ToString ()); + output(message, LogLevel.Debug); } - } - } - - private static void writeToFile (string value, string path) - { - using (var writer = new StreamWriter (path, true)) - using (var syncWriter = TextWriter.Synchronized (writer)) - syncWriter.WriteLine (value); - } - #endregion + /// + /// Outputs as a log with . + /// + /// The message + public void Error(string message) + { + if (_level > LogLevel.Error) + return; - #region Public Methods + output(message, LogLevel.Error); + } - /// - /// Outputs as a log with . - /// - /// - /// If the current logging level is higher than , - /// this method doesn't output as a log. - /// - /// - /// A that represents the message to output as a log. - /// - public void Debug (string message) - { - if (_level > LogLevel.Debug) - return; + /// + /// Outputs as a log with . + /// + /// The message + public void Fatal(string message) + { + output(message, LogLevel.Fatal); + } - output (message, LogLevel.Debug); - } + /// + /// Outputs as a log with . + /// + /// The message + public void Info(string message) + { + if (_level > LogLevel.Info) + return; - /// - /// Outputs as a log with . - /// - /// - /// If the current logging level is higher than , - /// this method doesn't output as a log. - /// - /// - /// A that represents the message to output as a log. - /// - public void Error (string message) - { - if (_level > LogLevel.Error) - return; + output(message, LogLevel.Info); + } - output (message, LogLevel.Error); - } + /// + /// Outputs as a log with . + /// + /// The message + public void Trace(string message) + { + if (_level > LogLevel.Trace) + return; - /// - /// Outputs as a log with . - /// - /// - /// A that represents the message to output as a log. - /// - public void Fatal (string message) - { - output (message, LogLevel.Fatal); - } + output(message, LogLevel.Trace); + } - /// - /// Outputs as a log with . - /// - /// - /// If the current logging level is higher than , - /// this method doesn't output as a log. - /// - /// - /// A that represents the message to output as a log. - /// - public void Info (string message) - { - if (_level > LogLevel.Info) - return; + /// + /// Outputs as a log with . + /// + /// The message + public void Warn(string message) + { + if (_level > LogLevel.Warn) + return; - output (message, LogLevel.Info); - } + output(message, LogLevel.Warn); + } - /// - /// Outputs as a log with . - /// - /// - /// If the current logging level is higher than , - /// this method doesn't output as a log. - /// - /// - /// A that represents the message to output as a log. - /// - public void Trace (string message) - { - if (_level > LogLevel.Trace) - return; + /// + /// The defaultOutput + /// + /// The data + /// The path + private static void defaultOutput(LogData data, string path) + { + var log = data.ToString(); + Console.WriteLine(log); + if (path != null && path.Length > 0) + writeToFile(log, path); + } - output (message, LogLevel.Trace); - } + /// + /// The writeToFile + /// + /// The value + /// The path + private static void writeToFile(string value, string path) + { + using (var writer = new StreamWriter(path, true)) + using (var syncWriter = TextWriter.Synchronized(writer)) + syncWriter.WriteLine(value); + } - /// - /// Outputs as a log with . - /// - /// - /// If the current logging level is higher than , - /// this method doesn't output as a log. - /// - /// - /// A that represents the message to output as a log. - /// - public void Warn (string message) - { - if (_level > LogLevel.Warn) - return; + /// + /// The output + /// + /// The message + /// The level + private void output(string message, LogLevel level) + { + lock (_sync) + { + if (_level > level) + return; + + LogData data = null; + try + { + data = new LogData(level, new StackFrame(2, true), message); + _output(data, _file); + } + catch (Exception ex) + { + data = new LogData(LogLevel.Fatal, new StackFrame(0, true), ex.Message); + Console.WriteLine(data.ToString()); + } + } + } - output (message, LogLevel.Warn); + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Mask.cs b/src/WebSocket/WebSocketCore/Mask.cs index 5830b9f82..d3465a399 100644 --- a/src/WebSocket/WebSocketCore/Mask.cs +++ b/src/WebSocket/WebSocketCore/Mask.cs @@ -1,4 +1,3 @@ -#region License /* * Mask.cs * @@ -24,28 +23,32 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; namespace WebSocketCore { - /// - /// Indicates whether the payload data of a WebSocket frame is masked. - /// - /// - /// The values of this enumeration are defined in - /// Section 5.2 of RFC 6455. - /// - internal enum Mask : byte - { - /// - /// Equivalent to numeric value 0. Indicates not masked. - /// - Off = 0x0, + #region ö + /// - /// Equivalent to numeric value 1. Indicates masked. + /// Indicates whether the payload data of a WebSocket frame is masked. /// - On = 0x1 - } -} + /// + /// The values of this enumeration are defined in + /// Section 5.2 of RFC 6455. + /// + internal enum Mask : byte + { + /// + /// Equivalent to numeric value 0. Indicates not masked. + /// + Off = 0x0, + + /// + /// Equivalent to numeric value 1. Indicates masked. + /// + On = 0x1 + } + + #endregion ö +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/MessageEventArgs.cs b/src/WebSocket/WebSocketCore/MessageEventArgs.cs index 0b02132a0..734b4e75b 100644 --- a/src/WebSocket/WebSocketCore/MessageEventArgs.cs +++ b/src/WebSocket/WebSocketCore/MessageEventArgs.cs @@ -1,4 +1,3 @@ -#region License /* * MessageEventArgs.cs * @@ -24,157 +23,160 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; namespace WebSocketCore { - /// - /// Represents the event data for the event. - /// - /// - /// - /// That event occurs when the receives - /// a message or a ping if the - /// property is set to true. - /// - /// - /// If you would like to get the message data, you should access - /// the or property. - /// - /// - public class MessageEventArgs : EventArgs - { - #region Private Fields - - private string _data; - private bool _dataSet; - private Opcode _opcode; - private byte[] _rawData; - - #endregion - - #region Internal Constructors - - internal MessageEventArgs (WebSocketFrame frame) - { - _opcode = frame.Opcode; - _rawData = frame.PayloadData.ApplicationData; - } - - internal MessageEventArgs (Opcode opcode, byte[] rawData) - { - if ((ulong) rawData.LongLength > PayloadData.MaxLength) - throw new WebSocketException (CloseStatusCode.TooBig); - - _opcode = opcode; - _rawData = rawData; - } - - #endregion - - #region Internal Properties - - /// - /// Gets the opcode for the message. - /// - /// - /// , , - /// or . - /// - internal Opcode Opcode { - get { - return _opcode; - } - } - - #endregion - - #region Public Properties - - /// - /// Gets the message data as a . - /// - /// - /// A that represents the message data if its type is - /// text or ping and if decoding it to a string has successfully done; - /// otherwise, . - /// - public string Data { - get { - setData (); - return _data; - } - } - /// - /// Gets a value indicating whether the message type is binary. + /// Represents the event data for the event. /// - /// - /// true if the message type is binary; otherwise, false. - /// - public bool IsBinary { - get { - return _opcode == Opcode.Binary; - } - } - - /// - /// Gets a value indicating whether the message type is ping. - /// - /// - /// true if the message type is ping; otherwise, false. - /// - public bool IsPing { - get { - return _opcode == Opcode.Ping; - } - } - - /// - /// Gets a value indicating whether the message type is text. - /// - /// - /// true if the message type is text; otherwise, false. - /// - public bool IsText { - get { - return _opcode == Opcode.Text; - } - } - - /// - /// Gets the message data as an array of . - /// - /// - /// An array of that represents the message data. - /// - public byte[] RawData { - get { - setData (); - return _rawData; - } - } - - #endregion - - #region Private Methods - - private void setData () + public class MessageEventArgs : EventArgs { - if (_dataSet) - return; - - if (_opcode == Opcode.Binary) { - _dataSet = true; - return; - } - - _data = _rawData.UTF8Decode (); - _dataSet = true; + #region ֶ + + /// + /// Defines the _data + /// + private string _data; + + /// + /// Defines the _dataSet + /// + private bool _dataSet; + + /// + /// Defines the _opcode + /// + private Opcode _opcode; + + /// + /// Defines the _rawData + /// + private byte[] _rawData; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The opcode + /// The rawData + internal MessageEventArgs(Opcode opcode, byte[] rawData) + { + if ((ulong)rawData.LongLength > PayloadData.MaxLength) + throw new WebSocketException(CloseStatusCode.TooBig); + + _opcode = opcode; + _rawData = rawData; + } + + /// + /// Initializes a new instance of the class. + /// + /// The frame + internal MessageEventArgs(WebSocketFrame frame) + { + _opcode = frame.Opcode; + _rawData = frame.PayloadData.ApplicationData; + } + + #endregion 캯 + + #region + + /// + /// Gets the message data as a . + /// + public string Data + { + get + { + setData(); + return _data; + } + } + + /// + /// Gets a value indicating whether the message type is binary. + /// + public bool IsBinary + { + get + { + return _opcode == Opcode.Binary; + } + } + + /// + /// Gets a value indicating whether the message type is ping. + /// + public bool IsPing + { + get + { + return _opcode == Opcode.Ping; + } + } + + /// + /// Gets a value indicating whether the message type is text. + /// + public bool IsText + { + get + { + return _opcode == Opcode.Text; + } + } + + /// + /// Gets the message data as an array of . + /// + public byte[] RawData + { + get + { + setData(); + return _rawData; + } + } + + /// + /// Gets the opcode for the message. + /// + internal Opcode Opcode + { + get + { + return _opcode; + } + } + + #endregion + + #region + + /// + /// The setData + /// + private void setData() + { + if (_dataSet) + return; + + if (_opcode == Opcode.Binary) + { + _dataSet = true; + return; + } + + _data = _rawData.UTF8Decode(); + _dataSet = true; + } + + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/AuthenticationBase.cs b/src/WebSocket/WebSocketCore/Net/AuthenticationBase.cs index ad927a490..a90bb8124 100644 --- a/src/WebSocket/WebSocketCore/Net/AuthenticationBase.cs +++ b/src/WebSocket/WebSocketCore/Net/AuthenticationBase.cs @@ -1,4 +1,3 @@ -#region License /* * AuthenticationBase.cs * @@ -24,7 +23,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; using System.Collections.Specialized; @@ -32,120 +30,178 @@ namespace WebSocketCore.Net { - internal abstract class AuthenticationBase - { - #region Private Fields - - private AuthenticationSchemes _scheme; - - #endregion - - #region Internal Fields - - internal NameValueCollection Parameters; - - #endregion - - #region Protected Constructors - - protected AuthenticationBase (AuthenticationSchemes scheme, NameValueCollection parameters) + /// + /// Defines the + /// + internal abstract class AuthenticationBase { - _scheme = scheme; - Parameters = parameters; - } - - #endregion - - #region Public Properties - - public string Algorithm { - get { - return Parameters["algorithm"]; - } - } - - public string Nonce { - get { - return Parameters["nonce"]; - } - } - - public string Opaque { - get { - return Parameters["opaque"]; - } + #region ֶ + + /// + /// Defines the Parameters + /// + internal NameValueCollection Parameters; + + /// + /// Defines the _scheme + /// + private AuthenticationSchemes _scheme; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The scheme + /// The parameters + protected AuthenticationBase(AuthenticationSchemes scheme, NameValueCollection parameters) + { + _scheme = scheme; + Parameters = parameters; + } + + #endregion 캯 + + #region + + /// + /// Gets the Algorithm + /// + public string Algorithm + { + get + { + return Parameters["algorithm"]; + } + } + + /// + /// Gets the Nonce + /// + public string Nonce + { + get + { + return Parameters["nonce"]; + } + } + + /// + /// Gets the Opaque + /// + public string Opaque + { + get + { + return Parameters["opaque"]; + } + } + + /// + /// Gets the Qop + /// + public string Qop + { + get + { + return Parameters["qop"]; + } + } + + /// + /// Gets the Realm + /// + public string Realm + { + get + { + return Parameters["realm"]; + } + } + + /// + /// Gets the Scheme + /// + public AuthenticationSchemes Scheme + { + get + { + return _scheme; + } + } + + #endregion + + #region + + /// + /// The ToString + /// + /// The + public override string ToString() + { + return _scheme == AuthenticationSchemes.Basic + ? ToBasicString() + : _scheme == AuthenticationSchemes.Digest + ? ToDigestString() + : String.Empty; + } + + /// + /// The CreateNonceValue + /// + /// The + internal static string CreateNonceValue() + { + var src = new byte[16]; + var rand = new Random(); + rand.NextBytes(src); + + var res = new StringBuilder(32); + foreach (var b in src) + res.Append(b.ToString("x2")); + + return res.ToString(); + } + + /// + /// The ParseParameters + /// + /// The value + /// The + internal static NameValueCollection ParseParameters(string value) + { + var res = new NameValueCollection(); + foreach (var param in value.SplitHeaderValue(',')) + { + var i = param.IndexOf('='); + var name = i > 0 ? param.Substring(0, i).Trim() : null; + var val = i < 0 + ? param.Trim().Trim('"') + : i < param.Length - 1 + ? param.Substring(i + 1).Trim().Trim('"') + : String.Empty; + + res.Add(name, val); + } + + return res; + } + + /// + /// The ToBasicString + /// + /// The + internal abstract string ToBasicString(); + + /// + /// The ToDigestString + /// + /// The + internal abstract string ToDigestString(); + + #endregion } - - public string Qop { - get { - return Parameters["qop"]; - } - } - - public string Realm { - get { - return Parameters["realm"]; - } - } - - public AuthenticationSchemes Scheme { - get { - return _scheme; - } - } - - #endregion - - #region Internal Methods - - internal static string CreateNonceValue () - { - var src = new byte[16]; - var rand = new Random (); - rand.NextBytes (src); - - var res = new StringBuilder (32); - foreach (var b in src) - res.Append (b.ToString ("x2")); - - return res.ToString (); - } - - internal static NameValueCollection ParseParameters (string value) - { - var res = new NameValueCollection (); - foreach (var param in value.SplitHeaderValue (',')) { - var i = param.IndexOf ('='); - var name = i > 0 ? param.Substring (0, i).Trim () : null; - var val = i < 0 - ? param.Trim ().Trim ('"') - : i < param.Length - 1 - ? param.Substring (i + 1).Trim ().Trim ('"') - : String.Empty; - - res.Add (name, val); - } - - return res; - } - - internal abstract string ToBasicString (); - - internal abstract string ToDigestString (); - - #endregion - - #region Public Methods - - public override string ToString () - { - return _scheme == AuthenticationSchemes.Basic - ? ToBasicString () - : _scheme == AuthenticationSchemes.Digest - ? ToDigestString () - : String.Empty; - } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/AuthenticationChallenge.cs b/src/WebSocket/WebSocketCore/Net/AuthenticationChallenge.cs index cfefe788e..6992f354d 100644 --- a/src/WebSocket/WebSocketCore/Net/AuthenticationChallenge.cs +++ b/src/WebSocket/WebSocketCore/Net/AuthenticationChallenge.cs @@ -1,4 +1,3 @@ -#region License /* * AuthenticationChallenge.cs * @@ -24,7 +23,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; using System.Collections.Specialized; @@ -32,115 +30,158 @@ namespace WebSocketCore.Net { - internal class AuthenticationChallenge : AuthenticationBase - { - #region Private Constructors - - private AuthenticationChallenge (AuthenticationSchemes scheme, NameValueCollection parameters) - : base (scheme, parameters) - { - } - - #endregion - - #region Internal Constructors - - internal AuthenticationChallenge (AuthenticationSchemes scheme, string realm) - : base (scheme, new NameValueCollection ()) - { - Parameters["realm"] = realm; - if (scheme == AuthenticationSchemes.Digest) { - Parameters["nonce"] = CreateNonceValue (); - Parameters["algorithm"] = "MD5"; - Parameters["qop"] = "auth"; - } - } - - #endregion - - #region Public Properties - - public string Domain { - get { - return Parameters["domain"]; - } - } - - public string Stale { - get { - return Parameters["stale"]; - } - } - - #endregion - - #region Internal Methods - - internal static AuthenticationChallenge CreateBasicChallenge (string realm) - { - return new AuthenticationChallenge (AuthenticationSchemes.Basic, realm); - } - - internal static AuthenticationChallenge CreateDigestChallenge (string realm) - { - return new AuthenticationChallenge (AuthenticationSchemes.Digest, realm); - } - - internal static AuthenticationChallenge Parse (string value) + /// + /// Defines the + /// + internal class AuthenticationChallenge : AuthenticationBase { - var chal = value.Split (new[] { ' ' }, 2); - if (chal.Length != 2) - return null; - - var schm = chal[0].ToLower (); - return schm == "basic" - ? new AuthenticationChallenge ( - AuthenticationSchemes.Basic, ParseParameters (chal[1])) - : schm == "digest" - ? new AuthenticationChallenge ( - AuthenticationSchemes.Digest, ParseParameters (chal[1])) - : null; + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The scheme + /// The realm + internal AuthenticationChallenge(AuthenticationSchemes scheme, string realm) + : base(scheme, new NameValueCollection()) + { + Parameters["realm"] = realm; + if (scheme == AuthenticationSchemes.Digest) + { + Parameters["nonce"] = CreateNonceValue(); + Parameters["algorithm"] = "MD5"; + Parameters["qop"] = "auth"; + } + } + + /// + /// Prevents a default instance of the class from being created. + /// + /// The scheme + /// The parameters + private AuthenticationChallenge(AuthenticationSchemes scheme, NameValueCollection parameters) + : base(scheme, parameters) + { + } + + #endregion 캯 + + #region + + /// + /// Gets the Domain + /// + public string Domain + { + get + { + return Parameters["domain"]; + } + } + + /// + /// Gets the Stale + /// + public string Stale + { + get + { + return Parameters["stale"]; + } + } + + #endregion + + #region + + /// + /// The CreateBasicChallenge + /// + /// The realm + /// The + internal static AuthenticationChallenge CreateBasicChallenge(string realm) + { + return new AuthenticationChallenge(AuthenticationSchemes.Basic, realm); + } + + /// + /// The CreateDigestChallenge + /// + /// The realm + /// The + internal static AuthenticationChallenge CreateDigestChallenge(string realm) + { + return new AuthenticationChallenge(AuthenticationSchemes.Digest, realm); + } + + /// + /// The Parse + /// + /// The value + /// The + internal static AuthenticationChallenge Parse(string value) + { + var chal = value.Split(new[] { ' ' }, 2); + if (chal.Length != 2) + return null; + + var schm = chal[0].ToLower(); + return schm == "basic" + ? new AuthenticationChallenge( + AuthenticationSchemes.Basic, ParseParameters(chal[1])) + : schm == "digest" + ? new AuthenticationChallenge( + AuthenticationSchemes.Digest, ParseParameters(chal[1])) + : null; + } + + /// + /// The ToBasicString + /// + /// The + internal override string ToBasicString() + { + return String.Format("Basic realm=\"{0}\"", Parameters["realm"]); + } + + /// + /// The ToDigestString + /// + /// The + internal override string ToDigestString() + { + var output = new StringBuilder(128); + + var domain = Parameters["domain"]; + if (domain != null) + output.AppendFormat( + "Digest realm=\"{0}\", domain=\"{1}\", nonce=\"{2}\"", + Parameters["realm"], + domain, + Parameters["nonce"]); + else + output.AppendFormat( + "Digest realm=\"{0}\", nonce=\"{1}\"", Parameters["realm"], Parameters["nonce"]); + + var opaque = Parameters["opaque"]; + if (opaque != null) + output.AppendFormat(", opaque=\"{0}\"", opaque); + + var stale = Parameters["stale"]; + if (stale != null) + output.AppendFormat(", stale={0}", stale); + + var algo = Parameters["algorithm"]; + if (algo != null) + output.AppendFormat(", algorithm={0}", algo); + + var qop = Parameters["qop"]; + if (qop != null) + output.AppendFormat(", qop=\"{0}\"", qop); + + return output.ToString(); + } + + #endregion } - - internal override string ToBasicString () - { - return String.Format ("Basic realm=\"{0}\"", Parameters["realm"]); - } - - internal override string ToDigestString () - { - var output = new StringBuilder (128); - - var domain = Parameters["domain"]; - if (domain != null) - output.AppendFormat ( - "Digest realm=\"{0}\", domain=\"{1}\", nonce=\"{2}\"", - Parameters["realm"], - domain, - Parameters["nonce"]); - else - output.AppendFormat ( - "Digest realm=\"{0}\", nonce=\"{1}\"", Parameters["realm"], Parameters["nonce"]); - - var opaque = Parameters["opaque"]; - if (opaque != null) - output.AppendFormat (", opaque=\"{0}\"", opaque); - - var stale = Parameters["stale"]; - if (stale != null) - output.AppendFormat (", stale={0}", stale); - - var algo = Parameters["algorithm"]; - if (algo != null) - output.AppendFormat (", algorithm={0}", algo); - - var qop = Parameters["qop"]; - if (qop != null) - output.AppendFormat (", qop=\"{0}\"", qop); - - return output.ToString (); - } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/AuthenticationResponse.cs b/src/WebSocket/WebSocketCore/Net/AuthenticationResponse.cs index 14952e09e..0aa18f259 100644 --- a/src/WebSocket/WebSocketCore/Net/AuthenticationResponse.cs +++ b/src/WebSocket/WebSocketCore/Net/AuthenticationResponse.cs @@ -1,4 +1,3 @@ -#region License /* * AuthenticationResponse.cs * @@ -28,7 +27,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; using System.Collections.Specialized; @@ -38,286 +36,402 @@ namespace WebSocketCore.Net { - internal class AuthenticationResponse : AuthenticationBase - { - #region Private Fields - - private uint _nonceCount; - - #endregion - - #region Private Constructors - - private AuthenticationResponse (AuthenticationSchemes scheme, NameValueCollection parameters) - : base (scheme, parameters) + /// + /// Defines the + /// + internal class AuthenticationResponse : AuthenticationBase { - } + #region ֶ - #endregion + /// + /// Defines the _nonceCount + /// + private uint _nonceCount; - #region Internal Constructors + #endregion ֶ - internal AuthenticationResponse (NetworkCredential credentials) - : this (AuthenticationSchemes.Basic, new NameValueCollection (), credentials, 0) - { - } + #region 캯 - internal AuthenticationResponse ( + /// + /// Initializes a new instance of the class. + /// + /// The challenge + /// The credentials + /// The nonceCount + internal AuthenticationResponse( AuthenticationChallenge challenge, NetworkCredential credentials, uint nonceCount) - : this (challenge.Scheme, challenge.Parameters, credentials, nonceCount) - { - } + : this(challenge.Scheme, challenge.Parameters, credentials, nonceCount) + { + } - internal AuthenticationResponse ( + /// + /// Initializes a new instance of the class. + /// + /// The scheme + /// The parameters + /// The credentials + /// The nonceCount + internal AuthenticationResponse( AuthenticationSchemes scheme, NameValueCollection parameters, NetworkCredential credentials, uint nonceCount) - : base (scheme, parameters) - { - Parameters["username"] = credentials.Username; - Parameters["password"] = credentials.Password; - Parameters["uri"] = credentials.Domain; - _nonceCount = nonceCount; - if (scheme == AuthenticationSchemes.Digest) - initAsDigest (); - } - - #endregion - - #region Internal Properties - - internal uint NonceCount { - get { - return _nonceCount < UInt32.MaxValue - ? _nonceCount - : 0; - } - } - - #endregion - - #region Public Properties - - public string Cnonce { - get { - return Parameters["cnonce"]; - } - } - - public string Nc { - get { - return Parameters["nc"]; - } - } - - public string Password { - get { - return Parameters["password"]; - } - } - - public string Response { - get { - return Parameters["response"]; - } - } - - public string Uri { - get { - return Parameters["uri"]; - } - } - - public string UserName { - get { - return Parameters["username"]; - } - } + : base(scheme, parameters) + { + Parameters["username"] = credentials.Username; + Parameters["password"] = credentials.Password; + Parameters["uri"] = credentials.Domain; + _nonceCount = nonceCount; + if (scheme == AuthenticationSchemes.Digest) + initAsDigest(); + } - #endregion + /// + /// Initializes a new instance of the class. + /// + /// The credentials + internal AuthenticationResponse(NetworkCredential credentials) + : this(AuthenticationSchemes.Basic, new NameValueCollection(), credentials, 0) + { + } - #region Private Methods + /// + /// Prevents a default instance of the class from being created. + /// + /// The scheme + /// The parameters + private AuthenticationResponse(AuthenticationSchemes scheme, NameValueCollection parameters) + : base(scheme, parameters) + { + } - private static string createA1 (string username, string password, string realm) - { - return String.Format ("{0}:{1}:{2}", username, realm, password); - } + #endregion 캯 - private static string createA1 ( - string username, string password, string realm, string nonce, string cnonce) - { - return String.Format ( - "{0}:{1}:{2}", hash (createA1 (username, password, realm)), nonce, cnonce); - } + #region - private static string createA2 (string method, string uri) - { - return String.Format ("{0}:{1}", method, uri); - } - - private static string createA2 (string method, string uri, string entity) - { - return String.Format ("{0}:{1}:{2}", method, uri, hash (entity)); - } - - private static string hash (string value) - { - var src = Encoding.UTF8.GetBytes (value); - var md5 = MD5.Create (); - var hashed = md5.ComputeHash (src); + /// + /// Gets the Cnonce + /// + public string Cnonce + { + get + { + return Parameters["cnonce"]; + } + } - var res = new StringBuilder (64); - foreach (var b in hashed) - res.Append (b.ToString ("x2")); + /// + /// Gets the Nc + /// + public string Nc + { + get + { + return Parameters["nc"]; + } + } - return res.ToString (); - } + /// + /// Gets the Password + /// + public string Password + { + get + { + return Parameters["password"]; + } + } - private void initAsDigest () - { - var qops = Parameters["qop"]; - if (qops != null) { - if (qops.Split (',').Contains (qop => qop.Trim ().ToLower () == "auth")) { - Parameters["qop"] = "auth"; - Parameters["cnonce"] = CreateNonceValue (); - Parameters["nc"] = String.Format ("{0:x8}", ++_nonceCount); + /// + /// Gets the Response + /// + public string Response + { + get + { + return Parameters["response"]; + } } - else { - Parameters["qop"] = null; + + /// + /// Gets the Uri + /// + public string Uri + { + get + { + return Parameters["uri"]; + } } - } - Parameters["method"] = "GET"; - Parameters["response"] = CreateRequestDigest (Parameters); - } + /// + /// Gets the UserName + /// + public string UserName + { + get + { + return Parameters["username"]; + } + } - #endregion + /// + /// Gets the NonceCount + /// + internal uint NonceCount + { + get + { + return _nonceCount < UInt32.MaxValue + ? _nonceCount + : 0; + } + } - #region Internal Methods + #endregion + + #region + + /// + /// The ToIdentity + /// + /// The + public IIdentity ToIdentity() + { + var schm = Scheme; + return schm == AuthenticationSchemes.Basic + ? new HttpBasicIdentity(Parameters["username"], Parameters["password"]) as IIdentity + : schm == AuthenticationSchemes.Digest + ? new HttpDigestIdentity(Parameters) + : null; + } - internal static string CreateRequestDigest (NameValueCollection parameters) - { - var user = parameters["username"]; - var pass = parameters["password"]; - var realm = parameters["realm"]; - var nonce = parameters["nonce"]; - var uri = parameters["uri"]; - var algo = parameters["algorithm"]; - var qop = parameters["qop"]; - var cnonce = parameters["cnonce"]; - var nc = parameters["nc"]; - var method = parameters["method"]; - - var a1 = algo != null && algo.ToLower () == "md5-sess" - ? createA1 (user, pass, realm, nonce, cnonce) - : createA1 (user, pass, realm); - - var a2 = qop != null && qop.ToLower () == "auth-int" - ? createA2 (method, uri, parameters["entity"]) - : createA2 (method, uri); - - var secret = hash (a1); - var data = qop != null - ? String.Format ("{0}:{1}:{2}:{3}:{4}", nonce, nc, cnonce, qop, hash (a2)) - : String.Format ("{0}:{1}", nonce, hash (a2)); - - return hash (String.Format ("{0}:{1}", secret, data)); - } + /// + /// The CreateRequestDigest + /// + /// The parameters + /// The + internal static string CreateRequestDigest(NameValueCollection parameters) + { + var user = parameters["username"]; + var pass = parameters["password"]; + var realm = parameters["realm"]; + var nonce = parameters["nonce"]; + var uri = parameters["uri"]; + var algo = parameters["algorithm"]; + var qop = parameters["qop"]; + var cnonce = parameters["cnonce"]; + var nc = parameters["nc"]; + var method = parameters["method"]; + + var a1 = algo != null && algo.ToLower() == "md5-sess" + ? createA1(user, pass, realm, nonce, cnonce) + : createA1(user, pass, realm); + + var a2 = qop != null && qop.ToLower() == "auth-int" + ? createA2(method, uri, parameters["entity"]) + : createA2(method, uri); + + var secret = hash(a1); + var data = qop != null + ? String.Format("{0}:{1}:{2}:{3}:{4}", nonce, nc, cnonce, qop, hash(a2)) + : String.Format("{0}:{1}", nonce, hash(a2)); + + return hash(String.Format("{0}:{1}", secret, data)); + } - internal static AuthenticationResponse Parse (string value) - { - try { - var cred = value.Split (new[] { ' ' }, 2); - if (cred.Length != 2) - return null; - - var schm = cred[0].ToLower (); - return schm == "basic" - ? new AuthenticationResponse ( - AuthenticationSchemes.Basic, ParseBasicCredentials (cred[1])) - : schm == "digest" - ? new AuthenticationResponse ( - AuthenticationSchemes.Digest, ParseParameters (cred[1])) - : null; - } - catch { - } - - return null; - } + /// + /// The Parse + /// + /// The value + /// The + internal static AuthenticationResponse Parse(string value) + { + try + { + var cred = value.Split(new[] { ' ' }, 2); + if (cred.Length != 2) + return null; + + var schm = cred[0].ToLower(); + return schm == "basic" + ? new AuthenticationResponse( + AuthenticationSchemes.Basic, ParseBasicCredentials(cred[1])) + : schm == "digest" + ? new AuthenticationResponse( + AuthenticationSchemes.Digest, ParseParameters(cred[1])) + : null; + } + catch + { + } + + return null; + } - internal static NameValueCollection ParseBasicCredentials (string value) - { - // Decode the basic-credentials (a Base64 encoded string). - var userPass = Encoding.Default.GetString (Convert.FromBase64String (value)); + /// + /// The ParseBasicCredentials + /// + /// The value + /// The + internal static NameValueCollection ParseBasicCredentials(string value) + { + // Decode the basic-credentials (a Base64 encoded string). + var userPass = Encoding.Default.GetString(Convert.FromBase64String(value)); + + // The format is [\]:. + var i = userPass.IndexOf(':'); + var user = userPass.Substring(0, i); + var pass = i < userPass.Length - 1 ? userPass.Substring(i + 1) : String.Empty; + + // Check if 'domain' exists. + i = user.IndexOf('\\'); + if (i > -1) + user = user.Substring(i + 1); + + var res = new NameValueCollection(); + res["username"] = user; + res["password"] = pass; + + return res; + } - // The format is [\]:. - var i = userPass.IndexOf (':'); - var user = userPass.Substring (0, i); - var pass = i < userPass.Length - 1 ? userPass.Substring (i + 1) : String.Empty; + /// + /// The ToBasicString + /// + /// The + internal override string ToBasicString() + { + var userPass = String.Format("{0}:{1}", Parameters["username"], Parameters["password"]); + var cred = Convert.ToBase64String(Encoding.UTF8.GetBytes(userPass)); - // Check if 'domain' exists. - i = user.IndexOf ('\\'); - if (i > -1) - user = user.Substring (i + 1); + return "Basic " + cred; + } - var res = new NameValueCollection (); - res["username"] = user; - res["password"] = pass; + /// + /// The ToDigestString + /// + /// The + internal override string ToDigestString() + { + var output = new StringBuilder(256); + output.AppendFormat( + "Digest username=\"{0}\", realm=\"{1}\", nonce=\"{2}\", uri=\"{3}\", response=\"{4}\"", + Parameters["username"], + Parameters["realm"], + Parameters["nonce"], + Parameters["uri"], + Parameters["response"]); + + var opaque = Parameters["opaque"]; + if (opaque != null) + output.AppendFormat(", opaque=\"{0}\"", opaque); + + var algo = Parameters["algorithm"]; + if (algo != null) + output.AppendFormat(", algorithm={0}", algo); + + var qop = Parameters["qop"]; + if (qop != null) + output.AppendFormat( + ", qop={0}, cnonce=\"{1}\", nc={2}", qop, Parameters["cnonce"], Parameters["nc"]); + + return output.ToString(); + } - return res; - } + /// + /// The createA1 + /// + /// The username + /// The password + /// The realm + /// The nonce + /// The cnonce + /// The + private static string createA1( + string username, string password, string realm, string nonce, string cnonce) + { + return String.Format( + "{0}:{1}:{2}", hash(createA1(username, password, realm)), nonce, cnonce); + } - internal override string ToBasicString () - { - var userPass = String.Format ("{0}:{1}", Parameters["username"], Parameters["password"]); - var cred = Convert.ToBase64String (Encoding.UTF8.GetBytes (userPass)); + /// + /// The createA1 + /// + /// The username + /// The password + /// The realm + /// The + private static string createA1(string username, string password, string realm) + { + return String.Format("{0}:{1}:{2}", username, realm, password); + } - return "Basic " + cred; - } + /// + /// The createA2 + /// + /// The method + /// The uri + /// The + private static string createA2(string method, string uri) + { + return String.Format("{0}:{1}", method, uri); + } - internal override string ToDigestString () - { - var output = new StringBuilder (256); - output.AppendFormat ( - "Digest username=\"{0}\", realm=\"{1}\", nonce=\"{2}\", uri=\"{3}\", response=\"{4}\"", - Parameters["username"], - Parameters["realm"], - Parameters["nonce"], - Parameters["uri"], - Parameters["response"]); - - var opaque = Parameters["opaque"]; - if (opaque != null) - output.AppendFormat (", opaque=\"{0}\"", opaque); - - var algo = Parameters["algorithm"]; - if (algo != null) - output.AppendFormat (", algorithm={0}", algo); - - var qop = Parameters["qop"]; - if (qop != null) - output.AppendFormat ( - ", qop={0}, cnonce=\"{1}\", nc={2}", qop, Parameters["cnonce"], Parameters["nc"]); - - return output.ToString (); - } + /// + /// The createA2 + /// + /// The method + /// The uri + /// The entity + /// The + private static string createA2(string method, string uri, string entity) + { + return String.Format("{0}:{1}:{2}", method, uri, hash(entity)); + } - #endregion + /// + /// The hash + /// + /// The value + /// The + private static string hash(string value) + { + var src = Encoding.UTF8.GetBytes(value); + var md5 = MD5.Create(); + var hashed = md5.ComputeHash(src); + + var res = new StringBuilder(64); + foreach (var b in hashed) + res.Append(b.ToString("x2")); + + return res.ToString(); + } - #region Public Methods + /// + /// The initAsDigest + /// + private void initAsDigest() + { + var qops = Parameters["qop"]; + if (qops != null) + { + if (qops.Split(',').Contains(qop => qop.Trim().ToLower() == "auth")) + { + Parameters["qop"] = "auth"; + Parameters["cnonce"] = CreateNonceValue(); + Parameters["nc"] = String.Format("{0:x8}", ++_nonceCount); + } + else + { + Parameters["qop"] = null; + } + } + + Parameters["method"] = "GET"; + Parameters["response"] = CreateRequestDigest(Parameters); + } - public IIdentity ToIdentity () - { - var schm = Scheme; - return schm == AuthenticationSchemes.Basic - ? new HttpBasicIdentity (Parameters["username"], Parameters["password"]) as IIdentity - : schm == AuthenticationSchemes.Digest - ? new HttpDigestIdentity (Parameters) - : null; + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/AuthenticationSchemes.cs b/src/WebSocket/WebSocketCore/Net/AuthenticationSchemes.cs index 7981477eb..ff9217c3c 100644 --- a/src/WebSocket/WebSocketCore/Net/AuthenticationSchemes.cs +++ b/src/WebSocket/WebSocketCore/Net/AuthenticationSchemes.cs @@ -1,4 +1,3 @@ -#region License /* * AuthenticationSchemes.cs * @@ -28,39 +27,43 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Atsushi Enomoto */ -#endregion using System; namespace WebSocketCore.Net { - /// - /// Specifies the scheme for authentication. - /// - public enum AuthenticationSchemes - { - /// - /// No authentication is allowed. - /// - None, - /// - /// Specifies digest authentication. - /// - Digest = 1, - /// - /// Specifies basic authentication. - /// - Basic = 8, + #region ö + /// - /// Specifies anonymous authentication. + /// Specifies the scheme for authentication. /// - Anonymous = 0x8000 - } -} + public enum AuthenticationSchemes + { + /// + /// No authentication is allowed. + /// + None, + + /// + /// Specifies digest authentication. + /// + Digest = 1, + + /// + /// Specifies basic authentication. + /// + Basic = 8, + + /// + /// Specifies anonymous authentication. + /// + Anonymous = 0x8000 + } + + #endregion ö +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/Chunk.cs b/src/WebSocket/WebSocketCore/Net/Chunk.cs index 7a310a3a7..94e7fdb61 100644 --- a/src/WebSocket/WebSocketCore/Net/Chunk.cs +++ b/src/WebSocket/WebSocketCore/Net/Chunk.cs @@ -1,4 +1,3 @@ -#region License /* * Chunk.cs * @@ -28,64 +27,87 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Gonzalo Paniagua Javier */ -#endregion using System; namespace WebSocketCore.Net { - internal class Chunk - { - #region Private Fields - - private byte[] _data; - private int _offset; - - #endregion - - #region Public Constructors - - public Chunk (byte[] data) + /// + /// Defines the + /// + internal class Chunk { - _data = data; + #region ֶ + + /// + /// Defines the _data + /// + private byte[] _data; + + /// + /// Defines the _offset + /// + private int _offset; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The data + public Chunk(byte[] data) + { + _data = data; + } + + #endregion 캯 + + #region + + /// + /// Gets the ReadLeft + /// + public int ReadLeft + { + get + { + return _data.Length - _offset; + } + } + + #endregion + + #region + + /// + /// The Read + /// + /// The buffer + /// The offset + /// The count + /// The + public int Read(byte[] buffer, int offset, int count) + { + var left = _data.Length - _offset; + if (left == 0) + return left; + + if (count > left) + count = left; + + Buffer.BlockCopy(_data, _offset, buffer, offset, count); + _offset += count; + + return count; + } + + #endregion } - - #endregion - - #region Public Properties - - public int ReadLeft { - get { - return _data.Length - _offset; - } - } - - #endregion - - #region Public Methods - - public int Read (byte[] buffer, int offset, int count) - { - var left = _data.Length - _offset; - if (left == 0) - return left; - - if (count > left) - count = left; - - Buffer.BlockCopy (_data, _offset, buffer, offset, count); - _offset += count; - - return count; - } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/ChunkStream.cs b/src/WebSocket/WebSocketCore/Net/ChunkStream.cs index 894d1d0cf..2e74abd49 100644 --- a/src/WebSocket/WebSocketCore/Net/ChunkStream.cs +++ b/src/WebSocket/WebSocketCore/Net/ChunkStream.cs @@ -1,4 +1,3 @@ -#region License /* * ChunkStream.cs * @@ -28,14 +27,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Gonzalo Paniagua Javier */ -#endregion using System; using System.Collections.Generic; @@ -46,315 +42,459 @@ namespace WebSocketCore.Net { - internal class ChunkStream - { - #region Private Fields - - private int _chunkRead; - private int _chunkSize; - private List _chunks; - private bool _gotIt; - private WebHeaderCollection _headers; - private StringBuilder _saved; - private bool _sawCr; - private InputChunkState _state; - private int _trailerState; - - #endregion - - #region Public Constructors - - public ChunkStream (WebHeaderCollection headers) + /// + /// Defines the + /// + internal class ChunkStream { - _headers = headers; - _chunkSize = -1; - _chunks = new List (); - _saved = new StringBuilder (); - } - - public ChunkStream (byte[] buffer, int offset, int count, WebHeaderCollection headers) - : this (headers) - { - Write (buffer, offset, count); - } - - #endregion - - #region Internal Properties - - internal WebHeaderCollection Headers { - get { - return _headers; - } - } - - #endregion - - #region Public Properties - - public int ChunkLeft { - get { - return _chunkSize - _chunkRead; - } - } - - public bool WantMore { - get { - return _state != InputChunkState.End; - } - } - - #endregion - - #region Private Methods - - private int read (byte[] buffer, int offset, int count) - { - var nread = 0; - - var cnt = _chunks.Count; - for (var i = 0; i < cnt; i++) { - var chunk = _chunks[i]; - if (chunk == null) - continue; - - if (chunk.ReadLeft == 0) { - _chunks[i] = null; - continue; + #region ֶ + + /// + /// Defines the _chunkRead + /// + private int _chunkRead; + + /// + /// Defines the _chunks + /// + private List _chunks; + + /// + /// Defines the _chunkSize + /// + private int _chunkSize; + + /// + /// Defines the _gotIt + /// + private bool _gotIt; + + /// + /// Defines the _headers + /// + private WebHeaderCollection _headers; + + /// + /// Defines the _saved + /// + private StringBuilder _saved; + + /// + /// Defines the _sawCr + /// + private bool _sawCr; + + /// + /// Defines the _state + /// + private InputChunkState _state; + + /// + /// Defines the _trailerState + /// + private int _trailerState; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The buffer + /// The offset + /// The count + /// The headers + public ChunkStream(byte[] buffer, int offset, int count, WebHeaderCollection headers) + : this(headers) + { + Write(buffer, offset, count); } - nread += chunk.Read (buffer, offset + nread, count - nread); - if (nread == count) - break; - } - - return nread; - } - - private static string removeChunkExtension (string value) - { - var idx = value.IndexOf (';'); - return idx > -1 ? value.Substring (0, idx) : value; - } - - private InputChunkState seekCrLf (byte[] buffer, ref int offset, int length) - { - if (!_sawCr) { - if (buffer[offset++] != 13) - throwProtocolViolation ("CR is expected."); - - _sawCr = true; - if (offset == length) - return InputChunkState.DataEnded; - } + /// + /// Initializes a new instance of the class. + /// + /// The headers + public ChunkStream(WebHeaderCollection headers) + { + _headers = headers; + _chunkSize = -1; + _chunks = new List(); + _saved = new StringBuilder(); + } - if (buffer[offset++] != 10) - throwProtocolViolation ("LF is expected."); + #endregion 캯 - return InputChunkState.None; - } + #region - private InputChunkState setChunkSize (byte[] buffer, ref int offset, int length) - { - byte b = 0; - while (offset < length) { - b = buffer[offset++]; - if (_sawCr) { - if (b != 10) - throwProtocolViolation ("LF is expected."); - - break; + /// + /// Gets the ChunkLeft + /// + public int ChunkLeft + { + get + { + return _chunkSize - _chunkRead; + } } - if (b == 13) { - _sawCr = true; - continue; + /// + /// Gets a value indicating whether WantMore + /// + public bool WantMore + { + get + { + return _state != InputChunkState.End; + } } - if (b == 10) - throwProtocolViolation ("LF is unexpected."); - - if (b == 32) // SP - _gotIt = true; - - if (!_gotIt) - _saved.Append ((char) b); - - if (_saved.Length > 20) - throwProtocolViolation ("The chunk size is too long."); - } - - if (!_sawCr || b != 10) - return InputChunkState.None; - - _chunkRead = 0; - try { - _chunkSize = Int32.Parse ( - removeChunkExtension (_saved.ToString ()), NumberStyles.HexNumber); - } - catch { - throwProtocolViolation ("The chunk size cannot be parsed."); - } - - if (_chunkSize == 0) { - _trailerState = 2; - return InputChunkState.Trailer; - } - - return InputChunkState.Data; - } - - private InputChunkState setTrailer (byte[] buffer, ref int offset, int length) - { - // Check if no trailer. - if (_trailerState == 2 && buffer[offset] == 13 && _saved.Length == 0) { - offset++; - if (offset < length && buffer[offset] == 10) { - offset++; - return InputChunkState.End; + /// + /// Gets the Headers + /// + internal WebHeaderCollection Headers + { + get + { + return _headers; + } } - offset--; - } + #endregion - while (offset < length && _trailerState < 4) { - var b = buffer[offset++]; - _saved.Append ((char) b); - if (_saved.Length > 4196) - throwProtocolViolation ("The trailer is too long."); + #region - if (_trailerState == 1 || _trailerState == 3) { - if (b != 10) - throwProtocolViolation ("LF is expected."); + /// + /// The Read + /// + /// The buffer + /// The offset + /// The count + /// The + public int Read(byte[] buffer, int offset, int count) + { + if (count <= 0) + return 0; - _trailerState++; - continue; + return read(buffer, offset, count); } - if (b == 13) { - _trailerState++; - continue; + /// + /// The Write + /// + /// The buffer + /// The offset + /// The count + public void Write(byte[] buffer, int offset, int count) + { + if (count <= 0) + return; + + write(buffer, ref offset, offset + count); } - if (b == 10) - throwProtocolViolation ("LF is unexpected."); - - _trailerState = 0; - } - - if (_trailerState < 4) - return InputChunkState.Trailer; - - _saved.Length -= 2; - var reader = new StringReader (_saved.ToString ()); - - string line; - while ((line = reader.ReadLine ()) != null && line.Length > 0) - _headers.Add (line); - - return InputChunkState.End; - } - - private static void throwProtocolViolation (string message) - { - throw new WebException (message, null, WebExceptionStatus.ServerProtocolViolation, null); - } - - private void write (byte[] buffer, ref int offset, int length) - { - if (_state == InputChunkState.End) - throwProtocolViolation ("The chunks were ended."); - - if (_state == InputChunkState.None) { - _state = setChunkSize (buffer, ref offset, length); - if (_state == InputChunkState.None) - return; - - _saved.Length = 0; - _sawCr = false; - _gotIt = false; - } - - if (_state == InputChunkState.Data && offset < length) { - _state = writeData (buffer, ref offset, length); - if (_state == InputChunkState.Data) - return; - } - - if (_state == InputChunkState.DataEnded && offset < length) { - _state = seekCrLf (buffer, ref offset, length); - if (_state == InputChunkState.DataEnded) - return; - - _sawCr = false; - } - - if (_state == InputChunkState.Trailer && offset < length) { - _state = setTrailer (buffer, ref offset, length); - if (_state == InputChunkState.Trailer) - return; - - _saved.Length = 0; - } - - if (offset < length) - write (buffer, ref offset, length); - } - - private InputChunkState writeData (byte[] buffer, ref int offset, int length) - { - var cnt = length - offset; - var left = _chunkSize - _chunkRead; - if (cnt > left) - cnt = left; - - var data = new byte[cnt]; - Buffer.BlockCopy (buffer, offset, data, 0, cnt); - _chunks.Add (new Chunk (data)); - - offset += cnt; - _chunkRead += cnt; - - return _chunkRead == _chunkSize ? InputChunkState.DataEnded : InputChunkState.Data; - } + /// + /// The ResetBuffer + /// + internal void ResetBuffer() + { + _chunkRead = 0; + _chunkSize = -1; + _chunks.Clear(); + } - #endregion + /// + /// The WriteAndReadBack + /// + /// The buffer + /// The offset + /// The writeCount + /// The readCount + /// The + internal int WriteAndReadBack(byte[] buffer, int offset, int writeCount, int readCount) + { + Write(buffer, offset, writeCount); + return Read(buffer, offset, readCount); + } - #region Internal Methods + /// + /// The removeChunkExtension + /// + /// The value + /// The + private static string removeChunkExtension(string value) + { + var idx = value.IndexOf(';'); + return idx > -1 ? value.Substring(0, idx) : value; + } - internal void ResetBuffer () - { - _chunkRead = 0; - _chunkSize = -1; - _chunks.Clear (); - } + /// + /// The throwProtocolViolation + /// + /// The message + private static void throwProtocolViolation(string message) + { + throw new WebException(message, null, WebExceptionStatus.ServerProtocolViolation, null); + } - internal int WriteAndReadBack (byte[] buffer, int offset, int writeCount, int readCount) - { - Write (buffer, offset, writeCount); - return Read (buffer, offset, readCount); - } + /// + /// The read + /// + /// The buffer + /// The offset + /// The count + /// The + private int read(byte[] buffer, int offset, int count) + { + var nread = 0; + + var cnt = _chunks.Count; + for (var i = 0; i < cnt; i++) + { + var chunk = _chunks[i]; + if (chunk == null) + continue; + + if (chunk.ReadLeft == 0) + { + _chunks[i] = null; + continue; + } + + nread += chunk.Read(buffer, offset + nread, count - nread); + if (nread == count) + break; + } + + return nread; + } - #endregion + /// + /// The seekCrLf + /// + /// The buffer + /// The offset + /// The length + /// The + private InputChunkState seekCrLf(byte[] buffer, ref int offset, int length) + { + if (!_sawCr) + { + if (buffer[offset++] != 13) + throwProtocolViolation("CR is expected."); + + _sawCr = true; + if (offset == length) + return InputChunkState.DataEnded; + } + + if (buffer[offset++] != 10) + throwProtocolViolation("LF is expected."); + + return InputChunkState.None; + } - #region Public Methods + /// + /// The setChunkSize + /// + /// The buffer + /// The offset + /// The length + /// The + private InputChunkState setChunkSize(byte[] buffer, ref int offset, int length) + { + byte b = 0; + while (offset < length) + { + b = buffer[offset++]; + if (_sawCr) + { + if (b != 10) + throwProtocolViolation("LF is expected."); + + break; + } + + if (b == 13) + { + _sawCr = true; + continue; + } + + if (b == 10) + throwProtocolViolation("LF is unexpected."); + + if (b == 32) // SP + _gotIt = true; + + if (!_gotIt) + _saved.Append((char)b); + + if (_saved.Length > 20) + throwProtocolViolation("The chunk size is too long."); + } + + if (!_sawCr || b != 10) + return InputChunkState.None; + + _chunkRead = 0; + try + { + _chunkSize = Int32.Parse( + removeChunkExtension(_saved.ToString()), NumberStyles.HexNumber); + } + catch + { + throwProtocolViolation("The chunk size cannot be parsed."); + } + + if (_chunkSize == 0) + { + _trailerState = 2; + return InputChunkState.Trailer; + } + + return InputChunkState.Data; + } - public int Read (byte[] buffer, int offset, int count) - { - if (count <= 0) - return 0; + /// + /// The setTrailer + /// + /// The buffer + /// The offset + /// The length + /// The + private InputChunkState setTrailer(byte[] buffer, ref int offset, int length) + { + // Check if no trailer. + if (_trailerState == 2 && buffer[offset] == 13 && _saved.Length == 0) + { + offset++; + if (offset < length && buffer[offset] == 10) + { + offset++; + return InputChunkState.End; + } + + offset--; + } + + while (offset < length && _trailerState < 4) + { + var b = buffer[offset++]; + _saved.Append((char)b); + if (_saved.Length > 4196) + throwProtocolViolation("The trailer is too long."); + + if (_trailerState == 1 || _trailerState == 3) + { + if (b != 10) + throwProtocolViolation("LF is expected."); + + _trailerState++; + continue; + } + + if (b == 13) + { + _trailerState++; + continue; + } + + if (b == 10) + throwProtocolViolation("LF is unexpected."); + + _trailerState = 0; + } + + if (_trailerState < 4) + return InputChunkState.Trailer; + + _saved.Length -= 2; + var reader = new StringReader(_saved.ToString()); + + string line; + while ((line = reader.ReadLine()) != null && line.Length > 0) + _headers.Add(line); + + return InputChunkState.End; + } - return read (buffer, offset, count); - } + /// + /// The write + /// + /// The buffer + /// The offset + /// The length + private void write(byte[] buffer, ref int offset, int length) + { + if (_state == InputChunkState.End) + throwProtocolViolation("The chunks were ended."); + + if (_state == InputChunkState.None) + { + _state = setChunkSize(buffer, ref offset, length); + if (_state == InputChunkState.None) + return; + + _saved.Length = 0; + _sawCr = false; + _gotIt = false; + } + + if (_state == InputChunkState.Data && offset < length) + { + _state = writeData(buffer, ref offset, length); + if (_state == InputChunkState.Data) + return; + } + + if (_state == InputChunkState.DataEnded && offset < length) + { + _state = seekCrLf(buffer, ref offset, length); + if (_state == InputChunkState.DataEnded) + return; + + _sawCr = false; + } + + if (_state == InputChunkState.Trailer && offset < length) + { + _state = setTrailer(buffer, ref offset, length); + if (_state == InputChunkState.Trailer) + return; + + _saved.Length = 0; + } + + if (offset < length) + write(buffer, ref offset, length); + } - public void Write (byte[] buffer, int offset, int count) - { - if (count <= 0) - return; + /// + /// The writeData + /// + /// The buffer + /// The offset + /// The length + /// The + private InputChunkState writeData(byte[] buffer, ref int offset, int length) + { + var cnt = length - offset; + var left = _chunkSize - _chunkRead; + if (cnt > left) + cnt = left; + + var data = new byte[cnt]; + Buffer.BlockCopy(buffer, offset, data, 0, cnt); + _chunks.Add(new Chunk(data)); + + offset += cnt; + _chunkRead += cnt; + + return _chunkRead == _chunkSize ? InputChunkState.DataEnded : InputChunkState.Data; + } - write (buffer, ref offset, offset + count); + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/ChunkedRequestStream.cs b/src/WebSocket/WebSocketCore/Net/ChunkedRequestStream.cs index d71c37624..b50adc777 100644 --- a/src/WebSocket/WebSocketCore/Net/ChunkedRequestStream.cs +++ b/src/WebSocket/WebSocketCore/Net/ChunkedRequestStream.cs @@ -1,4 +1,3 @@ -#region License /* * ChunkedRequestStream.cs * @@ -28,184 +27,251 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Gonzalo Paniagua Javier */ -#endregion using System; using System.IO; namespace WebSocketCore.Net { - internal class ChunkedRequestStream : RequestStream - { - #region Private Fields - - private const int _bufferLength = 8192; - private HttpListenerContext _context; - private ChunkStream _decoder; - private bool _disposed; - private bool _noMoreData; - - #endregion - - #region Internal Constructors - - internal ChunkedRequestStream ( - Stream stream, byte[] buffer, int offset, int count, HttpListenerContext context) - : base (stream, buffer, offset, count) + /// + /// Defines the + /// + internal class ChunkedRequestStream : RequestStream { - _context = context; - _decoder = new ChunkStream ((WebHeaderCollection) context.Request.Headers); - } - - #endregion - - #region Internal Properties - - internal ChunkStream Decoder { - get { - return _decoder; - } + #region + + /// + /// Defines the _bufferLength + /// + private const int _bufferLength = 8192; + + #endregion + + #region ֶ + + /// + /// Defines the _context + /// + private HttpListenerContext _context; + + /// + /// Defines the _decoder + /// + private ChunkStream _decoder; + + /// + /// Defines the _disposed + /// + private bool _disposed; + + /// + /// Defines the _noMoreData + /// + private bool _noMoreData; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The stream + /// The buffer + /// The offset + /// The count + /// The context + internal ChunkedRequestStream( + Stream stream, byte[] buffer, int offset, int count, HttpListenerContext context) + : base(stream, buffer, offset, count) + { + _context = context; + _decoder = new ChunkStream((WebHeaderCollection)context.Request.Headers); + } - set { - _decoder = value; - } - } + #endregion 캯 - #endregion + #region - #region Private Methods + /// + /// Gets or sets the Decoder + /// + internal ChunkStream Decoder + { + get + { + return _decoder; + } - private void onRead (IAsyncResult asyncResult) - { - var rstate = (ReadBufferState) asyncResult.AsyncState; - var ares = rstate.AsyncResult; - try { - var nread = base.EndRead (asyncResult); - _decoder.Write (ares.Buffer, ares.Offset, nread); - nread = _decoder.Read (rstate.Buffer, rstate.Offset, rstate.Count); - rstate.Offset += nread; - rstate.Count -= nread; - if (rstate.Count == 0 || !_decoder.WantMore || nread == 0) { - _noMoreData = !_decoder.WantMore && nread == 0; - ares.Count = rstate.InitialCount - rstate.Count; - ares.Complete (); - - return; + set + { + _decoder = value; + } } - ares.Offset = 0; - ares.Count = Math.Min (_bufferLength, _decoder.ChunkLeft + 6); - base.BeginRead (ares.Buffer, ares.Offset, ares.Count, onRead, rstate); - } - catch (Exception ex) { - _context.Connection.SendError (ex.Message, 400); - ares.Complete (ex); - } - } - - #endregion + #endregion - #region Public Methods + #region - public override IAsyncResult BeginRead ( + /// + /// The BeginRead + /// + /// The buffer + /// The offset + /// The count + /// The callback + /// The state + /// The + public override IAsyncResult BeginRead( byte[] buffer, int offset, int count, AsyncCallback callback, object state) - { - if (_disposed) - throw new ObjectDisposedException (GetType ().ToString ()); - - if (buffer == null) - throw new ArgumentNullException ("buffer"); - - if (offset < 0) - throw new ArgumentOutOfRangeException ("offset", "A negative value."); - - if (count < 0) - throw new ArgumentOutOfRangeException ("count", "A negative value."); - - var len = buffer.Length; - if (offset + count > len) - throw new ArgumentException ( - "The sum of 'offset' and 'count' is greater than 'buffer' length."); - - var ares = new HttpStreamAsyncResult (callback, state); - if (_noMoreData) { - ares.Complete (); - return ares; - } - - var nread = _decoder.Read (buffer, offset, count); - offset += nread; - count -= nread; - if (count == 0) { - // Got all we wanted, no need to bother the decoder yet. - ares.Count = nread; - ares.Complete (); - - return ares; - } + { + if (_disposed) + throw new ObjectDisposedException(GetType().ToString()); + + if (buffer == null) + throw new ArgumentNullException("buffer"); + + if (offset < 0) + throw new ArgumentOutOfRangeException("offset", "A negative value."); + + if (count < 0) + throw new ArgumentOutOfRangeException("count", "A negative value."); + + var len = buffer.Length; + if (offset + count > len) + throw new ArgumentException( + "The sum of 'offset' and 'count' is greater than 'buffer' length."); + + var ares = new HttpStreamAsyncResult(callback, state); + if (_noMoreData) + { + ares.Complete(); + return ares; + } + + var nread = _decoder.Read(buffer, offset, count); + offset += nread; + count -= nread; + if (count == 0) + { + // Got all we wanted, no need to bother the decoder yet. + ares.Count = nread; + ares.Complete(); + + return ares; + } + + if (!_decoder.WantMore) + { + _noMoreData = nread == 0; + ares.Count = nread; + ares.Complete(); + + return ares; + } + + ares.Buffer = new byte[_bufferLength]; + ares.Offset = 0; + ares.Count = _bufferLength; + + var rstate = new ReadBufferState(buffer, offset, count, ares); + rstate.InitialCount += nread; + base.BeginRead(ares.Buffer, ares.Offset, ares.Count, onRead, rstate); + + return ares; + } - if (!_decoder.WantMore) { - _noMoreData = nread == 0; - ares.Count = nread; - ares.Complete (); + /// + /// The Close + /// + public override void Close() + { + if (_disposed) + return; - return ares; - } + _disposed = true; + base.Close(); + } - ares.Buffer = new byte[_bufferLength]; - ares.Offset = 0; - ares.Count = _bufferLength; + /// + /// The EndRead + /// + /// The asyncResult + /// The + public override int EndRead(IAsyncResult asyncResult) + { + if (_disposed) + throw new ObjectDisposedException(GetType().ToString()); - var rstate = new ReadBufferState (buffer, offset, count, ares); - rstate.InitialCount += nread; - base.BeginRead (ares.Buffer, ares.Offset, ares.Count, onRead, rstate); + if (asyncResult == null) + throw new ArgumentNullException("asyncResult"); - return ares; - } + var ares = asyncResult as HttpStreamAsyncResult; + if (ares == null) + throw new ArgumentException("A wrong IAsyncResult.", "asyncResult"); - public override void Close () - { - if (_disposed) - return; + if (!ares.IsCompleted) + ares.AsyncWaitHandle.WaitOne(); - _disposed = true; - base.Close (); - } + if (ares.HasException) + throw new HttpListenerException(400, "I/O operation aborted."); - public override int EndRead (IAsyncResult asyncResult) - { - if (_disposed) - throw new ObjectDisposedException (GetType ().ToString ()); - - if (asyncResult == null) - throw new ArgumentNullException ("asyncResult"); - - var ares = asyncResult as HttpStreamAsyncResult; - if (ares == null) - throw new ArgumentException ("A wrong IAsyncResult.", "asyncResult"); - - if (!ares.IsCompleted) - ares.AsyncWaitHandle.WaitOne (); + return ares.Count; + } - if (ares.HasException) - throw new HttpListenerException (400, "I/O operation aborted."); + /// + /// The Read + /// + /// The buffer + /// The offset + /// The count + /// The + public override int Read(byte[] buffer, int offset, int count) + { + var ares = BeginRead(buffer, offset, count, null, null); + return EndRead(ares); + } - return ares.Count; - } + /// + /// The onRead + /// + /// The asyncResult + private void onRead(IAsyncResult asyncResult) + { + var rstate = (ReadBufferState)asyncResult.AsyncState; + var ares = rstate.AsyncResult; + try + { + var nread = base.EndRead(asyncResult); + _decoder.Write(ares.Buffer, ares.Offset, nread); + nread = _decoder.Read(rstate.Buffer, rstate.Offset, rstate.Count); + rstate.Offset += nread; + rstate.Count -= nread; + if (rstate.Count == 0 || !_decoder.WantMore || nread == 0) + { + _noMoreData = !_decoder.WantMore && nread == 0; + ares.Count = rstate.InitialCount - rstate.Count; + ares.Complete(); + + return; + } + + ares.Offset = 0; + ares.Count = Math.Min(_bufferLength, _decoder.ChunkLeft + 6); + base.BeginRead(ares.Buffer, ares.Offset, ares.Count, onRead, rstate); + } + catch (Exception ex) + { + _context.Connection.SendError(ex.Message, 400); + ares.Complete(ex); + } + } - public override int Read (byte[] buffer, int offset, int count) - { - var ares = BeginRead (buffer, offset, count, null, null); - return EndRead (ares); + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/ClientSslConfiguration.cs b/src/WebSocket/WebSocketCore/Net/ClientSslConfiguration.cs index d6f2a843b..2a10841f9 100644 --- a/src/WebSocket/WebSocketCore/Net/ClientSslConfiguration.cs +++ b/src/WebSocket/WebSocketCore/Net/ClientSslConfiguration.cs @@ -1,5 +1,4 @@ -#region License -/* +/* * ClientSslConfiguration.cs * * The MIT License @@ -25,14 +24,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Liryna */ -#endregion using System; using System.Net.Security; @@ -41,251 +37,234 @@ namespace WebSocketCore.Net { - /// - /// Stores the parameters for the used by clients. - /// - public class ClientSslConfiguration - { - #region Private Fields + /// + /// Stores the parameters for the used by clients. + /// + public class ClientSslConfiguration + { + #region 字段 - private bool _checkCertRevocation; - private LocalCertificateSelectionCallback _clientCertSelectionCallback; - private X509CertificateCollection _clientCerts; - private SslProtocols _enabledSslProtocols; - private RemoteCertificateValidationCallback _serverCertValidationCallback; - private string _targetHost; + /// + /// Defines the _checkCertRevocation + /// + private bool _checkCertRevocation; - #endregion + /// + /// Defines the _clientCerts + /// + private X509CertificateCollection _clientCerts; - #region Public Constructors + /// + /// Defines the _clientCertSelectionCallback + /// + private LocalCertificateSelectionCallback _clientCertSelectionCallback; - /// - /// Initializes a new instance of the class. - /// - public ClientSslConfiguration () - { - _enabledSslProtocols = SslProtocols.Default; - } + /// + /// Defines the _enabledSslProtocols + /// + private SslProtocols _enabledSslProtocols; - /// - /// Initializes a new instance of the class - /// with the specified . - /// - /// - /// A that represents the target host server name. - /// - public ClientSslConfiguration (string targetHost) - { - _targetHost = targetHost; - _enabledSslProtocols = SslProtocols.Default; - } + /// + /// Defines the _serverCertValidationCallback + /// + private RemoteCertificateValidationCallback _serverCertValidationCallback; - /// - /// Copies the parameters from the specified to - /// a new instance of the class. - /// - /// - /// A from which to copy. - /// - /// - /// is . - /// - public ClientSslConfiguration (ClientSslConfiguration configuration) - { - if (configuration == null) - throw new ArgumentNullException ("configuration"); - - _checkCertRevocation = configuration._checkCertRevocation; - _clientCertSelectionCallback = configuration._clientCertSelectionCallback; - _clientCerts = configuration._clientCerts; - _enabledSslProtocols = configuration._enabledSslProtocols; - _serverCertValidationCallback = configuration._serverCertValidationCallback; - _targetHost = configuration._targetHost; - } + /// + /// Defines the _targetHost + /// + private string _targetHost; - #endregion + #endregion 字段 - #region Public Properties + #region 构造函数 - /// - /// Gets or sets a value indicating whether the certificate revocation - /// list is checked during authentication. - /// - /// - /// - /// true if the certificate revocation list is checked during - /// authentication; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - public bool CheckCertificateRevocation { - get { - return _checkCertRevocation; - } - - set { - _checkCertRevocation = value; - } - } + /// + /// Initializes a new instance of the class. + /// + public ClientSslConfiguration() + { + _enabledSslProtocols = SslProtocols.Default; + } - /// - /// Gets or sets the certificates from which to select one to - /// supply to the server. - /// - /// - /// - /// A or . - /// - /// - /// That collection contains client certificates from which to select. - /// - /// - /// The default value is . - /// - /// - public X509CertificateCollection ClientCertificates { - get { - return _clientCerts; - } - - set { - _clientCerts = value; - } - } + /// + /// Initializes a new instance of the class. + /// + /// The configuration + public ClientSslConfiguration(ClientSslConfiguration configuration) + { + if (configuration == null) + throw new ArgumentNullException("configuration"); - /// - /// Gets or sets the callback used to select the certificate to - /// supply to the server. - /// - /// - /// No certificate is supplied if the callback returns - /// . - /// - /// - /// - /// A delegate that - /// invokes the method called for selecting the certificate. - /// - /// - /// The default value is a delegate that invokes a method that - /// only returns . - /// - /// - public LocalCertificateSelectionCallback ClientCertificateSelectionCallback { - get { - if (_clientCertSelectionCallback == null) - _clientCertSelectionCallback = defaultSelectClientCertificate; - - return _clientCertSelectionCallback; - } - - set { - _clientCertSelectionCallback = value; - } - } + _checkCertRevocation = configuration._checkCertRevocation; + _clientCertSelectionCallback = configuration._clientCertSelectionCallback; + _clientCerts = configuration._clientCerts; + _enabledSslProtocols = configuration._enabledSslProtocols; + _serverCertValidationCallback = configuration._serverCertValidationCallback; + _targetHost = configuration._targetHost; + } - /// - /// Gets or sets the protocols used for authentication. - /// - /// - /// - /// The enum values that represent - /// the protocols used for authentication. - /// - /// - /// The default value is . - /// - /// - public SslProtocols EnabledSslProtocols { - get { - return _enabledSslProtocols; - } - - set { - _enabledSslProtocols = value; - } - } + /// + /// Initializes a new instance of the class. + /// + /// The targetHost + public ClientSslConfiguration(string targetHost) + { + _targetHost = targetHost; + _enabledSslProtocols = SslProtocols.Default; + } - /// - /// Gets or sets the callback used to validate the certificate - /// supplied by the server. - /// - /// - /// The certificate is valid if the callback returns true. - /// - /// - /// - /// A delegate that - /// invokes the method called for validating the certificate. - /// - /// - /// The default value is a delegate that invokes a method that - /// only returns true. - /// - /// - public RemoteCertificateValidationCallback ServerCertificateValidationCallback { - get { - if (_serverCertValidationCallback == null) - _serverCertValidationCallback = defaultValidateServerCertificate; - - return _serverCertValidationCallback; - } - - set { - _serverCertValidationCallback = value; - } - } + #endregion 构造函数 - /// - /// Gets or sets the target host server name. - /// - /// - /// - /// A or - /// if not specified. - /// - /// - /// That string represents the name of the server that - /// will share a secure connection with a client. - /// - /// - public string TargetHost { - get { - return _targetHost; - } - - set { - _targetHost = value; - } - } + #region 属性 + + /// + /// Gets or sets a value indicating whether the certificate revocation + /// list is checked during authentication. + /// + public bool CheckCertificateRevocation + { + get + { + return _checkCertRevocation; + } + + set + { + _checkCertRevocation = value; + } + } + + /// + /// Gets or sets the certificates from which to select one to + /// supply to the server. + /// + public X509CertificateCollection ClientCertificates + { + get + { + return _clientCerts; + } + + set + { + _clientCerts = value; + } + } + + /// + /// Gets or sets the callback used to select the certificate to + /// supply to the server. + /// + public LocalCertificateSelectionCallback ClientCertificateSelectionCallback + { + get + { + if (_clientCertSelectionCallback == null) + _clientCertSelectionCallback = defaultSelectClientCertificate; + + return _clientCertSelectionCallback; + } - #endregion + set + { + _clientCertSelectionCallback = value; + } + } - #region Private Methods + /// + /// Gets or sets the protocols used for authentication. + /// + public SslProtocols EnabledSslProtocols + { + get + { + return _enabledSslProtocols; + } - private static X509Certificate defaultSelectClientCertificate ( + set + { + _enabledSslProtocols = value; + } + } + + /// + /// Gets or sets the callback used to validate the certificate + /// supplied by the server. + /// + public RemoteCertificateValidationCallback ServerCertificateValidationCallback + { + get + { + if (_serverCertValidationCallback == null) + _serverCertValidationCallback = defaultValidateServerCertificate; + + return _serverCertValidationCallback; + } + + set + { + _serverCertValidationCallback = value; + } + } + + /// + /// Gets or sets the target host server name. + /// + public string TargetHost + { + get + { + return _targetHost; + } + + set + { + _targetHost = value; + } + } + + #endregion 属性 + + #region 方法 + + /// + /// The defaultSelectClientCertificate + /// + /// The sender + /// The targetHost + /// The clientCertificates + /// The serverCertificate + /// The acceptableIssuers + /// The + private static X509Certificate defaultSelectClientCertificate( object sender, string targetHost, X509CertificateCollection clientCertificates, X509Certificate serverCertificate, string[] acceptableIssuers ) - { - return null; - } + { + return null; + } - private static bool defaultValidateServerCertificate ( + /// + /// The defaultValidateServerCertificate + /// + /// The sender + /// The certificate + /// The chain + /// The sslPolicyErrors + /// The + private static bool defaultValidateServerCertificate( object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors ) - { - return true; - } + { + return true; + } - #endregion - } -} + #endregion 方法 + } +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/Cookie.cs b/src/WebSocket/WebSocketCore/Net/Cookie.cs index 1a784890d..4752b2749 100644 --- a/src/WebSocket/WebSocketCore/Net/Cookie.cs +++ b/src/WebSocket/WebSocketCore/Net/Cookie.cs @@ -1,4 +1,3 @@ -#region License /* * Cookie.cs * @@ -28,9 +27,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Lawrence Pit @@ -38,7 +35,6 @@ * - Daniel Nauck * - Sebastien Pouliot */ -#endregion using System; using System.Globalization; @@ -46,777 +42,734 @@ namespace WebSocketCore.Net { - /// - /// Provides a set of methods and properties used to manage an HTTP Cookie. - /// - /// - /// - /// The Cookie class supports the following cookie formats: - /// Netscape specification, - /// RFC 2109, and - /// RFC 2965 - /// - /// - /// The Cookie class cannot be inherited. - /// - /// - [Serializable] - public sealed class Cookie - { - #region Private Fields - - private string _comment; - private Uri _commentUri; - private bool _discard; - private string _domain; - private DateTime _expires; - private bool _httpOnly; - private string _name; - private string _path; - private string _port; - private int[] _ports; - private static readonly char[] _reservedCharsForName; - private static readonly char[] _reservedCharsForValue; - private bool _secure; - private DateTime _timestamp; - private string _value; - private int _version; - - #endregion - - #region Static Constructor - - static Cookie () - { - _reservedCharsForName = new[] { ' ', '=', ';', ',', '\n', '\r', '\t' }; - _reservedCharsForValue = new[] { ';', ',' }; - } - - #endregion - - #region Public Constructors - - /// - /// Initializes a new instance of the class. - /// - public Cookie () - { - _comment = String.Empty; - _domain = String.Empty; - _expires = DateTime.MinValue; - _name = String.Empty; - _path = String.Empty; - _port = String.Empty; - _ports = new int[0]; - _timestamp = DateTime.Now; - _value = String.Empty; - _version = 0; - } - - /// - /// Initializes a new instance of the class with the specified - /// and . - /// - /// - /// A that represents the Name of the cookie. - /// - /// - /// A that represents the Value of the cookie. - /// - /// - /// - /// is or empty. - /// - /// - /// - or - - /// - /// - /// contains an invalid character. - /// - /// - /// - or - - /// - /// - /// is . - /// - /// - /// - or - - /// - /// - /// contains a string not enclosed in double quotes - /// that contains an invalid character. - /// - /// - public Cookie (string name, string value) - : this () - { - Name = name; - Value = value; - } - - /// - /// Initializes a new instance of the class with the specified - /// , , and . - /// - /// - /// A that represents the Name of the cookie. - /// - /// - /// A that represents the Value of the cookie. - /// - /// - /// A that represents the value of the Path attribute of the cookie. - /// - /// - /// - /// is or empty. - /// - /// - /// - or - - /// - /// - /// contains an invalid character. - /// - /// - /// - or - - /// - /// - /// is . - /// - /// - /// - or - - /// - /// - /// contains a string not enclosed in double quotes - /// that contains an invalid character. - /// - /// - public Cookie (string name, string value, string path) - : this (name, value) - { - Path = path; - } - /// - /// Initializes a new instance of the class with the specified - /// , , , and - /// . + /// Provides a set of methods and properties used to manage an HTTP Cookie. /// - /// - /// A that represents the Name of the cookie. - /// - /// - /// A that represents the Value of the cookie. - /// - /// - /// A that represents the value of the Path attribute of the cookie. - /// - /// - /// A that represents the value of the Domain attribute of the cookie. - /// - /// - /// - /// is or empty. - /// - /// - /// - or - - /// - /// - /// contains an invalid character. - /// - /// - /// - or - - /// - /// - /// is . - /// - /// - /// - or - - /// - /// - /// contains a string not enclosed in double quotes - /// that contains an invalid character. - /// - /// - public Cookie (string name, string value, string path, string domain) - : this (name, value, path) + [Serializable] + public sealed class Cookie { - Domain = domain; - } - - #endregion - - #region Internal Properties - - internal bool ExactDomain { - get; set; - } - - internal int MaxAge { - get { - if (_expires == DateTime.MinValue) - return 0; - - var expires = _expires.Kind != DateTimeKind.Local - ? _expires.ToLocalTime () - : _expires; - - var span = expires - DateTime.Now; - return span > TimeSpan.Zero - ? (int) span.TotalSeconds - : 0; - } - } - - internal int[] Ports { - get { - return _ports; - } - } - - #endregion - - #region Public Properties - - /// - /// Gets or sets the value of the Comment attribute of the cookie. - /// - /// - /// A that represents the comment to document intended use of the cookie. - /// - public string Comment { - get { - return _comment; - } - - set { - _comment = value ?? String.Empty; - } - } + #region ֶ + + /// + /// Defines the _reservedCharsForName + /// + private static readonly char[] _reservedCharsForName; + + /// + /// Defines the _reservedCharsForValue + /// + private static readonly char[] _reservedCharsForValue; + + /// + /// Defines the _comment + /// + private string _comment; + + /// + /// Defines the _commentUri + /// + private Uri _commentUri; + + /// + /// Defines the _discard + /// + private bool _discard; + + /// + /// Defines the _domain + /// + private string _domain; + + /// + /// Defines the _expires + /// + private DateTime _expires; + + /// + /// Defines the _httpOnly + /// + private bool _httpOnly; + + /// + /// Defines the _name + /// + private string _name; + + /// + /// Defines the _path + /// + private string _path; + + /// + /// Defines the _port + /// + private string _port; + + /// + /// Defines the _ports + /// + private int[] _ports; + + /// + /// Defines the _secure + /// + private bool _secure; + + /// + /// Defines the _timestamp + /// + private DateTime _timestamp; + + /// + /// Defines the _value + /// + private string _value; + + /// + /// Defines the _version + /// + private int _version; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + public Cookie() + { + _comment = String.Empty; + _domain = String.Empty; + _expires = DateTime.MinValue; + _name = String.Empty; + _path = String.Empty; + _port = String.Empty; + _ports = new int[0]; + _timestamp = DateTime.Now; + _value = String.Empty; + _version = 0; + } - /// - /// Gets or sets the value of the CommentURL attribute of the cookie. - /// - /// - /// A that represents the URI that provides the comment to document intended - /// use of the cookie. - /// - public Uri CommentUri { - get { - return _commentUri; - } - - set { - _commentUri = value; - } - } + /// + /// Initializes a new instance of the class. + /// + /// The name + /// The value + public Cookie(string name, string value) + : this() + { + Name = name; + Value = value; + } - /// - /// Gets or sets a value indicating whether the client discards the cookie unconditionally - /// when the client terminates. - /// - /// - /// true if the client discards the cookie unconditionally when the client terminates; - /// otherwise, false. The default value is false. - /// - public bool Discard { - get { - return _discard; - } - - set { - _discard = value; - } - } + /// + /// Initializes a new instance of the class. + /// + /// The name + /// The value + /// The path + public Cookie(string name, string value, string path) + : this(name, value) + { + Path = path; + } - /// - /// Gets or sets the value of the Domain attribute of the cookie. - /// - /// - /// A that represents the URI for which the cookie is valid. - /// - public string Domain { - get { - return _domain; - } - - set { - if (value.IsNullOrEmpty ()) { - _domain = String.Empty; - ExactDomain = true; - } - else { - _domain = value; - ExactDomain = value[0] != '.'; - } - } - } + /// + /// Initializes a new instance of the class. + /// + /// The name + /// The value + /// The path + /// The domain + public Cookie(string name, string value, string path, string domain) + : this(name, value, path) + { + Domain = domain; + } - /// - /// Gets or sets a value indicating whether the cookie has expired. - /// - /// - /// true if the cookie has expired; otherwise, false. - /// The default value is false. - /// - public bool Expired { - get { - return _expires != DateTime.MinValue && _expires <= DateTime.Now; - } - - set { - _expires = value ? DateTime.Now : DateTime.MinValue; - } - } + /// + /// Initializes static members of the class. + /// + static Cookie() + { + _reservedCharsForName = new[] { ' ', '=', ';', ',', '\n', '\r', '\t' }; + _reservedCharsForValue = new[] { ';', ',' }; + } - /// - /// Gets or sets the value of the Expires attribute of the cookie. - /// - /// - /// A that represents the date and time at which the cookie expires. - /// The default value is . - /// - public DateTime Expires { - get { - return _expires; - } - - set { - _expires = value; - } - } + #endregion 캯 - /// - /// Gets or sets a value indicating whether non-HTTP APIs can access the cookie. - /// - /// - /// true if non-HTTP APIs cannot access the cookie; otherwise, false. - /// The default value is false. - /// - public bool HttpOnly { - get { - return _httpOnly; - } - - set { - _httpOnly = value; - } - } + #region - /// - /// Gets or sets the Name of the cookie. - /// - /// - /// A that represents the Name of the cookie. - /// - /// - /// - /// The value specified for a set operation is or empty. - /// - /// - /// - or - - /// - /// - /// The value specified for a set operation contains an invalid character. - /// - /// - public string Name { - get { - return _name; - } - - set { - string msg; - if (!canSetName (value, out msg)) - throw new CookieException (msg); - - _name = value; - } - } + /// + /// Gets or sets the value of the Comment attribute of the cookie. + /// + public string Comment + { + get + { + return _comment; + } - /// - /// Gets or sets the value of the Path attribute of the cookie. - /// - /// - /// A that represents the subset of URI on the origin server - /// to which the cookie applies. - /// - public string Path { - get { - return _path; - } - - set { - _path = value ?? String.Empty; - } - } + set + { + _comment = value ?? String.Empty; + } + } - /// - /// Gets or sets the value of the Port attribute of the cookie. - /// - /// - /// A that represents the list of TCP ports to which the cookie applies. - /// - /// - /// The value specified for a set operation isn't enclosed in double quotes or - /// couldn't be parsed. - /// - public string Port { - get { - return _port; - } - - set { - if (value.IsNullOrEmpty ()) { - _port = String.Empty; - _ports = new int[0]; - - return; - } - - if (!value.IsEnclosedIn ('"')) - throw new CookieException ( - "The value specified for the Port attribute isn't enclosed in double quotes."); - - string err; - if (!tryCreatePorts (value, out _ports, out err)) - throw new CookieException ( - String.Format ( - "The value specified for the Port attribute contains an invalid value: {0}", err)); - - _port = value; - } - } + /// + /// Gets or sets the value of the CommentURL attribute of the cookie. + /// + public Uri CommentUri + { + get + { + return _commentUri; + } + + set + { + _commentUri = value; + } + } - /// - /// Gets or sets a value indicating whether the security level of the cookie is secure. - /// - /// - /// When this property is true, the cookie may be included in the HTTP request - /// only if the request is transmitted over the HTTPS. - /// - /// - /// true if the security level of the cookie is secure; otherwise, false. - /// The default value is false. - /// - public bool Secure { - get { - return _secure; - } - - set { - _secure = value; - } - } + /// + /// Gets or sets a value indicating whether the client discards the cookie unconditionally + /// when the client terminates. + /// + public bool Discard + { + get + { + return _discard; + } + + set + { + _discard = value; + } + } - /// - /// Gets the time when the cookie was issued. - /// - /// - /// A that represents the time when the cookie was issued. - /// - public DateTime TimeStamp { - get { - return _timestamp; - } - } + /// + /// Gets or sets the value of the Domain attribute of the cookie. + /// + public string Domain + { + get + { + return _domain; + } + + set + { + if (value.IsNullOrEmpty()) + { + _domain = String.Empty; + ExactDomain = true; + } + else + { + _domain = value; + ExactDomain = value[0] != '.'; + } + } + } - /// - /// Gets or sets the Value of the cookie. - /// - /// - /// A that represents the Value of the cookie. - /// - /// - /// - /// The value specified for a set operation is . - /// - /// - /// - or - - /// - /// - /// The value specified for a set operation contains a string not enclosed in double quotes - /// that contains an invalid character. - /// - /// - public string Value { - get { - return _value; - } - - set { - string msg; - if (!canSetValue (value, out msg)) - throw new CookieException (msg); - - _value = value.Length > 0 ? value : "\"\""; - } - } + /// + /// Gets or sets a value indicating whether the cookie has expired. + /// + public bool Expired + { + get + { + return _expires != DateTime.MinValue && _expires <= DateTime.Now; + } + + set + { + _expires = value ? DateTime.Now : DateTime.MinValue; + } + } - /// - /// Gets or sets the value of the Version attribute of the cookie. - /// - /// - /// An that represents the version of the HTTP state management - /// to which the cookie conforms. - /// - /// - /// The value specified for a set operation isn't 0 or 1. - /// - public int Version { - get { - return _version; - } - - set { - if (value < 0 || value > 1) - throw new ArgumentOutOfRangeException ("value", "Not 0 or 1."); - - _version = value; - } - } + /// + /// Gets or sets the value of the Expires attribute of the cookie. + /// + public DateTime Expires + { + get + { + return _expires; + } + + set + { + _expires = value; + } + } - #endregion + /// + /// Gets or sets a value indicating whether non-HTTP APIs can access the cookie. + /// + public bool HttpOnly + { + get + { + return _httpOnly; + } + + set + { + _httpOnly = value; + } + } - #region Private Methods + /// + /// Gets or sets the Name of the cookie. + /// + public string Name + { + get + { + return _name; + } + + set + { + string msg; + if (!canSetName(value, out msg)) + throw new CookieException(msg); + + _name = value; + } + } - private static bool canSetName (string name, out string message) - { - if (name.IsNullOrEmpty ()) { - message = "The value specified for the Name is null or empty."; - return false; - } - - if (name[0] == '$' || name.Contains (_reservedCharsForName)) { - message = "The value specified for the Name contains an invalid character."; - return false; - } - - message = String.Empty; - return true; - } + /// + /// Gets or sets the value of the Path attribute of the cookie. + /// + public string Path + { + get + { + return _path; + } + + set + { + _path = value ?? String.Empty; + } + } - private static bool canSetValue (string value, out string message) - { - if (value == null) { - message = "The value specified for the Value is null."; - return false; - } - - if (value.Contains (_reservedCharsForValue) && !value.IsEnclosedIn ('"')) { - message = "The value specified for the Value contains an invalid character."; - return false; - } - - message = String.Empty; - return true; - } + /// + /// Gets or sets the value of the Port attribute of the cookie. + /// + public string Port + { + get + { + return _port; + } + + set + { + if (value.IsNullOrEmpty()) + { + _port = String.Empty; + _ports = new int[0]; + + return; + } + + if (!value.IsEnclosedIn('"')) + throw new CookieException( + "The value specified for the Port attribute isn't enclosed in double quotes."); + + string err; + if (!tryCreatePorts(value, out _ports, out err)) + throw new CookieException( + String.Format( + "The value specified for the Port attribute contains an invalid value: {0}", err)); + + _port = value; + } + } - private static int hash (int i, int j, int k, int l, int m) - { - return i ^ - (j << 13 | j >> 19) ^ - (k << 26 | k >> 6) ^ - (l << 7 | l >> 25) ^ - (m << 20 | m >> 12); - } + /// + /// Gets or sets a value indicating whether the security level of the cookie is secure. + /// + public bool Secure + { + get + { + return _secure; + } + + set + { + _secure = value; + } + } - private string toResponseStringVersion0 () - { - var output = new StringBuilder (64); - output.AppendFormat ("{0}={1}", _name, _value); + /// + /// Gets the time when the cookie was issued. + /// + public DateTime TimeStamp + { + get + { + return _timestamp; + } + } - if (_expires != DateTime.MinValue) - output.AppendFormat ( - "; Expires={0}", - _expires.ToUniversalTime ().ToString ( - "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'", - CultureInfo.CreateSpecificCulture ("en-US"))); + /// + /// Gets or sets the Value of the cookie. + /// + public string Value + { + get + { + return _value; + } + + set + { + string msg; + if (!canSetValue(value, out msg)) + throw new CookieException(msg); + + _value = value.Length > 0 ? value : "\"\""; + } + } - if (!_path.IsNullOrEmpty ()) - output.AppendFormat ("; Path={0}", _path); + /// + /// Gets or sets the value of the Version attribute of the cookie. + /// + public int Version + { + get + { + return _version; + } + + set + { + if (value < 0 || value > 1) + throw new ArgumentOutOfRangeException("value", "Not 0 or 1."); + + _version = value; + } + } - if (!_domain.IsNullOrEmpty ()) - output.AppendFormat ("; Domain={0}", _domain); + /// + /// Gets or sets a value indicating whether ExactDomain + /// + internal bool ExactDomain { get; set; } + + /// + /// Gets the MaxAge + /// + internal int MaxAge + { + get + { + if (_expires == DateTime.MinValue) + return 0; + + var expires = _expires.Kind != DateTimeKind.Local + ? _expires.ToLocalTime() + : _expires; + + var span = expires - DateTime.Now; + return span > TimeSpan.Zero + ? (int)span.TotalSeconds + : 0; + } + } - if (_secure) - output.Append ("; Secure"); + /// + /// Gets the Ports + /// + internal int[] Ports + { + get + { + return _ports; + } + } - if (_httpOnly) - output.Append ("; HttpOnly"); + #endregion + + #region + + /// + /// Determines whether the specified is equal to the current + /// . + /// + /// The comparand + /// The + public override bool Equals(Object comparand) + { + var cookie = comparand as Cookie; + return cookie != null && + _name.Equals(cookie.Name, StringComparison.InvariantCultureIgnoreCase) && + _value.Equals(cookie.Value, StringComparison.InvariantCulture) && + _path.Equals(cookie.Path, StringComparison.InvariantCulture) && + _domain.Equals(cookie.Domain, StringComparison.InvariantCultureIgnoreCase) && + _version == cookie.Version; + } - return output.ToString (); - } + /// + /// Serves as a hash function for a object. + /// + /// The + public override int GetHashCode() + { + return hash( + StringComparer.InvariantCultureIgnoreCase.GetHashCode(_name), + _value.GetHashCode(), + _path.GetHashCode(), + StringComparer.InvariantCultureIgnoreCase.GetHashCode(_domain), + _version); + } - private string toResponseStringVersion1 () - { - var output = new StringBuilder (64); - output.AppendFormat ("{0}={1}; Version={2}", _name, _value, _version); + /// + /// Returns a that represents the current . + /// + /// The + public override string ToString() + { + // i.e., only used for clients + // See para 4.2.2 of RFC 2109 and para 3.3.4 of RFC 2965 + // See also bug #316017 + return ToRequestString(null); + } - if (_expires != DateTime.MinValue) - output.AppendFormat ("; Max-Age={0}", MaxAge); + // From client to server + /// + /// The ToRequestString + /// + /// The uri + /// The + internal string ToRequestString(Uri uri) + { + if (_name.Length == 0) + return String.Empty; + + if (_version == 0) + return String.Format("{0}={1}", _name, _value); + + var output = new StringBuilder(64); + output.AppendFormat("$Version={0}; {1}={2}", _version, _name, _value); + + if (!_path.IsNullOrEmpty()) + output.AppendFormat("; $Path={0}", _path); + else if (uri != null) + output.AppendFormat("; $Path={0}", uri.GetAbsolutePath()); + else + output.Append("; $Path=/"); + + var appendDomain = uri == null || uri.Host != _domain; + if (appendDomain && !_domain.IsNullOrEmpty()) + output.AppendFormat("; $Domain={0}", _domain); + + if (!_port.IsNullOrEmpty()) + { + if (_port == "\"\"") + output.Append("; $Port"); + else + output.AppendFormat("; $Port={0}", _port); + } + + return output.ToString(); + } - if (!_path.IsNullOrEmpty ()) - output.AppendFormat ("; Path={0}", _path); + // From server to client + /// + /// The ToResponseString + /// + /// The + internal string ToResponseString() + { + return _name.Length > 0 + ? (_version == 0 ? toResponseStringVersion0() : toResponseStringVersion1()) + : String.Empty; + } - if (!_domain.IsNullOrEmpty ()) - output.AppendFormat ("; Domain={0}", _domain); + /// + /// The canSetName + /// + /// The name + /// The message + /// The + private static bool canSetName(string name, out string message) + { + if (name.IsNullOrEmpty()) + { + message = "The value specified for the Name is null or empty."; + return false; + } + + if (name[0] == '$' || name.Contains(_reservedCharsForName)) + { + message = "The value specified for the Name contains an invalid character."; + return false; + } + + message = String.Empty; + return true; + } - if (!_port.IsNullOrEmpty ()) { - if (_port == "\"\"") - output.Append ("; Port"); - else - output.AppendFormat ("; Port={0}", _port); - } + /// + /// The canSetValue + /// + /// The value + /// The message + /// The + private static bool canSetValue(string value, out string message) + { + if (value == null) + { + message = "The value specified for the Value is null."; + return false; + } + + if (value.Contains(_reservedCharsForValue) && !value.IsEnclosedIn('"')) + { + message = "The value specified for the Value contains an invalid character."; + return false; + } + + message = String.Empty; + return true; + } - if (!_comment.IsNullOrEmpty ()) - output.AppendFormat ("; Comment={0}", _comment.UrlEncode ()); + /// + /// The hash + /// + /// The i + /// The j + /// The k + /// The l + /// The m + /// The + private static int hash(int i, int j, int k, int l, int m) + { + return i ^ + (j << 13 | j >> 19) ^ + (k << 26 | k >> 6) ^ + (l << 7 | l >> 25) ^ + (m << 20 | m >> 12); + } - if (_commentUri != null) { - var url = _commentUri.OriginalString; - output.AppendFormat ("; CommentURL={0}", url.IsToken () ? url : url.Quote ()); - } + /// + /// The tryCreatePorts + /// + /// The value + /// The result + /// The parseError + /// The + private static bool tryCreatePorts(string value, out int[] result, out string parseError) + { + var ports = value.Trim('"').Split(','); + var len = ports.Length; + var res = new int[len]; + for (var i = 0; i < len; i++) + { + res[i] = Int32.MinValue; + + var port = ports[i].Trim(); + if (port.Length == 0) + continue; + + if (!Int32.TryParse(port, out res[i])) + { + result = new int[0]; + parseError = port; + + return false; + } + } + + result = res; + parseError = String.Empty; + + return true; + } - if (_discard) - output.Append ("; Discard"); + /// + /// The toResponseStringVersion0 + /// + /// The + private string toResponseStringVersion0() + { + var output = new StringBuilder(64); + output.AppendFormat("{0}={1}", _name, _value); - if (_secure) - output.Append ("; Secure"); + if (_expires != DateTime.MinValue) + output.AppendFormat( + "; Expires={0}", + _expires.ToUniversalTime().ToString( + "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'", + CultureInfo.CreateSpecificCulture("en-US"))); - return output.ToString (); - } + if (!_path.IsNullOrEmpty()) + output.AppendFormat("; Path={0}", _path); - private static bool tryCreatePorts (string value, out int[] result, out string parseError) - { - var ports = value.Trim ('"').Split (','); - var len = ports.Length; - var res = new int[len]; - for (var i = 0; i < len; i++) { - res[i] = Int32.MinValue; + if (!_domain.IsNullOrEmpty()) + output.AppendFormat("; Domain={0}", _domain); - var port = ports[i].Trim (); - if (port.Length == 0) - continue; + if (_secure) + output.Append("; Secure"); - if (!Int32.TryParse (port, out res[i])) { - result = new int[0]; - parseError = port; + if (_httpOnly) + output.Append("; HttpOnly"); - return false; + return output.ToString(); } - } - result = res; - parseError = String.Empty; + /// + /// The toResponseStringVersion1 + /// + /// The + private string toResponseStringVersion1() + { + var output = new StringBuilder(64); + output.AppendFormat("{0}={1}; Version={2}", _name, _value, _version); - return true; - } + if (_expires != DateTime.MinValue) + output.AppendFormat("; Max-Age={0}", MaxAge); - #endregion + if (!_path.IsNullOrEmpty()) + output.AppendFormat("; Path={0}", _path); - #region Internal Methods + if (!_domain.IsNullOrEmpty()) + output.AppendFormat("; Domain={0}", _domain); - // From client to server - internal string ToRequestString (Uri uri) - { - if (_name.Length == 0) - return String.Empty; - - if (_version == 0) - return String.Format ("{0}={1}", _name, _value); - - var output = new StringBuilder (64); - output.AppendFormat ("$Version={0}; {1}={2}", _version, _name, _value); - - if (!_path.IsNullOrEmpty ()) - output.AppendFormat ("; $Path={0}", _path); - else if (uri != null) - output.AppendFormat ("; $Path={0}", uri.GetAbsolutePath ()); - else - output.Append ("; $Path=/"); - - var appendDomain = uri == null || uri.Host != _domain; - if (appendDomain && !_domain.IsNullOrEmpty ()) - output.AppendFormat ("; $Domain={0}", _domain); - - if (!_port.IsNullOrEmpty ()) { - if (_port == "\"\"") - output.Append ("; $Port"); - else - output.AppendFormat ("; $Port={0}", _port); - } - - return output.ToString (); - } + if (!_port.IsNullOrEmpty()) + { + if (_port == "\"\"") + output.Append("; Port"); + else + output.AppendFormat("; Port={0}", _port); + } - // From server to client - internal string ToResponseString () - { - return _name.Length > 0 - ? (_version == 0 ? toResponseStringVersion0 () : toResponseStringVersion1 ()) - : String.Empty; - } + if (!_comment.IsNullOrEmpty()) + output.AppendFormat("; Comment={0}", _comment.UrlEncode()); - #endregion + if (_commentUri != null) + { + var url = _commentUri.OriginalString; + output.AppendFormat("; CommentURL={0}", url.IsToken() ? url : url.Quote()); + } - #region Public Methods + if (_discard) + output.Append("; Discard"); - /// - /// Determines whether the specified is equal to the current - /// . - /// - /// - /// An to compare with the current . - /// - /// - /// true if is equal to the current ; - /// otherwise, false. - /// - public override bool Equals (Object comparand) - { - var cookie = comparand as Cookie; - return cookie != null && - _name.Equals (cookie.Name, StringComparison.InvariantCultureIgnoreCase) && - _value.Equals (cookie.Value, StringComparison.InvariantCulture) && - _path.Equals (cookie.Path, StringComparison.InvariantCulture) && - _domain.Equals (cookie.Domain, StringComparison.InvariantCultureIgnoreCase) && - _version == cookie.Version; - } + if (_secure) + output.Append("; Secure"); - /// - /// Serves as a hash function for a object. - /// - /// - /// An that represents the hash code for the current . - /// - public override int GetHashCode () - { - return hash ( - StringComparer.InvariantCultureIgnoreCase.GetHashCode (_name), - _value.GetHashCode (), - _path.GetHashCode (), - StringComparer.InvariantCultureIgnoreCase.GetHashCode (_domain), - _version); - } + return output.ToString(); + } - /// - /// Returns a that represents the current . - /// - /// - /// This method returns a to use to send an HTTP Cookie to - /// an origin server. - /// - /// - /// A that represents the current . - /// - public override string ToString () - { - // i.e., only used for clients - // See para 4.2.2 of RFC 2109 and para 3.3.4 of RFC 2965 - // See also bug #316017 - return ToRequestString (null); + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/CookieCollection.cs b/src/WebSocket/WebSocketCore/Net/CookieCollection.cs index dbcc4a0d5..211f351df 100644 --- a/src/WebSocket/WebSocketCore/Net/CookieCollection.cs +++ b/src/WebSocket/WebSocketCore/Net/CookieCollection.cs @@ -1,4 +1,3 @@ -#region License /* * CookieCollection.cs * @@ -28,16 +27,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Lawrence Pit * - Gonzalo Paniagua Javier * - Sebastien Pouliot */ -#endregion using System; using System.Collections; @@ -47,552 +43,569 @@ namespace WebSocketCore.Net { - /// - /// Provides a collection container for instances of the class. - /// - [Serializable] - public class CookieCollection : ICollection, IEnumerable - { - #region Private Fields - - private List _list; - private object _sync; - - #endregion - - #region Public Constructors - /// - /// Initializes a new instance of the class. + /// Provides a collection container for instances of the class. /// - public CookieCollection () + [Serializable] + public class CookieCollection : ICollection, IEnumerable { - _list = new List (); - } - - #endregion - - #region Internal Properties - - internal IList List { - get { - return _list; - } - } - - internal IEnumerable Sorted { - get { - var list = new List (_list); - if (list.Count > 1) - list.Sort (compareCookieWithinSorted); - - return list; - } - } - - #endregion - - #region Public Properties - - /// - /// Gets the number of cookies in the collection. - /// - /// - /// An that represents the number of cookies in the collection. - /// - public int Count { - get { - return _list.Count; - } - } - - /// - /// Gets a value indicating whether the collection is read-only. - /// - /// - /// true if the collection is read-only; otherwise, false. - /// The default value is true. - /// - public bool IsReadOnly { - // LAMESPEC: So how is one supposed to create a writable CookieCollection instance? - // We simply ignore this property, as this collection is always writable. - get { - return true; - } - } - - /// - /// Gets a value indicating whether the access to the collection is thread safe. - /// - /// - /// true if the access to the collection is thread safe; otherwise, false. - /// The default value is false. - /// - public bool IsSynchronized { - get { - return false; - } - } - - /// - /// Gets the at the specified from - /// the collection. - /// - /// - /// A at the specified in the collection. - /// - /// - /// An that represents the zero-based index of the - /// to find. - /// - /// - /// is out of allowable range of indexes for the collection. - /// - public Cookie this[int index] { - get { - if (index < 0 || index >= _list.Count) - throw new ArgumentOutOfRangeException ("index"); - - return _list[index]; - } - } - - /// - /// Gets the with the specified from - /// the collection. - /// - /// - /// A with the specified in the collection. - /// - /// - /// A that represents the name of the to find. - /// - /// - /// is . - /// - public Cookie this[string name] { - get { - if (name == null) - throw new ArgumentNullException ("name"); - - foreach (var cookie in Sorted) - if (cookie.Name.Equals (name, StringComparison.InvariantCultureIgnoreCase)) - return cookie; - - return null; - } - } - - /// - /// Gets an object used to synchronize access to the collection. - /// - /// - /// An used to synchronize access to the collection. - /// - public Object SyncRoot { - get { - return _sync ?? (_sync = ((ICollection) _list).SyncRoot); - } - } - - #endregion - - #region Private Methods - - private static int compareCookieWithinSort (Cookie x, Cookie y) - { - return (x.Name.Length + x.Value.Length) - (y.Name.Length + y.Value.Length); - } - - private static int compareCookieWithinSorted (Cookie x, Cookie y) - { - var ret = 0; - return (ret = x.Version - y.Version) != 0 - ? ret - : (ret = x.Name.CompareTo (y.Name)) != 0 - ? ret - : y.Path.Length - x.Path.Length; - } - - private static CookieCollection parseRequest (string value) - { - var cookies = new CookieCollection (); - - Cookie cookie = null; - var ver = 0; - var pairs = splitCookieHeaderValue (value); - for (var i = 0; i < pairs.Length; i++) { - var pair = pairs[i].Trim (); - if (pair.Length == 0) - continue; - - if (pair.StartsWith ("$version", StringComparison.InvariantCultureIgnoreCase)) { - ver = Int32.Parse (pair.GetValue ('=', true)); + private List _list; + private object _sync; + + /// + /// Initializes a new instance of the class. + /// + public CookieCollection() + { + _list = new List(); } - else if (pair.StartsWith ("$path", StringComparison.InvariantCultureIgnoreCase)) { - if (cookie != null) - cookie.Path = pair.GetValue ('='); - } - else if (pair.StartsWith ("$domain", StringComparison.InvariantCultureIgnoreCase)) { - if (cookie != null) - cookie.Domain = pair.GetValue ('='); - } - else if (pair.StartsWith ("$port", StringComparison.InvariantCultureIgnoreCase)) { - var port = pair.Equals ("$port", StringComparison.InvariantCultureIgnoreCase) - ? "\"\"" - : pair.GetValue ('='); - if (cookie != null) - cookie.Port = port; + internal IList List + { + get + { + return _list; + } } - else { - if (cookie != null) - cookies.Add (cookie); - - string name; - string val = String.Empty; - - var pos = pair.IndexOf ('='); - if (pos == -1) { - name = pair; - } - else if (pos == pair.Length - 1) { - name = pair.Substring (0, pos).TrimEnd (' '); - } - else { - name = pair.Substring (0, pos).TrimEnd (' '); - val = pair.Substring (pos + 1).TrimStart (' '); - } - - cookie = new Cookie (name, val); - if (ver != 0) - cookie.Version = ver; - } - } - - if (cookie != null) - cookies.Add (cookie); - return cookies; - } + internal IEnumerable Sorted + { + get + { + var list = new List(_list); + if (list.Count > 1) + list.Sort(compareCookieWithinSorted); - private static CookieCollection parseResponse (string value) - { - var cookies = new CookieCollection (); - - Cookie cookie = null; - var pairs = splitCookieHeaderValue (value); - for (var i = 0; i < pairs.Length; i++) { - var pair = pairs[i].Trim (); - if (pair.Length == 0) - continue; - - if (pair.StartsWith ("version", StringComparison.InvariantCultureIgnoreCase)) { - if (cookie != null) - cookie.Version = Int32.Parse (pair.GetValue ('=', true)); - } - else if (pair.StartsWith ("expires", StringComparison.InvariantCultureIgnoreCase)) { - var buff = new StringBuilder (pair.GetValue ('='), 32); - if (i < pairs.Length - 1) - buff.AppendFormat (", {0}", pairs[++i].Trim ()); - - DateTime expires; - if (!DateTime.TryParseExact ( - buff.ToString (), - new[] { "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'", "r" }, - CultureInfo.CreateSpecificCulture ("en-US"), - DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, - out expires)) - expires = DateTime.Now; - - if (cookie != null && cookie.Expires == DateTime.MinValue) - cookie.Expires = expires.ToLocalTime (); - } - else if (pair.StartsWith ("max-age", StringComparison.InvariantCultureIgnoreCase)) { - var max = Int32.Parse (pair.GetValue ('=', true)); - var expires = DateTime.Now.AddSeconds ((double) max); - if (cookie != null) - cookie.Expires = expires; + return list; + } } - else if (pair.StartsWith ("path", StringComparison.InvariantCultureIgnoreCase)) { - if (cookie != null) - cookie.Path = pair.GetValue ('='); - } - else if (pair.StartsWith ("domain", StringComparison.InvariantCultureIgnoreCase)) { - if (cookie != null) - cookie.Domain = pair.GetValue ('='); - } - else if (pair.StartsWith ("port", StringComparison.InvariantCultureIgnoreCase)) { - var port = pair.Equals ("port", StringComparison.InvariantCultureIgnoreCase) - ? "\"\"" - : pair.GetValue ('='); - if (cookie != null) - cookie.Port = port; - } - else if (pair.StartsWith ("comment", StringComparison.InvariantCultureIgnoreCase)) { - if (cookie != null) - cookie.Comment = pair.GetValue ('=').UrlDecode (); - } - else if (pair.StartsWith ("commenturl", StringComparison.InvariantCultureIgnoreCase)) { - if (cookie != null) - cookie.CommentUri = pair.GetValue ('=', true).ToUri (); - } - else if (pair.StartsWith ("discard", StringComparison.InvariantCultureIgnoreCase)) { - if (cookie != null) - cookie.Discard = true; - } - else if (pair.StartsWith ("secure", StringComparison.InvariantCultureIgnoreCase)) { - if (cookie != null) - cookie.Secure = true; - } - else if (pair.StartsWith ("httponly", StringComparison.InvariantCultureIgnoreCase)) { - if (cookie != null) - cookie.HttpOnly = true; + /// + /// Gets the number of cookies in the collection. + /// + /// + /// An that represents the number of cookies in the collection. + /// + public int Count + { + get + { + return _list.Count; + } } - else { - if (cookie != null) - cookies.Add (cookie); - - string name; - string val = String.Empty; - - var pos = pair.IndexOf ('='); - if (pos == -1) { - name = pair; - } - else if (pos == pair.Length - 1) { - name = pair.Substring (0, pos).TrimEnd (' '); - } - else { - name = pair.Substring (0, pos).TrimEnd (' '); - val = pair.Substring (pos + 1).TrimStart (' '); - } - - cookie = new Cookie (name, val); - } - } - - if (cookie != null) - cookies.Add (cookie); - - return cookies; - } - - private int searchCookie (Cookie cookie) - { - var name = cookie.Name; - var path = cookie.Path; - var domain = cookie.Domain; - var ver = cookie.Version; - - for (var i = _list.Count - 1; i >= 0; i--) { - var c = _list[i]; - if (c.Name.Equals (name, StringComparison.InvariantCultureIgnoreCase) && - c.Path.Equals (path, StringComparison.InvariantCulture) && - c.Domain.Equals (domain, StringComparison.InvariantCultureIgnoreCase) && - c.Version == ver) - return i; - } - - return -1; - } - - private static string[] splitCookieHeaderValue (string value) - { - return new List (value.SplitHeaderValue (',', ';')).ToArray (); - } - - #endregion - - #region Internal Methods - internal static CookieCollection Parse (string value, bool response) - { - return response - ? parseResponse (value) - : parseRequest (value); - } - - internal void SetOrRemove (Cookie cookie) - { - var pos = searchCookie (cookie); - if (pos == -1) { - if (!cookie.Expired) - _list.Add (cookie); - - return; - } + /// + /// Gets a value indicating whether the collection is read-only. + /// + /// + /// true if the collection is read-only; otherwise, false. + /// The default value is true. + /// + public bool IsReadOnly + { + // LAMESPEC: So how is one supposed to create a writable CookieCollection instance? + // We simply ignore this property, as this collection is always writable. + get + { + return true; + } + } - if (!cookie.Expired) { - _list[pos] = cookie; - return; - } + /// + /// Gets a value indicating whether the access to the collection is thread safe. + /// + /// + /// true if the access to the collection is thread safe; otherwise, false. + /// The default value is false. + /// + public bool IsSynchronized + { + get + { + return false; + } + } - _list.RemoveAt (pos); - } + /// + /// Gets the at the specified from + /// the collection. + /// + /// + /// A at the specified in the collection. + /// + /// + /// An that represents the zero-based index of the + /// to find. + /// + /// + /// is out of allowable range of indexes for the collection. + /// + public Cookie this[int index] + { + get + { + if (index < 0 || index >= _list.Count) + throw new ArgumentOutOfRangeException("index"); + + return _list[index]; + } + } - internal void SetOrRemove (CookieCollection cookies) - { - foreach (Cookie cookie in cookies) - SetOrRemove (cookie); - } + /// + /// Gets the with the specified from + /// the collection. + /// + /// + /// A with the specified in the collection. + /// + /// + /// A that represents the name of the to find. + /// + /// + /// is . + /// + public Cookie this[string name] + { + get + { + if (name == null) + throw new ArgumentNullException("name"); + + foreach (var cookie in Sorted) + if (cookie.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) + return cookie; + + return null; + } + } - internal void Sort () - { - if (_list.Count > 1) - _list.Sort (compareCookieWithinSort); - } + /// + /// Gets an object used to synchronize access to the collection. + /// + /// + /// An used to synchronize access to the collection. + /// + public Object SyncRoot + { + get + { + return _sync ?? (_sync = ((ICollection)_list).SyncRoot); + } + } - #endregion + private static int compareCookieWithinSort(Cookie x, Cookie y) + { + return (x.Name.Length + x.Value.Length) - (y.Name.Length + y.Value.Length); + } - #region Public Methods + private static int compareCookieWithinSorted(Cookie x, Cookie y) + { + var ret = 0; + return (ret = x.Version - y.Version) != 0 + ? ret + : (ret = x.Name.CompareTo(y.Name)) != 0 + ? ret + : y.Path.Length - x.Path.Length; + } - /// - /// Adds the specified to the collection. - /// - /// - /// A to add. - /// - /// - /// is . - /// - public void Add (Cookie cookie) - { - if (cookie == null) - throw new ArgumentNullException ("cookie"); + private static CookieCollection parseRequest(string value) + { + var cookies = new CookieCollection(); + + Cookie cookie = null; + var ver = 0; + var pairs = splitCookieHeaderValue(value); + for (var i = 0; i < pairs.Length; i++) + { + var pair = pairs[i].Trim(); + if (pair.Length == 0) + continue; + + if (pair.StartsWith("$version", StringComparison.InvariantCultureIgnoreCase)) + { + ver = Int32.Parse(pair.GetValue('=', true)); + } + else if (pair.StartsWith("$path", StringComparison.InvariantCultureIgnoreCase)) + { + if (cookie != null) + cookie.Path = pair.GetValue('='); + } + else if (pair.StartsWith("$domain", StringComparison.InvariantCultureIgnoreCase)) + { + if (cookie != null) + cookie.Domain = pair.GetValue('='); + } + else if (pair.StartsWith("$port", StringComparison.InvariantCultureIgnoreCase)) + { + var port = pair.Equals("$port", StringComparison.InvariantCultureIgnoreCase) + ? "\"\"" + : pair.GetValue('='); + + if (cookie != null) + cookie.Port = port; + } + else + { + if (cookie != null) + cookies.Add(cookie); + + string name; + string val = String.Empty; + + var pos = pair.IndexOf('='); + if (pos == -1) + { + name = pair; + } + else if (pos == pair.Length - 1) + { + name = pair.Substring(0, pos).TrimEnd(' '); + } + else + { + name = pair.Substring(0, pos).TrimEnd(' '); + val = pair.Substring(pos + 1).TrimStart(' '); + } + + cookie = new Cookie(name, val); + if (ver != 0) + cookie.Version = ver; + } + } + + if (cookie != null) + cookies.Add(cookie); + + return cookies; + } - var pos = searchCookie (cookie); - if (pos == -1) { - _list.Add (cookie); - return; - } + private static CookieCollection parseResponse(string value) + { + var cookies = new CookieCollection(); + + Cookie cookie = null; + var pairs = splitCookieHeaderValue(value); + for (var i = 0; i < pairs.Length; i++) + { + var pair = pairs[i].Trim(); + if (pair.Length == 0) + continue; + + if (pair.StartsWith("version", StringComparison.InvariantCultureIgnoreCase)) + { + if (cookie != null) + cookie.Version = Int32.Parse(pair.GetValue('=', true)); + } + else if (pair.StartsWith("expires", StringComparison.InvariantCultureIgnoreCase)) + { + var buff = new StringBuilder(pair.GetValue('='), 32); + if (i < pairs.Length - 1) + buff.AppendFormat(", {0}", pairs[++i].Trim()); + + DateTime expires; + if (!DateTime.TryParseExact( + buff.ToString(), + new[] { "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'", "r" }, + CultureInfo.CreateSpecificCulture("en-US"), + DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, + out expires)) + expires = DateTime.Now; + + if (cookie != null && cookie.Expires == DateTime.MinValue) + cookie.Expires = expires.ToLocalTime(); + } + else if (pair.StartsWith("max-age", StringComparison.InvariantCultureIgnoreCase)) + { + var max = Int32.Parse(pair.GetValue('=', true)); + var expires = DateTime.Now.AddSeconds((double)max); + if (cookie != null) + cookie.Expires = expires; + } + else if (pair.StartsWith("path", StringComparison.InvariantCultureIgnoreCase)) + { + if (cookie != null) + cookie.Path = pair.GetValue('='); + } + else if (pair.StartsWith("domain", StringComparison.InvariantCultureIgnoreCase)) + { + if (cookie != null) + cookie.Domain = pair.GetValue('='); + } + else if (pair.StartsWith("port", StringComparison.InvariantCultureIgnoreCase)) + { + var port = pair.Equals("port", StringComparison.InvariantCultureIgnoreCase) + ? "\"\"" + : pair.GetValue('='); + + if (cookie != null) + cookie.Port = port; + } + else if (pair.StartsWith("comment", StringComparison.InvariantCultureIgnoreCase)) + { + if (cookie != null) + cookie.Comment = pair.GetValue('=').UrlDecode(); + } + else if (pair.StartsWith("commenturl", StringComparison.InvariantCultureIgnoreCase)) + { + if (cookie != null) + cookie.CommentUri = pair.GetValue('=', true).ToUri(); + } + else if (pair.StartsWith("discard", StringComparison.InvariantCultureIgnoreCase)) + { + if (cookie != null) + cookie.Discard = true; + } + else if (pair.StartsWith("secure", StringComparison.InvariantCultureIgnoreCase)) + { + if (cookie != null) + cookie.Secure = true; + } + else if (pair.StartsWith("httponly", StringComparison.InvariantCultureIgnoreCase)) + { + if (cookie != null) + cookie.HttpOnly = true; + } + else + { + if (cookie != null) + cookies.Add(cookie); + + string name; + string val = String.Empty; + + var pos = pair.IndexOf('='); + if (pos == -1) + { + name = pair; + } + else if (pos == pair.Length - 1) + { + name = pair.Substring(0, pos).TrimEnd(' '); + } + else + { + name = pair.Substring(0, pos).TrimEnd(' '); + val = pair.Substring(pos + 1).TrimStart(' '); + } + + cookie = new Cookie(name, val); + } + } + + if (cookie != null) + cookies.Add(cookie); + + return cookies; + } - _list[pos] = cookie; - } + private int searchCookie(Cookie cookie) + { + var name = cookie.Name; + var path = cookie.Path; + var domain = cookie.Domain; + var ver = cookie.Version; + + for (var i = _list.Count - 1; i >= 0; i--) + { + var c = _list[i]; + if (c.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase) && + c.Path.Equals(path, StringComparison.InvariantCulture) && + c.Domain.Equals(domain, StringComparison.InvariantCultureIgnoreCase) && + c.Version == ver) + return i; + } + + return -1; + } - /// - /// Adds the specified to the collection. - /// - /// - /// A that contains the cookies to add. - /// - /// - /// is . - /// - public void Add (CookieCollection cookies) - { - if (cookies == null) - throw new ArgumentNullException ("cookies"); + private static string[] splitCookieHeaderValue(string value) + { + return new List(value.SplitHeaderValue(',', ';')).ToArray(); + } - foreach (Cookie cookie in cookies) - Add (cookie); - } + internal static CookieCollection Parse(string value, bool response) + { + return response + ? parseResponse(value) + : parseRequest(value); + } - /// - /// Copies the elements of the collection to the specified , starting at - /// the specified in the . - /// - /// - /// An that represents the destination of the elements copied from - /// the collection. - /// - /// - /// An that represents the zero-based index in - /// at which copying begins. - /// - /// - /// is . - /// - /// - /// is less than zero. - /// - /// - /// - /// is multidimensional. - /// - /// - /// -or- - /// - /// - /// The number of elements in the collection is greater than the available space from - /// to the end of the destination . - /// - /// - /// - /// The elements in the collection cannot be cast automatically to the type of the destination - /// . - /// - public void CopyTo (Array array, int index) - { - if (array == null) - throw new ArgumentNullException ("array"); + internal void SetOrRemove(Cookie cookie) + { + var pos = searchCookie(cookie); + if (pos == -1) + { + if (!cookie.Expired) + _list.Add(cookie); - if (index < 0) - throw new ArgumentOutOfRangeException ("index", "Less than zero."); + return; + } - if (array.Rank > 1) - throw new ArgumentException ("Multidimensional.", "array"); + if (!cookie.Expired) + { + _list[pos] = cookie; + return; + } - if (array.Length - index < _list.Count) - throw new ArgumentException ( - "The number of elements in this collection is greater than the available space of the destination array."); + _list.RemoveAt(pos); + } - if (!array.GetType ().GetElementType ().IsAssignableFrom (typeof (Cookie))) - throw new InvalidCastException ( - "The elements in this collection cannot be cast automatically to the type of the destination array."); + internal void SetOrRemove(CookieCollection cookies) + { + foreach (Cookie cookie in cookies) + SetOrRemove(cookie); + } - ((IList) _list).CopyTo (array, index); - } + internal void Sort() + { + if (_list.Count > 1) + _list.Sort(compareCookieWithinSort); + } - /// - /// Copies the elements of the collection to the specified array of , - /// starting at the specified in the . - /// - /// - /// An array of that represents the destination of the elements - /// copied from the collection. - /// - /// - /// An that represents the zero-based index in - /// at which copying begins. - /// - /// - /// is . - /// - /// - /// is less than zero. - /// - /// - /// The number of elements in the collection is greater than the available space from - /// to the end of the destination . - /// - public void CopyTo (Cookie[] array, int index) - { - if (array == null) - throw new ArgumentNullException ("array"); + /// + /// Adds the specified to the collection. + /// + /// + /// A to add. + /// + /// + /// is . + /// + public void Add(Cookie cookie) + { + if (cookie == null) + throw new ArgumentNullException("cookie"); + + var pos = searchCookie(cookie); + if (pos == -1) + { + _list.Add(cookie); + return; + } + + _list[pos] = cookie; + } - if (index < 0) - throw new ArgumentOutOfRangeException ("index", "Less than zero."); + /// + /// Adds the specified to the collection. + /// + /// + /// A that contains the cookies to add. + /// + /// + /// is . + /// + public void Add(CookieCollection cookies) + { + if (cookies == null) + throw new ArgumentNullException("cookies"); + + foreach (Cookie cookie in cookies) + Add(cookie); + } - if (array.Length - index < _list.Count) - throw new ArgumentException ( - "The number of elements in this collection is greater than the available space of the destination array."); + /// + /// Copies the elements of the collection to the specified , starting at + /// the specified in the . + /// + /// + /// An that represents the destination of the elements copied from + /// the collection. + /// + /// + /// An that represents the zero-based index in + /// at which copying begins. + /// + /// + /// is . + /// + /// + /// is less than zero. + /// + /// + /// + /// is multidimensional. + /// + /// + /// -or- + /// + /// + /// The number of elements in the collection is greater than the available space from + /// to the end of the destination . + /// + /// + /// + /// The elements in the collection cannot be cast automatically to the type of the destination + /// . + /// + public void CopyTo(Array array, int index) + { + if (array == null) + throw new ArgumentNullException("array"); + + if (index < 0) + throw new ArgumentOutOfRangeException("index", "Less than zero."); + + if (array.Rank > 1) + throw new ArgumentException("Multidimensional.", "array"); + + if (array.Length - index < _list.Count) + throw new ArgumentException( + "The number of elements in this collection is greater than the available space of the destination array."); + + if (!array.GetType().GetElementType().IsAssignableFrom(typeof(Cookie))) + throw new InvalidCastException( + "The elements in this collection cannot be cast automatically to the type of the destination array."); + + ((IList)_list).CopyTo(array, index); + } - _list.CopyTo (array, index); - } + /// + /// Copies the elements of the collection to the specified array of , + /// starting at the specified in the . + /// + /// + /// An array of that represents the destination of the elements + /// copied from the collection. + /// + /// + /// An that represents the zero-based index in + /// at which copying begins. + /// + /// + /// is . + /// + /// + /// is less than zero. + /// + /// + /// The number of elements in the collection is greater than the available space from + /// to the end of the destination . + /// + public void CopyTo(Cookie[] array, int index) + { + if (array == null) + throw new ArgumentNullException("array"); + + if (index < 0) + throw new ArgumentOutOfRangeException("index", "Less than zero."); + + if (array.Length - index < _list.Count) + throw new ArgumentException( + "The number of elements in this collection is greater than the available space of the destination array."); + + _list.CopyTo(array, index); + } - /// - /// Gets the enumerator used to iterate through the collection. - /// - /// - /// An instance used to iterate through the collection. - /// - public IEnumerator GetEnumerator () - { - return _list.GetEnumerator (); + /// + /// Gets the enumerator used to iterate through the collection. + /// + /// + /// An instance used to iterate through the collection. + /// + public IEnumerator GetEnumerator() + { + return _list.GetEnumerator(); + } } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/CookieException.cs b/src/WebSocket/WebSocketCore/Net/CookieException.cs index bdff51e17..7a495c17a 100644 --- a/src/WebSocket/WebSocketCore/Net/CookieException.cs +++ b/src/WebSocket/WebSocketCore/Net/CookieException.cs @@ -1,4 +1,3 @@ -#region License /* * CookieException.cs * @@ -27,14 +26,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Lawrence Pit */ -#endregion using System; using System.Runtime.Serialization; @@ -42,102 +38,86 @@ namespace WebSocketCore.Net { - /// - /// The exception that is thrown when a gets an error. - /// - [Serializable] - public class CookieException : FormatException, ISerializable - { - #region Internal Constructors - - internal CookieException (string message) - : base (message) - { - } - - internal CookieException (string message, Exception innerException) - : base (message, innerException) - { - } - - #endregion - - #region Protected Constructors - /// - /// Initializes a new instance of the class from - /// the specified and . + /// The exception that is thrown when a gets an error. /// - /// - /// A that contains the serialized object data. - /// - /// - /// A that specifies the source for the deserialization. - /// - protected CookieException ( - SerializationInfo serializationInfo, StreamingContext streamingContext) - : base (serializationInfo, streamingContext) + [Serializable] + public class CookieException : FormatException, ISerializable { - } - - #endregion - - #region Public Constructors - - /// - /// Initializes a new instance of the class. - /// - public CookieException () - : base () - { - } + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + public CookieException() + : base() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message + internal CookieException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message + /// The innerException + internal CookieException(string message, Exception innerException) + : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The serializationInfo + /// The streamingContext + protected CookieException( + SerializationInfo serializationInfo, StreamingContext streamingContext) + : base(serializationInfo, streamingContext) + { + } - #endregion + #endregion 캯 - #region Public Methods + #region - /// - /// Populates the specified with the data needed to serialize - /// the current . - /// - /// - /// A that holds the serialized object data. - /// - /// - /// A that specifies the destination for the serialization. - /// - [SecurityPermission ( + /// + /// Populates the specified with the data needed to serialize + /// the current . + /// + /// The serializationInfo + /// The streamingContext + [SecurityPermission( SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] - public override void GetObjectData ( + public override void GetObjectData( SerializationInfo serializationInfo, StreamingContext streamingContext) - { - base.GetObjectData (serializationInfo, streamingContext); - } - - #endregion - - #region Explicit Interface Implementation - - /// - /// Populates the specified with the data needed to serialize - /// the current . - /// - /// - /// A that holds the serialized object data. - /// - /// - /// A that specifies the destination for the serialization. - /// - [SecurityPermission ( + { + base.GetObjectData(serializationInfo, streamingContext); + } + + /// + /// Populates the specified with the data needed to serialize + /// the current . + /// + /// The serializationInfo + /// The streamingContext + [SecurityPermission( SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter, SerializationFormatter = true)] - void ISerializable.GetObjectData ( + void ISerializable.GetObjectData( SerializationInfo serializationInfo, StreamingContext streamingContext) - { - base.GetObjectData (serializationInfo, streamingContext); - } + { + base.GetObjectData(serializationInfo, streamingContext); + } - #endregion - } -} + #endregion + } +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/EndPointListener.cs b/src/WebSocket/WebSocketCore/Net/EndPointListener.cs index c7ffdcee5..a5d391577 100644 --- a/src/WebSocket/WebSocketCore/Net/EndPointListener.cs +++ b/src/WebSocket/WebSocketCore/Net/EndPointListener.cs @@ -1,4 +1,3 @@ -#region License /* * EndPointListener.cs * @@ -28,22 +27,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Gonzalo Paniagua Javier */ -#endregion -#region Contributors /* * Contributors: * - Liryna * - Nicholas Devenish */ -#endregion using System; using System.Collections; @@ -57,459 +51,625 @@ namespace WebSocketCore.Net { - internal sealed class EndPointListener - { - #region Private Fields - - private List _all; // host == '+' - private static readonly string _defaultCertFolderPath; - private IPEndPoint _endpoint; - private Dictionary _prefixes; - private bool _secure; - private Socket _socket; - private ServerSslConfiguration _sslConfig; - private List _unhandled; // host == '*' - private Dictionary _unregistered; - private object _unregisteredSync; - - #endregion - - #region Static Constructor - - static EndPointListener () + /// + /// Defines the + /// + internal sealed class EndPointListener { - _defaultCertFolderPath = - Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData); - } - - #endregion - - #region Internal Constructors - - internal EndPointListener ( + #region ֶ + + /// + /// Defines the _defaultCertFolderPath + /// + private static readonly string _defaultCertFolderPath; + + /// + /// Defines the _all + /// + private List _all;// host == '+' + + /// + /// Defines the _endpoint + /// + private IPEndPoint _endpoint; + + /// + /// Defines the _prefixes + /// + private Dictionary _prefixes; + + /// + /// Defines the _secure + /// + private bool _secure; + + /// + /// Defines the _socket + /// + private Socket _socket; + + /// + /// Defines the _sslConfig + /// + private ServerSslConfiguration _sslConfig; + + /// + /// Defines the _unhandled + /// + private List _unhandled;// host == '*' + + /// + /// Defines the _unregistered + /// + private Dictionary _unregistered; + + /// + /// Defines the _unregisteredSync + /// + private object _unregisteredSync; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The endpoint + /// The secure + /// The certificateFolderPath + /// The sslConfig + /// The reuseAddress + internal EndPointListener( IPEndPoint endpoint, bool secure, string certificateFolderPath, ServerSslConfiguration sslConfig, bool reuseAddress ) - { - if (secure) { - var cert = - getCertificate (endpoint.Port, certificateFolderPath, sslConfig.ServerCertificate); - - if (cert == null) - throw new ArgumentException ("No server certificate could be found."); - - _secure = true; - _sslConfig = new ServerSslConfiguration (sslConfig); - _sslConfig.ServerCertificate = cert; - } - - _endpoint = endpoint; - _prefixes = new Dictionary (); - _unregistered = new Dictionary (); - _unregisteredSync = ((ICollection) _unregistered).SyncRoot; - _socket = - new Socket (endpoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); - - if (reuseAddress) - _socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); - - _socket.Bind (endpoint); - _socket.Listen (500); - _socket.BeginAccept (onAccept, this); - } - - #endregion - - #region Public Properties - - public IPAddress Address { - get { - return _endpoint.Address; - } - } - - public bool IsSecure { - get { - return _secure; - } - } - - public int Port { - get { - return _endpoint.Port; - } - } - - public ServerSslConfiguration SslConfiguration { - get { - return _sslConfig; - } - } - - #endregion - - #region Private Methods - - private static void addSpecial (List prefixes, HttpListenerPrefix prefix) - { - var path = prefix.Path; - foreach (var pref in prefixes) { - if (pref.Path == path) - throw new HttpListenerException (87, "The prefix is already in use."); - } - - prefixes.Add (prefix); - } - - private static RSACryptoServiceProvider createRSAFromFile (string filename) - { - byte[] pvk = null; - using (var fs = File.Open (filename, FileMode.Open, FileAccess.Read, FileShare.Read)) { - pvk = new byte[fs.Length]; - fs.Read (pvk, 0, pvk.Length); - } + { + if (secure) + { + var cert = + getCertificate(endpoint.Port, certificateFolderPath, sslConfig.ServerCertificate); + + if (cert == null) + throw new ArgumentException("No server certificate could be found."); + + _secure = true; + _sslConfig = new ServerSslConfiguration(sslConfig); + _sslConfig.ServerCertificate = cert; + } + + _endpoint = endpoint; + _prefixes = new Dictionary(); + _unregistered = new Dictionary(); + _unregisteredSync = ((ICollection)_unregistered).SyncRoot; + _socket = + new Socket(endpoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + + if (reuseAddress) + _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + + _socket.Bind(endpoint); + _socket.Listen(500); + _socket.BeginAccept(onAccept, this); + } - var rsa = new RSACryptoServiceProvider (); - rsa.ImportCspBlob (pvk); + /// + /// Initializes static members of the class. + /// + static EndPointListener() + { + _defaultCertFolderPath = + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); + } - return rsa; - } + #endregion 캯 - private static X509Certificate2 getCertificate ( - int port, string folderPath, X509Certificate2 defaultCertificate - ) - { - if (folderPath == null || folderPath.Length == 0) - folderPath = _defaultCertFolderPath; + #region - try { - var cer = Path.Combine (folderPath, String.Format ("{0}.cer", port)); - var key = Path.Combine (folderPath, String.Format ("{0}.key", port)); - if (File.Exists (cer) && File.Exists (key)) { - var cert = new X509Certificate2 (cer); - cert.PrivateKey = createRSAFromFile (key); + /// + /// Gets the Address + /// + public IPAddress Address + { + get + { + return _endpoint.Address; + } + } - return cert; + /// + /// Gets a value indicating whether IsSecure + /// + public bool IsSecure + { + get + { + return _secure; + } } - } - catch { - } - return defaultCertificate; - } + /// + /// Gets the Port + /// + public int Port + { + get + { + return _endpoint.Port; + } + } - private void leaveIfNoPrefix () - { - if (_prefixes.Count > 0) - return; + /// + /// Gets the SslConfiguration + /// + public ServerSslConfiguration SslConfiguration + { + get + { + return _sslConfig; + } + } - var prefs = _unhandled; - if (prefs != null && prefs.Count > 0) - return; + #endregion + + #region + + /// + /// The AddPrefix + /// + /// The prefix + /// The listener + public void AddPrefix(HttpListenerPrefix prefix, HttpListener listener) + { + List current, future; + if (prefix.Host == "*") + { + do + { + current = _unhandled; + future = current != null + ? new List(current) + : new List(); + + prefix.Listener = listener; + addSpecial(future, prefix); + } + while (Interlocked.CompareExchange(ref _unhandled, future, current) != current); + + return; + } + + if (prefix.Host == "+") + { + do + { + current = _all; + future = current != null + ? new List(current) + : new List(); + + prefix.Listener = listener; + addSpecial(future, prefix); + } + while (Interlocked.CompareExchange(ref _all, future, current) != current); + + return; + } + + Dictionary prefs, prefs2; + do + { + prefs = _prefixes; + if (prefs.ContainsKey(prefix)) + { + if (prefs[prefix] != listener) + { + throw new HttpListenerException( + 87, String.Format("There's another listener for {0}.", prefix) + ); + } + + return; + } + + prefs2 = new Dictionary(prefs); + prefs2[prefix] = listener; + } + while (Interlocked.CompareExchange(ref _prefixes, prefs2, prefs) != prefs); + } - prefs = _all; - if (prefs != null && prefs.Count > 0) - return; + /// + /// The Close + /// + public void Close() + { + _socket.Close(); + + HttpConnection[] conns = null; + lock (_unregisteredSync) + { + if (_unregistered.Count == 0) + return; + + var keys = _unregistered.Keys; + conns = new HttpConnection[keys.Count]; + keys.CopyTo(conns, 0); + _unregistered.Clear(); + } + + for (var i = conns.Length - 1; i >= 0; i--) + conns[i].Close(true); + } - EndPointManager.RemoveEndPoint (_endpoint); - } + /// + /// The RemovePrefix + /// + /// The prefix + /// The listener + public void RemovePrefix(HttpListenerPrefix prefix, HttpListener listener) + { + List current, future; + if (prefix.Host == "*") + { + do + { + current = _unhandled; + if (current == null) + break; + + future = new List(current); + if (!removeSpecial(future, prefix)) + break; // The prefix wasn't found. + } + while (Interlocked.CompareExchange(ref _unhandled, future, current) != current); + + leaveIfNoPrefix(); + return; + } + + if (prefix.Host == "+") + { + do + { + current = _all; + if (current == null) + break; + + future = new List(current); + if (!removeSpecial(future, prefix)) + break; // The prefix wasn't found. + } + while (Interlocked.CompareExchange(ref _all, future, current) != current); + + leaveIfNoPrefix(); + return; + } + + Dictionary prefs, prefs2; + do + { + prefs = _prefixes; + if (!prefs.ContainsKey(prefix)) + break; + + prefs2 = new Dictionary(prefs); + prefs2.Remove(prefix); + } + while (Interlocked.CompareExchange(ref _prefixes, prefs2, prefs) != prefs); + + leaveIfNoPrefix(); + } - private static void onAccept (IAsyncResult asyncResult) - { - var lsnr = (EndPointListener) asyncResult.AsyncState; - - Socket sock = null; - try { - sock = lsnr._socket.EndAccept (asyncResult); - } - catch (SocketException) { - // TODO: Should log the error code when this class has a logging. - } - catch (ObjectDisposedException) { - return; - } - - try { - lsnr._socket.BeginAccept (onAccept, lsnr); - } - catch { - if (sock != null) - sock.Close (); - - return; - } - - if (sock == null) - return; - - processAccepted (sock, lsnr); - } + /// + /// The CertificateExists + /// + /// The port + /// The folderPath + /// The + internal static bool CertificateExists(int port, string folderPath) + { + if (folderPath == null || folderPath.Length == 0) + folderPath = _defaultCertFolderPath; + + var cer = Path.Combine(folderPath, String.Format("{0}.cer", port)); + var key = Path.Combine(folderPath, String.Format("{0}.key", port)); + + return File.Exists(cer) && File.Exists(key); + } - private static void processAccepted (Socket socket, EndPointListener listener) - { - HttpConnection conn = null; - try { - conn = new HttpConnection (socket, listener); - lock (listener._unregisteredSync) - listener._unregistered[conn] = conn; - - conn.BeginReadRequest (); - } - catch { - if (conn != null) { - conn.Close (true); - return; + /// + /// The RemoveConnection + /// + /// The connection + internal void RemoveConnection(HttpConnection connection) + { + lock (_unregisteredSync) + _unregistered.Remove(connection); } - socket.Close (); - } - } + /// + /// The TrySearchHttpListener + /// + /// The uri + /// The listener + /// The + internal bool TrySearchHttpListener(Uri uri, out HttpListener listener) + { + listener = null; + + if (uri == null) + return false; + + var host = uri.Host; + var dns = Uri.CheckHostName(host) == UriHostNameType.Dns; + var port = uri.Port.ToString(); + var path = HttpUtility.UrlDecode(uri.AbsolutePath); + var pathSlash = path[path.Length - 1] != '/' ? path + "/" : path; + + if (host != null && host.Length > 0) + { + var bestLen = -1; + foreach (var pref in _prefixes.Keys) + { + if (dns) + { + var prefHost = pref.Host; + if (Uri.CheckHostName(prefHost) == UriHostNameType.Dns && prefHost != host) + continue; + } + + if (pref.Port != port) + continue; + + var prefPath = pref.Path; + + var len = prefPath.Length; + if (len < bestLen) + continue; + + if (path.StartsWith(prefPath) || pathSlash.StartsWith(prefPath)) + { + bestLen = len; + listener = _prefixes[pref]; + } + } + + if (bestLen != -1) + return true; + } + + var prefs = _unhandled; + listener = searchHttpListenerFromSpecial(path, prefs); + if (listener == null && pathSlash != path) + listener = searchHttpListenerFromSpecial(pathSlash, prefs); + + if (listener != null) + return true; + + prefs = _all; + listener = searchHttpListenerFromSpecial(path, prefs); + if (listener == null && pathSlash != path) + listener = searchHttpListenerFromSpecial(pathSlash, prefs); + + return listener != null; + } - private static bool removeSpecial (List prefixes, HttpListenerPrefix prefix) - { - var path = prefix.Path; - var cnt = prefixes.Count; - for (var i = 0; i < cnt; i++) { - if (prefixes[i].Path == path) { - prefixes.RemoveAt (i); - return true; + /// + /// The addSpecial + /// + /// The prefixes + /// The prefix + private static void addSpecial(List prefixes, HttpListenerPrefix prefix) + { + var path = prefix.Path; + foreach (var pref in prefixes) + { + if (pref.Path == path) + throw new HttpListenerException(87, "The prefix is already in use."); + } + + prefixes.Add(prefix); } - } - return false; - } + /// + /// The createRSAFromFile + /// + /// The filename + /// The + private static RSACryptoServiceProvider createRSAFromFile(string filename) + { + byte[] pvk = null; + using (var fs = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + pvk = new byte[fs.Length]; + fs.Read(pvk, 0, pvk.Length); + } + + var rsa = new RSACryptoServiceProvider(); + rsa.ImportCspBlob(pvk); + + return rsa; + } - private static HttpListener searchHttpListenerFromSpecial ( - string path, List prefixes + /// + /// The getCertificate + /// + /// The port + /// The folderPath + /// The defaultCertificate + /// The + private static X509Certificate2 getCertificate( + int port, string folderPath, X509Certificate2 defaultCertificate ) - { - if (prefixes == null) - return null; - - HttpListener bestMatch = null; - - var bestLen = -1; - foreach (var pref in prefixes) { - var prefPath = pref.Path; - - var len = prefPath.Length; - if (len < bestLen) - continue; - - if (path.StartsWith (prefPath)) { - bestLen = len; - bestMatch = pref.Listener; + { + if (folderPath == null || folderPath.Length == 0) + folderPath = _defaultCertFolderPath; + + try + { + var cer = Path.Combine(folderPath, String.Format("{0}.cer", port)); + var key = Path.Combine(folderPath, String.Format("{0}.key", port)); + if (File.Exists(cer) && File.Exists(key)) + { + var cert = new X509Certificate2(cer); + cert.PrivateKey = createRSAFromFile(key); + + return cert; + } + } + catch + { + } + + return defaultCertificate; } - } - - return bestMatch; - } - - #endregion - #region Internal Methods - - internal static bool CertificateExists (int port, string folderPath) - { - if (folderPath == null || folderPath.Length == 0) - folderPath = _defaultCertFolderPath; - - var cer = Path.Combine (folderPath, String.Format ("{0}.cer", port)); - var key = Path.Combine (folderPath, String.Format ("{0}.key", port)); - - return File.Exists (cer) && File.Exists (key); - } - - internal void RemoveConnection (HttpConnection connection) - { - lock (_unregisteredSync) - _unregistered.Remove (connection); - } - - internal bool TrySearchHttpListener (Uri uri, out HttpListener listener) - { - listener = null; - - if (uri == null) - return false; - - var host = uri.Host; - var dns = Uri.CheckHostName (host) == UriHostNameType.Dns; - var port = uri.Port.ToString (); - var path = HttpUtility.UrlDecode (uri.AbsolutePath); - var pathSlash = path[path.Length - 1] != '/' ? path + "/" : path; - - if (host != null && host.Length > 0) { - var bestLen = -1; - foreach (var pref in _prefixes.Keys) { - if (dns) { - var prefHost = pref.Host; - if (Uri.CheckHostName (prefHost) == UriHostNameType.Dns && prefHost != host) - continue; - } - - if (pref.Port != port) - continue; - - var prefPath = pref.Path; - - var len = prefPath.Length; - if (len < bestLen) - continue; - - if (path.StartsWith (prefPath) || pathSlash.StartsWith (prefPath)) { - bestLen = len; - listener = _prefixes[pref]; - } + /// + /// The onAccept + /// + /// The asyncResult + private static void onAccept(IAsyncResult asyncResult) + { + var lsnr = (EndPointListener)asyncResult.AsyncState; + + Socket sock = null; + try + { + sock = lsnr._socket.EndAccept(asyncResult); + } + catch (SocketException) + { + // TODO: Should log the error code when this class has a logging. + } + catch (ObjectDisposedException) + { + return; + } + + try + { + lsnr._socket.BeginAccept(onAccept, lsnr); + } + catch + { + if (sock != null) + sock.Close(); + + return; + } + + if (sock == null) + return; + + processAccepted(sock, lsnr); } - if (bestLen != -1) - return true; - } - - var prefs = _unhandled; - listener = searchHttpListenerFromSpecial (path, prefs); - if (listener == null && pathSlash != path) - listener = searchHttpListenerFromSpecial (pathSlash, prefs); - - if (listener != null) - return true; - - prefs = _all; - listener = searchHttpListenerFromSpecial (path, prefs); - if (listener == null && pathSlash != path) - listener = searchHttpListenerFromSpecial (pathSlash, prefs); - - return listener != null; - } - - #endregion - - #region Public Methods - - public void AddPrefix (HttpListenerPrefix prefix, HttpListener listener) - { - List current, future; - if (prefix.Host == "*") { - do { - current = _unhandled; - future = current != null - ? new List (current) - : new List (); - - prefix.Listener = listener; - addSpecial (future, prefix); + /// + /// The processAccepted + /// + /// The socket + /// The listener + private static void processAccepted(Socket socket, EndPointListener listener) + { + HttpConnection conn = null; + try + { + conn = new HttpConnection(socket, listener); + lock (listener._unregisteredSync) + listener._unregistered[conn] = conn; + + conn.BeginReadRequest(); + } + catch + { + if (conn != null) + { + conn.Close(true); + return; + } + + socket.Close(); + } } - while (Interlocked.CompareExchange (ref _unhandled, future, current) != current); - - return; - } - if (prefix.Host == "+") { - do { - current = _all; - future = current != null - ? new List (current) - : new List (); - - prefix.Listener = listener; - addSpecial (future, prefix); - } - while (Interlocked.CompareExchange (ref _all, future, current) != current); - - return; - } - - Dictionary prefs, prefs2; - do { - prefs = _prefixes; - if (prefs.ContainsKey (prefix)) { - if (prefs[prefix] != listener) { - throw new HttpListenerException ( - 87, String.Format ("There's another listener for {0}.", prefix) - ); - } - - return; + /// + /// The removeSpecial + /// + /// The prefixes + /// The prefix + /// The + private static bool removeSpecial(List prefixes, HttpListenerPrefix prefix) + { + var path = prefix.Path; + var cnt = prefixes.Count; + for (var i = 0; i < cnt; i++) + { + if (prefixes[i].Path == path) + { + prefixes.RemoveAt(i); + return true; + } + } + + return false; } - prefs2 = new Dictionary (prefs); - prefs2[prefix] = listener; - } - while (Interlocked.CompareExchange (ref _prefixes, prefs2, prefs) != prefs); - } + /// + /// The searchHttpListenerFromSpecial + /// + /// The path + /// The prefixes + /// The + private static HttpListener searchHttpListenerFromSpecial( + string path, List prefixes + ) + { + if (prefixes == null) + return null; - public void Close () - { - _socket.Close (); + HttpListener bestMatch = null; - HttpConnection[] conns = null; - lock (_unregisteredSync) { - if (_unregistered.Count == 0) - return; + var bestLen = -1; + foreach (var pref in prefixes) + { + var prefPath = pref.Path; - var keys = _unregistered.Keys; - conns = new HttpConnection[keys.Count]; - keys.CopyTo (conns, 0); - _unregistered.Clear (); - } + var len = prefPath.Length; + if (len < bestLen) + continue; - for (var i = conns.Length - 1; i >= 0; i--) - conns[i].Close (true); - } + if (path.StartsWith(prefPath)) + { + bestLen = len; + bestMatch = pref.Listener; + } + } - public void RemovePrefix (HttpListenerPrefix prefix, HttpListener listener) - { - List current, future; - if (prefix.Host == "*") { - do { - current = _unhandled; - if (current == null) - break; - - future = new List (current); - if (!removeSpecial (future, prefix)) - break; // The prefix wasn't found. + return bestMatch; } - while (Interlocked.CompareExchange (ref _unhandled, future, current) != current); - leaveIfNoPrefix (); - return; - } + /// + /// The leaveIfNoPrefix + /// + private void leaveIfNoPrefix() + { + if (_prefixes.Count > 0) + return; - if (prefix.Host == "+") { - do { - current = _all; - if (current == null) - break; + var prefs = _unhandled; + if (prefs != null && prefs.Count > 0) + return; - future = new List (current); - if (!removeSpecial (future, prefix)) - break; // The prefix wasn't found. - } - while (Interlocked.CompareExchange (ref _all, future, current) != current); - - leaveIfNoPrefix (); - return; - } - - Dictionary prefs, prefs2; - do { - prefs = _prefixes; - if (!prefs.ContainsKey (prefix)) - break; + prefs = _all; + if (prefs != null && prefs.Count > 0) + return; - prefs2 = new Dictionary (prefs); - prefs2.Remove (prefix); - } - while (Interlocked.CompareExchange (ref _prefixes, prefs2, prefs) != prefs); + EndPointManager.RemoveEndPoint(_endpoint); + } - leaveIfNoPrefix (); + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/EndPointManager.cs b/src/WebSocket/WebSocketCore/Net/EndPointManager.cs index c59a18983..abfadaafd 100644 --- a/src/WebSocket/WebSocketCore/Net/EndPointManager.cs +++ b/src/WebSocket/WebSocketCore/Net/EndPointManager.cs @@ -1,4 +1,3 @@ -#region License /* * EndPointManager.cs * @@ -28,21 +27,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Gonzalo Paniagua Javier */ -#endregion -#region Contributors /* * Contributors: * - Liryna */ -#endregion using System; using System.Collections; @@ -51,190 +45,236 @@ namespace WebSocketCore.Net { - internal sealed class EndPointManager - { - #region Private Fields - - private static readonly Dictionary _endpoints; - - #endregion - - #region Static Constructor - - static EndPointManager () - { - _endpoints = new Dictionary (); - } - - #endregion - - #region Private Constructors - - private EndPointManager () + /// + /// Defines the + /// + internal sealed class EndPointManager { - } + #region ֶ - #endregion + /// + /// Defines the _endpoints + /// + private static readonly Dictionary _endpoints; - #region Private Methods + #endregion ֶ - private static void addPrefix (string uriPrefix, HttpListener listener) - { - var pref = new HttpListenerPrefix (uriPrefix); - - var addr = convertToIPAddress (pref.Host); - if (addr == null) - throw new HttpListenerException (87, "Includes an invalid host."); - - if (!addr.IsLocal ()) - throw new HttpListenerException (87, "Includes an invalid host."); - - int port; - if (!Int32.TryParse (pref.Port, out port)) - throw new HttpListenerException (87, "Includes an invalid port."); - - if (!port.IsPortNumber ()) - throw new HttpListenerException (87, "Includes an invalid port."); - - var path = pref.Path; - if (path.IndexOf ('%') != -1) - throw new HttpListenerException (87, "Includes an invalid path."); - - if (path.IndexOf ("//", StringComparison.Ordinal) != -1) - throw new HttpListenerException (87, "Includes an invalid path."); - - var endpoint = new IPEndPoint (addr, port); - - EndPointListener lsnr; - if (_endpoints.TryGetValue (endpoint, out lsnr)) { - if (lsnr.IsSecure ^ pref.IsSecure) - throw new HttpListenerException (87, "Includes an invalid scheme."); - } - else { - lsnr = - new EndPointListener ( - endpoint, - pref.IsSecure, - listener.CertificateFolderPath, - listener.SslConfiguration, - listener.ReuseAddress - ); - - _endpoints.Add (endpoint, lsnr); - } - - lsnr.AddPrefix (pref, listener); - } + #region 캯 - private static IPAddress convertToIPAddress (string hostname) - { - if (hostname == "*") - return IPAddress.Any; - - if (hostname == "+") - return IPAddress.Any; + /// + /// Prevents a default instance of the class from being created. + /// + private EndPointManager() + { + } - return hostname.ToIPAddress (); - } + /// + /// Initializes static members of the class. + /// + static EndPointManager() + { + _endpoints = new Dictionary(); + } - private static void removePrefix (string uriPrefix, HttpListener listener) - { - var pref = new HttpListenerPrefix (uriPrefix); + #endregion 캯 + + #region + + /// + /// The AddListener + /// + /// The listener + public static void AddListener(HttpListener listener) + { + var added = new List(); + lock (((ICollection)_endpoints).SyncRoot) + { + try + { + foreach (var pref in listener.Prefixes) + { + addPrefix(pref, listener); + added.Add(pref); + } + } + catch + { + foreach (var pref in added) + removePrefix(pref, listener); + + throw; + } + } + } - var addr = convertToIPAddress (pref.Host); - if (addr == null) - return; + /// + /// The AddPrefix + /// + /// The uriPrefix + /// The listener + public static void AddPrefix(string uriPrefix, HttpListener listener) + { + lock (((ICollection)_endpoints).SyncRoot) + addPrefix(uriPrefix, listener); + } - if (!addr.IsLocal ()) - return; + /// + /// The RemoveListener + /// + /// The listener + public static void RemoveListener(HttpListener listener) + { + lock (((ICollection)_endpoints).SyncRoot) + { + foreach (var pref in listener.Prefixes) + removePrefix(pref, listener); + } + } - int port; - if (!Int32.TryParse (pref.Port, out port)) - return; + /// + /// The RemovePrefix + /// + /// The uriPrefix + /// The listener + public static void RemovePrefix(string uriPrefix, HttpListener listener) + { + lock (((ICollection)_endpoints).SyncRoot) + removePrefix(uriPrefix, listener); + } - if (!port.IsPortNumber ()) - return; + /// + /// The RemoveEndPoint + /// + /// The endpoint + /// The + internal static bool RemoveEndPoint(IPEndPoint endpoint) + { + lock (((ICollection)_endpoints).SyncRoot) + { + EndPointListener lsnr; + if (!_endpoints.TryGetValue(endpoint, out lsnr)) + return false; + + _endpoints.Remove(endpoint); + lsnr.Close(); + + return true; + } + } - var path = pref.Path; - if (path.IndexOf ('%') != -1) - return; + /// + /// The addPrefix + /// + /// The uriPrefix + /// The listener + private static void addPrefix(string uriPrefix, HttpListener listener) + { + var pref = new HttpListenerPrefix(uriPrefix); + + var addr = convertToIPAddress(pref.Host); + if (addr == null) + throw new HttpListenerException(87, "Includes an invalid host."); + + if (!addr.IsLocal()) + throw new HttpListenerException(87, "Includes an invalid host."); + + int port; + if (!Int32.TryParse(pref.Port, out port)) + throw new HttpListenerException(87, "Includes an invalid port."); + + if (!port.IsPortNumber()) + throw new HttpListenerException(87, "Includes an invalid port."); + + var path = pref.Path; + if (path.IndexOf('%') != -1) + throw new HttpListenerException(87, "Includes an invalid path."); + + if (path.IndexOf("//", StringComparison.Ordinal) != -1) + throw new HttpListenerException(87, "Includes an invalid path."); + + var endpoint = new IPEndPoint(addr, port); + + EndPointListener lsnr; + if (_endpoints.TryGetValue(endpoint, out lsnr)) + { + if (lsnr.IsSecure ^ pref.IsSecure) + throw new HttpListenerException(87, "Includes an invalid scheme."); + } + else + { + lsnr = + new EndPointListener( + endpoint, + pref.IsSecure, + listener.CertificateFolderPath, + listener.SslConfiguration, + listener.ReuseAddress + ); + + _endpoints.Add(endpoint, lsnr); + } + + lsnr.AddPrefix(pref, listener); + } - if (path.IndexOf ("//", StringComparison.Ordinal) != -1) - return; + /// + /// The convertToIPAddress + /// + /// The hostname + /// The + private static IPAddress convertToIPAddress(string hostname) + { + if (hostname == "*") + return IPAddress.Any; - var endpoint = new IPEndPoint (addr, port); + if (hostname == "+") + return IPAddress.Any; - EndPointListener lsnr; - if (!_endpoints.TryGetValue (endpoint, out lsnr)) - return; + return hostname.ToIPAddress(); + } - if (lsnr.IsSecure ^ pref.IsSecure) - return; + /// + /// The removePrefix + /// + /// The uriPrefix + /// The listener + private static void removePrefix(string uriPrefix, HttpListener listener) + { + var pref = new HttpListenerPrefix(uriPrefix); - lsnr.RemovePrefix (pref, listener); - } + var addr = convertToIPAddress(pref.Host); + if (addr == null) + return; - #endregion + if (!addr.IsLocal()) + return; - #region Internal Methods + int port; + if (!Int32.TryParse(pref.Port, out port)) + return; - internal static bool RemoveEndPoint (IPEndPoint endpoint) - { - lock (((ICollection) _endpoints).SyncRoot) { - EndPointListener lsnr; - if (!_endpoints.TryGetValue (endpoint, out lsnr)) - return false; + if (!port.IsPortNumber()) + return; - _endpoints.Remove (endpoint); - lsnr.Close (); + var path = pref.Path; + if (path.IndexOf('%') != -1) + return; - return true; - } - } + if (path.IndexOf("//", StringComparison.Ordinal) != -1) + return; - #endregion + var endpoint = new IPEndPoint(addr, port); - #region Public Methods + EndPointListener lsnr; + if (!_endpoints.TryGetValue(endpoint, out lsnr)) + return; - public static void AddListener (HttpListener listener) - { - var added = new List (); - lock (((ICollection) _endpoints).SyncRoot) { - try { - foreach (var pref in listener.Prefixes) { - addPrefix (pref, listener); - added.Add (pref); - } - } - catch { - foreach (var pref in added) - removePrefix (pref, listener); + if (lsnr.IsSecure ^ pref.IsSecure) + return; - throw; + lsnr.RemovePrefix(pref, listener); } - } - } - public static void AddPrefix (string uriPrefix, HttpListener listener) - { - lock (((ICollection) _endpoints).SyncRoot) - addPrefix (uriPrefix, listener); - } - - public static void RemoveListener (HttpListener listener) - { - lock (((ICollection) _endpoints).SyncRoot) { - foreach (var pref in listener.Prefixes) - removePrefix (pref, listener); - } + #endregion } - - public static void RemovePrefix (string uriPrefix, HttpListener listener) - { - lock (((ICollection) _endpoints).SyncRoot) - removePrefix (uriPrefix, listener); - } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/HttpBasicIdentity.cs b/src/WebSocket/WebSocketCore/Net/HttpBasicIdentity.cs index d54567d29..8212b290a 100644 --- a/src/WebSocket/WebSocketCore/Net/HttpBasicIdentity.cs +++ b/src/WebSocket/WebSocketCore/Net/HttpBasicIdentity.cs @@ -1,4 +1,3 @@ -#region License /* * HttpBasicIdentity.cs * @@ -28,55 +27,59 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Gonzalo Paniagua Javier */ -#endregion using System; using System.Security.Principal; namespace WebSocketCore.Net { - /// - /// Holds the username and password from an HTTP Basic authentication attempt. - /// - public class HttpBasicIdentity : GenericIdentity - { - #region Private Fields + /// + /// Holds the username and password from an HTTP Basic authentication attempt. + /// + public class HttpBasicIdentity : GenericIdentity + { + #region ֶ - private string _password; + /// + /// Defines the _password + /// + private string _password; - #endregion + #endregion ֶ - #region Internal Constructors + #region 캯 - internal HttpBasicIdentity (string username, string password) - : base (username, "Basic") - { - _password = password; - } + /// + /// Initializes a new instance of the class. + /// + /// The username + /// The password + internal HttpBasicIdentity(string username, string password) + : base(username, "Basic") + { + _password = password; + } - #endregion + #endregion 캯 - #region Public Properties + #region - /// - /// Gets the password from a basic authentication attempt. - /// - /// - /// A that represents the password. - /// - public virtual string Password { - get { - return _password; - } - } + /// + /// Gets the password from a basic authentication attempt. + /// + public virtual string Password + { + get + { + return _password; + } + } - #endregion - } -} + #endregion + } +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/HttpConnection.cs b/src/WebSocket/WebSocketCore/Net/HttpConnection.cs index 334900ed1..1a0dd60f2 100644 --- a/src/WebSocket/WebSocketCore/Net/HttpConnection.cs +++ b/src/WebSocket/WebSocketCore/Net/HttpConnection.cs @@ -1,4 +1,3 @@ -#region License /* * HttpConnection.cs * @@ -28,22 +27,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Gonzalo Paniagua Javier */ -#endregion -#region Contributors /* * Contributors: * - Liryna * - Rohan Singh */ -#endregion using System; using System.Collections.Generic; @@ -56,542 +50,784 @@ namespace WebSocketCore.Net { - internal sealed class HttpConnection - { - #region Private Fields - - private byte[] _buffer; - private const int _bufferLength = 8192; - private HttpListenerContext _context; - private bool _contextRegistered; - private StringBuilder _currentLine; - private InputState _inputState; - private RequestStream _inputStream; - private HttpListener _lastListener; - private LineState _lineState; - private EndPointListener _listener; - private EndPoint _localEndPoint; - private ResponseStream _outputStream; - private int _position; - private EndPoint _remoteEndPoint; - private MemoryStream _requestBuffer; - private int _reuses; - private bool _secure; - private Socket _socket; - private Stream _stream; - private object _sync; - private int _timeout; - private Dictionary _timeoutCanceled; - private Timer _timer; - - #endregion - - #region Internal Constructors - - internal HttpConnection (Socket socket, EndPointListener listener) + /// + /// Defines the + /// + internal sealed class HttpConnection { - _socket = socket; - _listener = listener; - - var netStream = new NetworkStream (socket, false); - if (listener.IsSecure) { - var sslConf = listener.SslConfiguration; - var sslStream = new SslStream ( - netStream, - false, - sslConf.ClientCertificateValidationCallback - ); - - sslStream.AuthenticateAsServer ( - sslConf.ServerCertificate, - sslConf.ClientCertificateRequired, - sslConf.EnabledSslProtocols, - sslConf.CheckCertificateRevocation - ); - - _secure = true; - _stream = sslStream; - } - else { - _stream = netStream; - } - - _localEndPoint = socket.LocalEndPoint; - _remoteEndPoint = socket.RemoteEndPoint; - _sync = new object (); - _timeout = 90000; // 90k ms for first request, 15k ms from then on. - _timeoutCanceled = new Dictionary (); - _timer = new Timer (onTimeout, this, Timeout.Infinite, Timeout.Infinite); - - init (); - } - - #endregion - - #region Public Properties - - public bool IsClosed { - get { - return _socket == null; - } - } - - public bool IsLocal { - get { - return ((IPEndPoint) _remoteEndPoint).Address.IsLocal (); - } - } - - public bool IsSecure { - get { - return _secure; - } - } - - public IPEndPoint LocalEndPoint { - get { - return (IPEndPoint) _localEndPoint; - } - } - - public IPEndPoint RemoteEndPoint { - get { - return (IPEndPoint) _remoteEndPoint; - } - } + #region + + /// + /// Defines the _bufferLength + /// + private const int _bufferLength = 8192; + + #endregion + + #region ֶ + + /// + /// Defines the _buffer + /// + private byte[] _buffer; + + /// + /// Defines the _context + /// + private HttpListenerContext _context; + + /// + /// Defines the _contextRegistered + /// + private bool _contextRegistered; + + /// + /// Defines the _currentLine + /// + private StringBuilder _currentLine; + + /// + /// Defines the _inputState + /// + private InputState _inputState; + + /// + /// Defines the _inputStream + /// + private RequestStream _inputStream; + + /// + /// Defines the _lastListener + /// + private HttpListener _lastListener; + + /// + /// Defines the _lineState + /// + private LineState _lineState; + + /// + /// Defines the _listener + /// + private EndPointListener _listener; + + /// + /// Defines the _localEndPoint + /// + private EndPoint _localEndPoint; + + /// + /// Defines the _outputStream + /// + private ResponseStream _outputStream; + + /// + /// Defines the _position + /// + private int _position; + + /// + /// Defines the _remoteEndPoint + /// + private EndPoint _remoteEndPoint; + + /// + /// Defines the _requestBuffer + /// + private MemoryStream _requestBuffer; + + /// + /// Defines the _reuses + /// + private int _reuses; + + /// + /// Defines the _secure + /// + private bool _secure; + + /// + /// Defines the _socket + /// + private Socket _socket; + + /// + /// Defines the _stream + /// + private Stream _stream; + + /// + /// Defines the _sync + /// + private object _sync; + + /// + /// Defines the _timeout + /// + private int _timeout; + + /// + /// Defines the _timeoutCanceled + /// + private Dictionary _timeoutCanceled; + + /// + /// Defines the _timer + /// + private Timer _timer; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The socket + /// The listener + internal HttpConnection(Socket socket, EndPointListener listener) + { + _socket = socket; + _listener = listener; + + var netStream = new NetworkStream(socket, false); + if (listener.IsSecure) + { + var sslConf = listener.SslConfiguration; + var sslStream = new SslStream( + netStream, + false, + sslConf.ClientCertificateValidationCallback + ); + + sslStream.AuthenticateAsServer( + sslConf.ServerCertificate, + sslConf.ClientCertificateRequired, + sslConf.EnabledSslProtocols, + sslConf.CheckCertificateRevocation + ); + + _secure = true; + _stream = sslStream; + } + else + { + _stream = netStream; + } - public int Reuses { - get { - return _reuses; - } - } + _localEndPoint = socket.LocalEndPoint; + _remoteEndPoint = socket.RemoteEndPoint; + _sync = new object(); + _timeout = 90000; // 90k ms for first request, 15k ms from then on. + _timeoutCanceled = new Dictionary(); + _timer = new Timer(onTimeout, this, Timeout.Infinite, Timeout.Infinite); - public Stream Stream { - get { - return _stream; - } - } + init(); + } - #endregion + #endregion 캯 - #region Private Methods + #region - private void close () - { - lock (_sync) { - if (_socket == null) - return; - - disposeTimer (); - disposeRequestBuffer (); - disposeStream (); - closeSocket (); - } - - unregisterContext (); - removeConnection (); - } + /// + /// Gets a value indicating whether IsClosed + /// + public bool IsClosed + { + get + { + return _socket == null; + } + } - private void closeSocket () - { - try { - _socket.Shutdown (SocketShutdown.Both); - } - catch { - } - - _socket.Close (); - _socket = null; - } + /// + /// Gets a value indicating whether IsLocal + /// + public bool IsLocal + { + get + { + return ((IPEndPoint)_remoteEndPoint).Address.IsLocal(); + } + } - private void disposeRequestBuffer () - { - if (_requestBuffer == null) - return; + /// + /// Gets a value indicating whether IsSecure + /// + public bool IsSecure + { + get + { + return _secure; + } + } - _requestBuffer.Dispose (); - _requestBuffer = null; - } + /// + /// Gets the LocalEndPoint + /// + public IPEndPoint LocalEndPoint + { + get + { + return (IPEndPoint)_localEndPoint; + } + } - private void disposeStream () - { - if (_stream == null) - return; + /// + /// Gets the RemoteEndPoint + /// + public IPEndPoint RemoteEndPoint + { + get + { + return (IPEndPoint)_remoteEndPoint; + } + } - _inputStream = null; - _outputStream = null; + /// + /// Gets the Reuses + /// + public int Reuses + { + get + { + return _reuses; + } + } - _stream.Dispose (); - _stream = null; - } + /// + /// Gets the Stream + /// + public Stream Stream + { + get + { + return _stream; + } + } - private void disposeTimer () - { - if (_timer == null) - return; + #endregion - try { - _timer.Change (Timeout.Infinite, Timeout.Infinite); - } - catch { - } + #region - _timer.Dispose (); - _timer = null; - } + /// + /// The BeginReadRequest + /// + public void BeginReadRequest() + { + if (_buffer == null) + _buffer = new byte[_bufferLength]; - private void init () - { - _context = new HttpListenerContext (this); - _inputState = InputState.RequestLine; - _inputStream = null; - _lineState = LineState.None; - _outputStream = null; - _position = 0; - _requestBuffer = new MemoryStream (); - } + if (_reuses == 1) + _timeout = 15000; - private static void onRead (IAsyncResult asyncResult) - { - var conn = (HttpConnection) asyncResult.AsyncState; - if (conn._socket == null) - return; - - lock (conn._sync) { - if (conn._socket == null) - return; - - var nread = -1; - var len = 0; - try { - var current = conn._reuses; - if (!conn._timeoutCanceled[current]) { - conn._timer.Change (Timeout.Infinite, Timeout.Infinite); - conn._timeoutCanceled[current] = true; - } - - nread = conn._stream.EndRead (asyncResult); - conn._requestBuffer.Write (conn._buffer, 0, nread); - len = (int) conn._requestBuffer.Length; - } - catch (Exception ex) { - if (conn._requestBuffer != null && conn._requestBuffer.Length > 0) { - conn.SendError (ex.Message, 400); - return; - } - - conn.close (); - return; + try + { + _timeoutCanceled.Add(_reuses, false); + _timer.Change(_timeout, Timeout.Infinite); + _stream.BeginRead(_buffer, 0, _bufferLength, onRead, this); + } + catch + { + close(); + } } - if (nread <= 0) { - conn.close (); - return; + /// + /// The Close + /// + public void Close() + { + Close(false); } - if (conn.processInput (conn._requestBuffer.GetBuffer (), len)) { - if (!conn._context.HasError) - conn._context.Request.FinishInitialization (); - - if (conn._context.HasError) { - conn.SendError (); - return; - } - - HttpListener lsnr; - if (!conn._listener.TrySearchHttpListener (conn._context.Request.Url, out lsnr)) { - conn.SendError (null, 404); - return; - } - - if (conn._lastListener != lsnr) { - conn.removeConnection (); - if (!lsnr.AddConnection (conn)) { - conn.close (); - return; + /// + /// The GetRequestStream + /// + /// The contentLength + /// The chunked + /// The + public RequestStream GetRequestStream(long contentLength, bool chunked) + { + lock (_sync) + { + if (_socket == null) + return null; + + if (_inputStream != null) + return _inputStream; + + var buff = _requestBuffer.GetBuffer(); + var len = (int)_requestBuffer.Length; + var cnt = len - _position; + disposeRequestBuffer(); + + _inputStream = chunked + ? new ChunkedRequestStream( + _stream, buff, _position, cnt, _context + ) + : new RequestStream( + _stream, buff, _position, cnt, contentLength + ); + + return _inputStream; } - - conn._lastListener = lsnr; - } - - conn._context.Listener = lsnr; - if (!conn._context.Authenticate ()) - return; - - if (conn._context.Register ()) - conn._contextRegistered = true; - - return; } - conn._stream.BeginRead (conn._buffer, 0, _bufferLength, onRead, conn); - } - } - - private static void onTimeout (object state) - { - var conn = (HttpConnection) state; - var current = conn._reuses; - if (conn._socket == null) - return; + /// + /// The GetResponseStream + /// + /// The + public ResponseStream GetResponseStream() + { + // TODO: Can we get this stream before reading the input? - lock (conn._sync) { - if (conn._socket == null) - return; + lock (_sync) + { + if (_socket == null) + return null; - if (conn._timeoutCanceled[current]) - return; + if (_outputStream != null) + return _outputStream; - conn.SendError (null, 408); - } - } + var lsnr = _context.Listener; + var ignore = lsnr != null ? lsnr.IgnoreWriteExceptions : true; + _outputStream = new ResponseStream(_stream, _context.Response, ignore); - // true -> Done processing. - // false -> Need more input. - private bool processInput (byte[] data, int length) - { - if (_currentLine == null) - _currentLine = new StringBuilder (64); - - var nread = 0; - try { - string line; - while ((line = readLineFrom (data, _position, length, out nread)) != null) { - _position += nread; - if (line.Length == 0) { - if (_inputState == InputState.RequestLine) - continue; - - if (_position > 32768) - _context.ErrorMessage = "Headers too long"; - - _currentLine = null; - return true; - } - - if (_inputState == InputState.RequestLine) { - _context.Request.SetRequestLine (line); - _inputState = InputState.Headers; - } - else { - _context.Request.AddHeader (line); - } - - if (_context.HasError) - return true; + return _outputStream; + } } - } - catch (Exception ex) { - _context.ErrorMessage = ex.Message; - return true; - } - - _position += nread; - if (_position >= 32768) { - _context.ErrorMessage = "Headers too long"; - return true; - } - - return false; - } - - private string readLineFrom (byte[] buffer, int offset, int length, out int read) - { - read = 0; - - for (var i = offset; i < length && _lineState != LineState.Lf; i++) { - read++; - var b = buffer[i]; - if (b == 13) - _lineState = LineState.Cr; - else if (b == 10) - _lineState = LineState.Lf; - else - _currentLine.Append ((char) b); - } + /// + /// The SendError + /// + public void SendError() + { + SendError(_context.ErrorMessage, _context.ErrorStatus); + } - if (_lineState != LineState.Lf) - return null; + /// + /// The SendError + /// + /// The message + /// The status + public void SendError(string message, int status) + { + if (_socket == null) + return; + + lock (_sync) + { + if (_socket == null) + return; + + try + { + var res = _context.Response; + res.StatusCode = status; + res.ContentType = "text/html"; + + var content = new StringBuilder(64); + content.AppendFormat("

{0} {1}", status, res.StatusDescription); + if (message != null && message.Length > 0) + content.AppendFormat(" ({0})

", message); + else + content.Append(""); + + var enc = Encoding.UTF8; + var entity = enc.GetBytes(content.ToString()); + res.ContentEncoding = enc; + res.ContentLength64 = entity.LongLength; + + res.Close(entity, true); + } + catch + { + Close(true); + } + } + } - var line = _currentLine.ToString (); + /// + /// The Close + /// + /// The force + internal void Close(bool force) + { + if (_socket == null) + return; + + lock (_sync) + { + if (_socket == null) + return; + + if (force) + { + if (_outputStream != null) + _outputStream.Close(true); + + close(); + return; + } + + GetResponseStream().Close(false); + + if (_context.Response.CloseConnection) + { + close(); + return; + } + + if (!_context.Request.FlushInput()) + { + close(); + return; + } + + disposeRequestBuffer(); + unregisterContext(); + init(); + + _reuses++; + BeginReadRequest(); + } + } - _currentLine.Length = 0; - _lineState = LineState.None; + /// + /// The onRead + /// + /// The asyncResult + private static void onRead(IAsyncResult asyncResult) + { + var conn = (HttpConnection)asyncResult.AsyncState; + if (conn._socket == null) + return; + + lock (conn._sync) + { + if (conn._socket == null) + return; + + var nread = -1; + var len = 0; + try + { + var current = conn._reuses; + if (!conn._timeoutCanceled[current]) + { + conn._timer.Change(Timeout.Infinite, Timeout.Infinite); + conn._timeoutCanceled[current] = true; + } + + nread = conn._stream.EndRead(asyncResult); + conn._requestBuffer.Write(conn._buffer, 0, nread); + len = (int)conn._requestBuffer.Length; + } + catch (Exception ex) + { + if (conn._requestBuffer != null && conn._requestBuffer.Length > 0) + { + conn.SendError(ex.Message, 400); + return; + } + + conn.close(); + return; + } + + if (nread <= 0) + { + conn.close(); + return; + } + + if (conn.processInput(conn._requestBuffer.GetBuffer(), len)) + { + if (!conn._context.HasError) + conn._context.Request.FinishInitialization(); + + if (conn._context.HasError) + { + conn.SendError(); + return; + } + + HttpListener lsnr; + if (!conn._listener.TrySearchHttpListener(conn._context.Request.Url, out lsnr)) + { + conn.SendError(null, 404); + return; + } + + if (conn._lastListener != lsnr) + { + conn.removeConnection(); + if (!lsnr.AddConnection(conn)) + { + conn.close(); + return; + } + + conn._lastListener = lsnr; + } + + conn._context.Listener = lsnr; + if (!conn._context.Authenticate()) + return; + + if (conn._context.Register()) + conn._contextRegistered = true; + + return; + } + + conn._stream.BeginRead(conn._buffer, 0, _bufferLength, onRead, conn); + } + } - return line; - } + /// + /// The onTimeout + /// + /// The state + private static void onTimeout(object state) + { + var conn = (HttpConnection)state; + var current = conn._reuses; + if (conn._socket == null) + return; + + lock (conn._sync) + { + if (conn._socket == null) + return; + + if (conn._timeoutCanceled[current]) + return; + + conn.SendError(null, 408); + } + } - private void removeConnection () - { - if (_lastListener != null) - _lastListener.RemoveConnection (this); - else - _listener.RemoveConnection (this); - } + /// + /// The close + /// + private void close() + { + lock (_sync) + { + if (_socket == null) + return; + + disposeTimer(); + disposeRequestBuffer(); + disposeStream(); + closeSocket(); + } - private void unregisterContext () - { - if (!_contextRegistered) - return; + unregisterContext(); + removeConnection(); + } - _context.Unregister (); - _contextRegistered = false; - } + /// + /// The closeSocket + /// + private void closeSocket() + { + try + { + _socket.Shutdown(SocketShutdown.Both); + } + catch + { + } - #endregion + _socket.Close(); + _socket = null; + } - #region Internal Methods + /// + /// The disposeRequestBuffer + /// + private void disposeRequestBuffer() + { + if (_requestBuffer == null) + return; - internal void Close (bool force) - { - if (_socket == null) - return; + _requestBuffer.Dispose(); + _requestBuffer = null; + } - lock (_sync) { - if (_socket == null) - return; + /// + /// The disposeStream + /// + private void disposeStream() + { + if (_stream == null) + return; - if (force) { - if (_outputStream != null) - _outputStream.Close (true); + _inputStream = null; + _outputStream = null; - close (); - return; + _stream.Dispose(); + _stream = null; } - GetResponseStream ().Close (false); + /// + /// The disposeTimer + /// + private void disposeTimer() + { + if (_timer == null) + return; + + try + { + _timer.Change(Timeout.Infinite, Timeout.Infinite); + } + catch + { + } - if (_context.Response.CloseConnection) { - close (); - return; + _timer.Dispose(); + _timer = null; } - if (!_context.Request.FlushInput ()) { - close (); - return; + /// + /// The init + /// + private void init() + { + _context = new HttpListenerContext(this); + _inputState = InputState.RequestLine; + _inputStream = null; + _lineState = LineState.None; + _outputStream = null; + _position = 0; + _requestBuffer = new MemoryStream(); } - disposeRequestBuffer (); - unregisterContext (); - init (); - - _reuses++; - BeginReadRequest (); - } - } - - #endregion - - #region Public Methods + // true -> Done processing. + // false -> Need more input. + /// + /// The processInput + /// + /// The data + /// The length + /// The + private bool processInput(byte[] data, int length) + { + if (_currentLine == null) + _currentLine = new StringBuilder(64); + + var nread = 0; + try + { + string line; + while ((line = readLineFrom(data, _position, length, out nread)) != null) + { + _position += nread; + if (line.Length == 0) + { + if (_inputState == InputState.RequestLine) + continue; + + if (_position > 32768) + _context.ErrorMessage = "Headers too long"; + + _currentLine = null; + return true; + } + + if (_inputState == InputState.RequestLine) + { + _context.Request.SetRequestLine(line); + _inputState = InputState.Headers; + } + else + { + _context.Request.AddHeader(line); + } + + if (_context.HasError) + return true; + } + } + catch (Exception ex) + { + _context.ErrorMessage = ex.Message; + return true; + } - public void BeginReadRequest () - { - if (_buffer == null) - _buffer = new byte[_bufferLength]; - - if (_reuses == 1) - _timeout = 15000; - - try { - _timeoutCanceled.Add (_reuses, false); - _timer.Change (_timeout, Timeout.Infinite); - _stream.BeginRead (_buffer, 0, _bufferLength, onRead, this); - } - catch { - close (); - } - } + _position += nread; + if (_position >= 32768) + { + _context.ErrorMessage = "Headers too long"; + return true; + } - public void Close () - { - Close (false); - } + return false; + } - public RequestStream GetRequestStream (long contentLength, bool chunked) - { - lock (_sync) { - if (_socket == null) - return null; - - if (_inputStream != null) - return _inputStream; - - var buff = _requestBuffer.GetBuffer (); - var len = (int) _requestBuffer.Length; - var cnt = len - _position; - disposeRequestBuffer (); - - _inputStream = chunked - ? new ChunkedRequestStream ( - _stream, buff, _position, cnt, _context - ) - : new RequestStream ( - _stream, buff, _position, cnt, contentLength - ); - - return _inputStream; - } - } + /// + /// The readLineFrom + /// + /// The buffer + /// The offset + /// The length + /// The read + /// The + private string readLineFrom(byte[] buffer, int offset, int length, out int read) + { + read = 0; + + for (var i = offset; i < length && _lineState != LineState.Lf; i++) + { + read++; + + var b = buffer[i]; + if (b == 13) + _lineState = LineState.Cr; + else if (b == 10) + _lineState = LineState.Lf; + else + _currentLine.Append((char)b); + } - public ResponseStream GetResponseStream () - { - // TODO: Can we get this stream before reading the input? + if (_lineState != LineState.Lf) + return null; - lock (_sync) { - if (_socket == null) - return null; + var line = _currentLine.ToString(); - if (_outputStream != null) - return _outputStream; + _currentLine.Length = 0; + _lineState = LineState.None; - var lsnr = _context.Listener; - var ignore = lsnr != null ? lsnr.IgnoreWriteExceptions : true; - _outputStream = new ResponseStream (_stream, _context.Response, ignore); + return line; + } - return _outputStream; - } - } + /// + /// The removeConnection + /// + private void removeConnection() + { + if (_lastListener != null) + _lastListener.RemoveConnection(this); + else + _listener.RemoveConnection(this); + } - public void SendError () - { - SendError (_context.ErrorMessage, _context.ErrorStatus); - } + /// + /// The unregisterContext + /// + private void unregisterContext() + { + if (!_contextRegistered) + return; - public void SendError (string message, int status) - { - if (_socket == null) - return; - - lock (_sync) { - if (_socket == null) - return; - - try { - var res = _context.Response; - res.StatusCode = status; - res.ContentType = "text/html"; - - var content = new StringBuilder (64); - content.AppendFormat ("

{0} {1}", status, res.StatusDescription); - if (message != null && message.Length > 0) - content.AppendFormat (" ({0})

", message); - else - content.Append (""); - - var enc = Encoding.UTF8; - var entity = enc.GetBytes (content.ToString ()); - res.ContentEncoding = enc; - res.ContentLength64 = entity.LongLength; - - res.Close (entity, true); + _context.Unregister(); + _contextRegistered = false; } - catch { - Close (true); - } - } - } - #endregion - } -} + #endregion + } +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/HttpDigestIdentity.cs b/src/WebSocket/WebSocketCore/Net/HttpDigestIdentity.cs index 4938c63c0..1fc2f891a 100644 --- a/src/WebSocket/WebSocketCore/Net/HttpDigestIdentity.cs +++ b/src/WebSocket/WebSocketCore/Net/HttpDigestIdentity.cs @@ -1,4 +1,3 @@ -#region License /* * HttpDigestIdentity.cs * @@ -24,7 +23,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; using System.Collections.Specialized; @@ -32,156 +30,162 @@ namespace WebSocketCore.Net { - /// - /// Holds the username and other parameters from - /// an HTTP Digest authentication attempt. - /// - public class HttpDigestIdentity : GenericIdentity - { - #region Private Fields - - private NameValueCollection _parameters; - - #endregion - - #region Internal Constructors - - internal HttpDigestIdentity (NameValueCollection parameters) - : base (parameters["username"], "Digest") - { - _parameters = parameters; - } - - #endregion - - #region Public Properties - - /// - /// Gets the algorithm parameter from a digest authentication attempt. - /// - /// - /// A that represents the algorithm parameter. - /// - public string Algorithm { - get { - return _parameters["algorithm"]; - } - } - - /// - /// Gets the cnonce parameter from a digest authentication attempt. - /// - /// - /// A that represents the cnonce parameter. - /// - public string Cnonce { - get { - return _parameters["cnonce"]; - } - } - - /// - /// Gets the nc parameter from a digest authentication attempt. - /// - /// - /// A that represents the nc parameter. - /// - public string Nc { - get { - return _parameters["nc"]; - } - } - - /// - /// Gets the nonce parameter from a digest authentication attempt. - /// - /// - /// A that represents the nonce parameter. - /// - public string Nonce { - get { - return _parameters["nonce"]; - } - } - - /// - /// Gets the opaque parameter from a digest authentication attempt. - /// - /// - /// A that represents the opaque parameter. - /// - public string Opaque { - get { - return _parameters["opaque"]; - } - } - - /// - /// Gets the qop parameter from a digest authentication attempt. - /// - /// - /// A that represents the qop parameter. - /// - public string Qop { - get { - return _parameters["qop"]; - } - } - - /// - /// Gets the realm parameter from a digest authentication attempt. - /// - /// - /// A that represents the realm parameter. - /// - public string Realm { - get { - return _parameters["realm"]; - } - } - - /// - /// Gets the response parameter from a digest authentication attempt. - /// - /// - /// A that represents the response parameter. - /// - public string Response { - get { - return _parameters["response"]; - } - } - /// - /// Gets the uri parameter from a digest authentication attempt. + /// Holds the username and other parameters from + /// an HTTP Digest authentication attempt. /// - /// - /// A that represents the uri parameter. - /// - public string Uri { - get { - return _parameters["uri"]; - } - } - - #endregion - - #region Internal Methods - - internal bool IsValid ( + public class HttpDigestIdentity : GenericIdentity + { + #region ֶ + + /// + /// Defines the _parameters + /// + private NameValueCollection _parameters; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The parameters + internal HttpDigestIdentity(NameValueCollection parameters) + : base(parameters["username"], "Digest") + { + _parameters = parameters; + } + + #endregion 캯 + + #region + + /// + /// Gets the algorithm parameter from a digest authentication attempt. + /// + public string Algorithm + { + get + { + return _parameters["algorithm"]; + } + } + + /// + /// Gets the cnonce parameter from a digest authentication attempt. + /// + public string Cnonce + { + get + { + return _parameters["cnonce"]; + } + } + + /// + /// Gets the nc parameter from a digest authentication attempt. + /// + public string Nc + { + get + { + return _parameters["nc"]; + } + } + + /// + /// Gets the nonce parameter from a digest authentication attempt. + /// + public string Nonce + { + get + { + return _parameters["nonce"]; + } + } + + /// + /// Gets the opaque parameter from a digest authentication attempt. + /// + public string Opaque + { + get + { + return _parameters["opaque"]; + } + } + + /// + /// Gets the qop parameter from a digest authentication attempt. + /// + public string Qop + { + get + { + return _parameters["qop"]; + } + } + + /// + /// Gets the realm parameter from a digest authentication attempt. + /// + public string Realm + { + get + { + return _parameters["realm"]; + } + } + + /// + /// Gets the response parameter from a digest authentication attempt. + /// + public string Response + { + get + { + return _parameters["response"]; + } + } + + /// + /// Gets the uri parameter from a digest authentication attempt. + /// + public string Uri + { + get + { + return _parameters["uri"]; + } + } + + #endregion + + #region + + /// + /// The IsValid + /// + /// The password + /// The realm + /// The method + /// The entity + /// The + internal bool IsValid( string password, string realm, string method, string entity ) - { - var copied = new NameValueCollection (_parameters); - copied["password"] = password; - copied["realm"] = realm; - copied["method"] = method; - copied["entity"] = entity; - - var expected = AuthenticationResponse.CreateRequestDigest (copied); - return _parameters["response"] == expected; + { + var copied = new NameValueCollection(_parameters); + copied["password"] = password; + copied["realm"] = realm; + copied["method"] = method; + copied["entity"] = entity; + + var expected = AuthenticationResponse.CreateRequestDigest(copied); + return _parameters["response"] == expected; + } + + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/HttpHeaderInfo.cs b/src/WebSocket/WebSocketCore/Net/HttpHeaderInfo.cs index a0a145ec1..2034e3ccd 100644 --- a/src/WebSocket/WebSocketCore/Net/HttpHeaderInfo.cs +++ b/src/WebSocket/WebSocketCore/Net/HttpHeaderInfo.cs @@ -1,4 +1,3 @@ -#region License /* * HttpHeaderInfo.cs * @@ -24,91 +23,141 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; namespace WebSocketCore.Net { - internal class HttpHeaderInfo - { - #region Private Fields - - private string _name; - private HttpHeaderType _type; - - #endregion - - #region Internal Constructors - - internal HttpHeaderInfo (string name, HttpHeaderType type) + /// + /// Defines the + /// + internal class HttpHeaderInfo { - _name = name; - _type = type; - } - - #endregion - - #region Internal Properties - - internal bool IsMultiValueInRequest { - get { - return (_type & HttpHeaderType.MultiValueInRequest) == HttpHeaderType.MultiValueInRequest; - } + #region ֶ + + /// + /// Defines the _name + /// + private string _name; + + /// + /// Defines the _type + /// + private HttpHeaderType _type; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The name + /// The type + internal HttpHeaderInfo(string name, HttpHeaderType type) + { + _name = name; + _type = type; + } + + #endregion 캯 + + #region + + /// + /// Gets a value indicating whether IsRequest + /// + public bool IsRequest + { + get + { + return (_type & HttpHeaderType.Request) == HttpHeaderType.Request; + } + } + + /// + /// Gets a value indicating whether IsResponse + /// + public bool IsResponse + { + get + { + return (_type & HttpHeaderType.Response) == HttpHeaderType.Response; + } + } + + /// + /// Gets the Name + /// + public string Name + { + get + { + return _name; + } + } + + /// + /// Gets the Type + /// + public HttpHeaderType Type + { + get + { + return _type; + } + } + + /// + /// Gets a value indicating whether IsMultiValueInRequest + /// + internal bool IsMultiValueInRequest + { + get + { + return (_type & HttpHeaderType.MultiValueInRequest) == HttpHeaderType.MultiValueInRequest; + } + } + + /// + /// Gets a value indicating whether IsMultiValueInResponse + /// + internal bool IsMultiValueInResponse + { + get + { + return (_type & HttpHeaderType.MultiValueInResponse) == HttpHeaderType.MultiValueInResponse; + } + } + + #endregion + + #region + + /// + /// The IsMultiValue + /// + /// The response + /// The + public bool IsMultiValue(bool response) + { + return (_type & HttpHeaderType.MultiValue) == HttpHeaderType.MultiValue + ? (response ? IsResponse : IsRequest) + : (response ? IsMultiValueInResponse : IsMultiValueInRequest); + } + + /// + /// The IsRestricted + /// + /// The response + /// The + public bool IsRestricted(bool response) + { + return (_type & HttpHeaderType.Restricted) == HttpHeaderType.Restricted + ? (response ? IsResponse : IsRequest) + : false; + } + + #endregion } - - internal bool IsMultiValueInResponse { - get { - return (_type & HttpHeaderType.MultiValueInResponse) == HttpHeaderType.MultiValueInResponse; - } - } - - #endregion - - #region Public Properties - - public bool IsRequest { - get { - return (_type & HttpHeaderType.Request) == HttpHeaderType.Request; - } - } - - public bool IsResponse { - get { - return (_type & HttpHeaderType.Response) == HttpHeaderType.Response; - } - } - - public string Name { - get { - return _name; - } - } - - public HttpHeaderType Type { - get { - return _type; - } - } - - #endregion - - #region Public Methods - - public bool IsMultiValue (bool response) - { - return (_type & HttpHeaderType.MultiValue) == HttpHeaderType.MultiValue - ? (response ? IsResponse : IsRequest) - : (response ? IsMultiValueInResponse : IsMultiValueInRequest); - } - - public bool IsRestricted (bool response) - { - return (_type & HttpHeaderType.Restricted) == HttpHeaderType.Restricted - ? (response ? IsResponse : IsRequest) - : false; - } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/HttpHeaderType.cs b/src/WebSocket/WebSocketCore/Net/HttpHeaderType.cs index a6607bded..a12e9b350 100644 --- a/src/WebSocket/WebSocketCore/Net/HttpHeaderType.cs +++ b/src/WebSocket/WebSocketCore/Net/HttpHeaderType.cs @@ -1,4 +1,3 @@ -#region License /* * HttpHeaderType.cs * @@ -24,21 +23,54 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; namespace WebSocketCore.Net { - [Flags] - internal enum HttpHeaderType - { - Unspecified = 0, - Request = 1, - Response = 1 << 1, - Restricted = 1 << 2, - MultiValue = 1 << 3, - MultiValueInRequest = 1 << 4, - MultiValueInResponse = 1 << 5 - } -} + #region ö + + /// + /// Defines the HttpHeaderType + /// + [Flags] + internal enum HttpHeaderType + { + /// + /// Defines the Unspecified + /// + Unspecified = 0, + + /// + /// Defines the Request + /// + Request = 1, + + /// + /// Defines the Response + /// + Response = 1 << 1, + + /// + /// Defines the Restricted + /// + Restricted = 1 << 2, + + /// + /// Defines the MultiValue + /// + MultiValue = 1 << 3, + + /// + /// Defines the MultiValueInRequest + /// + MultiValueInRequest = 1 << 4, + + /// + /// Defines the MultiValueInResponse + /// + MultiValueInResponse = 1 << 5 + } + + #endregion ö +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/HttpListener.cs b/src/WebSocket/WebSocketCore/Net/HttpListener.cs index c8429d973..528626d11 100644 --- a/src/WebSocket/WebSocketCore/Net/HttpListener.cs +++ b/src/WebSocket/WebSocketCore/Net/HttpListener.cs @@ -1,4 +1,3 @@ -#region License /* * HttpListener.cs * @@ -28,21 +27,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Gonzalo Paniagua Javier */ -#endregion -#region Contributors /* * Contributors: * - Liryna */ -#endregion using System; using System.Collections; @@ -54,783 +48,791 @@ // TODO: Logging. namespace WebSocketCore.Net { - /// - /// Provides a simple, programmatically controlled HTTP listener. - /// - public sealed class HttpListener : IDisposable - { - #region Private Fields - - private AuthenticationSchemes _authSchemes; - private Func _authSchemeSelector; - private string _certFolderPath; - private Dictionary _connections; - private object _connectionsSync; - private List _ctxQueue; - private object _ctxQueueSync; - private Dictionary _ctxRegistry; - private object _ctxRegistrySync; - private static readonly string _defaultRealm; - private bool _disposed; - private bool _ignoreWriteExceptions; - private volatile bool _listening; - private Logger _logger; - private HttpListenerPrefixCollection _prefixes; - private string _realm; - private bool _reuseAddress; - private ServerSslConfiguration _sslConfig; - private Func _userCredFinder; - private List _waitQueue; - private object _waitQueueSync; - - #endregion - - #region Static Constructor - - static HttpListener () - { - _defaultRealm = "SECRET AREA"; - } - - #endregion - - #region Public Constructors - /// - /// Initializes a new instance of the class. + /// Provides a simple, programmatically controlled HTTP listener. /// - public HttpListener () + public sealed class HttpListener : IDisposable { - _authSchemes = AuthenticationSchemes.Anonymous; - - _connections = new Dictionary (); - _connectionsSync = ((ICollection) _connections).SyncRoot; - - _ctxQueue = new List (); - _ctxQueueSync = ((ICollection) _ctxQueue).SyncRoot; - - _ctxRegistry = new Dictionary (); - _ctxRegistrySync = ((ICollection) _ctxRegistry).SyncRoot; - - _logger = new Logger (); - - _prefixes = new HttpListenerPrefixCollection (this); - - _waitQueue = new List (); - _waitQueueSync = ((ICollection) _waitQueue).SyncRoot; - } - - #endregion - - #region Internal Properties - - internal bool IsDisposed { - get { - return _disposed; - } - } - - internal bool ReuseAddress { - get { - return _reuseAddress; - } - - set { - _reuseAddress = value; - } - } - - #endregion - - #region Public Properties - - /// - /// Gets or sets the scheme used to authenticate the clients. - /// - /// - /// One of the enum values, - /// represents the scheme used to authenticate the clients. The default value is - /// . - /// - /// - /// This listener has been closed. - /// - public AuthenticationSchemes AuthenticationSchemes { - get { - CheckDisposed (); - return _authSchemes; - } - - set { - CheckDisposed (); - _authSchemes = value; - } - } - - /// - /// Gets or sets the delegate called to select the scheme used to authenticate the clients. - /// - /// - /// If you set this property, the listener uses the authentication scheme selected by - /// the delegate for each request. Or if you don't set, the listener uses the value of - /// the property as the authentication - /// scheme for all requests. - /// - /// - /// A Func<, > - /// delegate that references the method used to select an authentication scheme. The default - /// value is . - /// - /// - /// This listener has been closed. - /// - public Func AuthenticationSchemeSelector { - get { - CheckDisposed (); - return _authSchemeSelector; - } - - set { - CheckDisposed (); - _authSchemeSelector = value; - } - } - - /// - /// Gets or sets the path to the folder in which stores the certificate files used to - /// authenticate the server on the secure connection. - /// - /// - /// - /// This property represents the path to the folder in which stores the certificate files - /// associated with each port number of added URI prefixes. A set of the certificate files - /// is a pair of the 'port number'.cer (DER) and 'port number'.key - /// (DER, RSA Private Key). - /// - /// - /// If this property is or empty, the result of - /// System.Environment.GetFolderPath - /// () is used as the default path. - /// - /// - /// - /// A that represents the path to the folder in which stores - /// the certificate files. The default value is . - /// - /// - /// This listener has been closed. - /// - public string CertificateFolderPath { - get { - CheckDisposed (); - return _certFolderPath; - } - - set { - CheckDisposed (); - _certFolderPath = value; - } - } - - /// - /// Gets or sets a value indicating whether the listener returns exceptions that occur when - /// sending the response to the client. - /// - /// - /// true if the listener shouldn't return those exceptions; otherwise, false. - /// The default value is false. - /// - /// - /// This listener has been closed. - /// - public bool IgnoreWriteExceptions { - get { - CheckDisposed (); - return _ignoreWriteExceptions; - } - - set { - CheckDisposed (); - _ignoreWriteExceptions = value; - } - } - - /// - /// Gets a value indicating whether the listener has been started. - /// - /// - /// true if the listener has been started; otherwise, false. - /// - public bool IsListening { - get { - return _listening; - } - } - - /// - /// Gets a value indicating whether the listener can be used with the current operating system. - /// - /// - /// true. - /// - public static bool IsSupported { - get { - return true; - } - } - - /// - /// Gets the logging functions. - /// - /// - /// The default logging level is . If you would like to change it, - /// you should set the Log.Level property to any of the enum - /// values. - /// - /// - /// A that provides the logging functions. - /// - public Logger Log { - get { - return _logger; - } - } - - /// - /// Gets the URI prefixes handled by the listener. - /// - /// - /// A that contains the URI prefixes. - /// - /// - /// This listener has been closed. - /// - public HttpListenerPrefixCollection Prefixes { - get { - CheckDisposed (); - return _prefixes; - } - } - - /// - /// Gets or sets the name of the realm associated with the listener. - /// - /// - /// If this property is or empty, "SECRET AREA" will be used as - /// the name of the realm. - /// - /// - /// A that represents the name of the realm. The default value is - /// . - /// - /// - /// This listener has been closed. - /// - public string Realm { - get { - CheckDisposed (); - return _realm; - } - - set { - CheckDisposed (); - _realm = value; - } - } - - /// - /// Gets or sets the SSL configuration used to authenticate the server and - /// optionally the client for secure connection. - /// - /// - /// A that represents the configuration used to - /// authenticate the server and optionally the client for secure connection. - /// - /// - /// This listener has been closed. - /// - public ServerSslConfiguration SslConfiguration { - get { - CheckDisposed (); - return _sslConfig ?? (_sslConfig = new ServerSslConfiguration ()); - } - - set { - CheckDisposed (); - _sslConfig = value; - } - } - - /// - /// Gets or sets a value indicating whether, when NTLM authentication is used, - /// the authentication information of first request is used to authenticate - /// additional requests on the same connection. - /// - /// - /// This property isn't currently supported and always throws - /// a . - /// - /// - /// true if the authentication information of first request is used; - /// otherwise, false. - /// - /// - /// Any use of this property. - /// - public bool UnsafeConnectionNtlmAuthentication { - get { - throw new NotSupportedException (); - } - - set { - throw new NotSupportedException (); - } - } - - /// - /// Gets or sets the delegate called to find the credentials for an identity used to - /// authenticate a client. - /// - /// - /// A Func<, > delegate - /// that references the method used to find the credentials. The default value is - /// . - /// - /// - /// This listener has been closed. - /// - public Func UserCredentialsFinder { - get { - CheckDisposed (); - return _userCredFinder; - } - - set { - CheckDisposed (); - _userCredFinder = value; - } - } - - #endregion - - #region Private Methods - - private void cleanupConnections () - { - HttpConnection[] conns = null; - lock (_connectionsSync) { - if (_connections.Count == 0) - return; - - // Need to copy this since closing will call the RemoveConnection method. - var keys = _connections.Keys; - conns = new HttpConnection[keys.Count]; - keys.CopyTo (conns, 0); - _connections.Clear (); - } - - for (var i = conns.Length - 1; i >= 0; i--) - conns[i].Close (true); - } - - private void cleanupContextQueue (bool sendServiceUnavailable) - { - HttpListenerContext[] ctxs = null; - lock (_ctxQueueSync) { - if (_ctxQueue.Count == 0) - return; - - ctxs = _ctxQueue.ToArray (); - _ctxQueue.Clear (); - } - - if (!sendServiceUnavailable) - return; - - foreach (var ctx in ctxs) { - var res = ctx.Response; - res.StatusCode = (int) HttpStatusCode.ServiceUnavailable; - res.Close (); - } - } - - private void cleanupContextRegistry () - { - HttpListenerContext[] ctxs = null; - lock (_ctxRegistrySync) { - if (_ctxRegistry.Count == 0) - return; - - // Need to copy this since closing will call the UnregisterContext method. - var keys = _ctxRegistry.Keys; - ctxs = new HttpListenerContext[keys.Count]; - keys.CopyTo (ctxs, 0); - _ctxRegistry.Clear (); - } - - for (var i = ctxs.Length - 1; i >= 0; i--) - ctxs[i].Connection.Close (true); - } - - private void cleanupWaitQueue (Exception exception) - { - HttpListenerAsyncResult[] aress = null; - lock (_waitQueueSync) { - if (_waitQueue.Count == 0) - return; - - aress = _waitQueue.ToArray (); - _waitQueue.Clear (); - } - - foreach (var ares in aress) - ares.Complete (exception); - } - - private void close (bool force) - { - if (_listening) { - _listening = false; - EndPointManager.RemoveListener (this); - } - - lock (_ctxRegistrySync) - cleanupContextQueue (!force); - - cleanupContextRegistry (); - cleanupConnections (); - cleanupWaitQueue (new ObjectDisposedException (GetType ().ToString ())); - - _disposed = true; - } - - private HttpListenerAsyncResult getAsyncResultFromQueue () - { - if (_waitQueue.Count == 0) - return null; - - var ares = _waitQueue[0]; - _waitQueue.RemoveAt (0); - - return ares; - } - - private HttpListenerContext getContextFromQueue () - { - if (_ctxQueue.Count == 0) - return null; - - var ctx = _ctxQueue[0]; - _ctxQueue.RemoveAt (0); - - return ctx; - } - - #endregion - - #region Internal Methods - - internal bool AddConnection (HttpConnection connection) - { - if (!_listening) - return false; - - lock (_connectionsSync) { - if (!_listening) - return false; - - _connections[connection] = connection; - return true; - } - } - - internal HttpListenerAsyncResult BeginGetContext (HttpListenerAsyncResult asyncResult) - { - lock (_ctxRegistrySync) { - if (!_listening) - throw new HttpListenerException (995); - - var ctx = getContextFromQueue (); - if (ctx == null) - _waitQueue.Add (asyncResult); - else - asyncResult.Complete (ctx, true); - - return asyncResult; - } - } - - internal void CheckDisposed () - { - if (_disposed) - throw new ObjectDisposedException (GetType ().ToString ()); - } - - internal string GetRealm () - { - var realm = _realm; - return realm != null && realm.Length > 0 ? realm : _defaultRealm; - } - - internal Func GetUserCredentialsFinder () - { - return _userCredFinder; - } - - internal bool RegisterContext (HttpListenerContext context) - { - if (!_listening) - return false; - - lock (_ctxRegistrySync) { - if (!_listening) - return false; - - _ctxRegistry[context] = context; - - var ares = getAsyncResultFromQueue (); - if (ares == null) - _ctxQueue.Add (context); - else - ares.Complete (context); - - return true; - } - } - - internal void RemoveConnection (HttpConnection connection) - { - lock (_connectionsSync) - _connections.Remove (connection); - } - - internal AuthenticationSchemes SelectAuthenticationScheme (HttpListenerRequest request) - { - var selector = _authSchemeSelector; - if (selector == null) - return _authSchemes; - - try { - return selector (request); - } - catch { - return AuthenticationSchemes.None; - } - } - - internal void UnregisterContext (HttpListenerContext context) - { - lock (_ctxRegistrySync) - _ctxRegistry.Remove (context); - } - - #endregion - - #region Public Methods - - /// - /// Shuts down the listener immediately. - /// - public void Abort () - { - if (_disposed) - return; - - close (true); - } - - /// - /// Begins getting an incoming request asynchronously. - /// - /// - /// This asynchronous operation must be completed by calling the EndGetContext method. - /// Typically, the method is invoked by the delegate. - /// - /// - /// An that represents the status of the asynchronous operation. - /// - /// - /// An delegate that references the method to invoke when - /// the asynchronous operation completes. - /// - /// - /// An that represents a user defined object to pass to - /// the delegate. - /// - /// - /// - /// This listener has no URI prefix on which listens. - /// - /// - /// -or- - /// - /// - /// This listener hasn't been started, or is currently stopped. - /// - /// - /// - /// This listener has been closed. - /// - public IAsyncResult BeginGetContext (AsyncCallback callback, Object state) - { - CheckDisposed (); - if (_prefixes.Count == 0) - throw new InvalidOperationException ("The listener has no URI prefix on which listens."); - - if (!_listening) - throw new InvalidOperationException ("The listener hasn't been started."); - - return BeginGetContext (new HttpListenerAsyncResult (callback, state)); - } - - /// - /// Shuts down the listener. - /// - public void Close () - { - if (_disposed) - return; - - close (false); - } - - /// - /// Ends an asynchronous operation to get an incoming request. - /// - /// - /// This method completes an asynchronous operation started by calling - /// the BeginGetContext method. - /// - /// - /// A that represents a request. - /// - /// - /// An obtained by calling the BeginGetContext method. - /// - /// - /// is . - /// - /// - /// wasn't obtained by calling the BeginGetContext method. - /// - /// - /// This method was already called for the specified . - /// - /// - /// This listener has been closed. - /// - public HttpListenerContext EndGetContext (IAsyncResult asyncResult) - { - CheckDisposed (); - if (asyncResult == null) - throw new ArgumentNullException ("asyncResult"); - - var ares = asyncResult as HttpListenerAsyncResult; - if (ares == null) - throw new ArgumentException ("A wrong IAsyncResult.", "asyncResult"); - - if (ares.EndCalled) - throw new InvalidOperationException ("This IAsyncResult cannot be reused."); - - ares.EndCalled = true; - if (!ares.IsCompleted) - ares.AsyncWaitHandle.WaitOne (); - - return ares.GetContext (); // This may throw an exception. - } - - /// - /// Gets an incoming request. - /// - /// - /// This method waits for an incoming request, and returns when a request is received. - /// - /// - /// A that represents a request. - /// - /// - /// - /// This listener has no URI prefix on which listens. - /// - /// - /// -or- - /// - /// - /// This listener hasn't been started, or is currently stopped. - /// - /// - /// - /// This listener has been closed. - /// - public HttpListenerContext GetContext () - { - CheckDisposed (); - if (_prefixes.Count == 0) - throw new InvalidOperationException ("The listener has no URI prefix on which listens."); - - if (!_listening) - throw new InvalidOperationException ("The listener hasn't been started."); - - var ares = BeginGetContext (new HttpListenerAsyncResult (null, null)); - ares.InGet = true; - - return EndGetContext (ares); - } - - /// - /// Starts receiving incoming requests. - /// - /// - /// This listener has been closed. - /// - public void Start () - { - CheckDisposed (); - if (_listening) - return; - - EndPointManager.AddListener (this); - _listening = true; - } - - /// - /// Stops receiving incoming requests. - /// - /// - /// This listener has been closed. - /// - public void Stop () - { - CheckDisposed (); - if (!_listening) - return; - - _listening = false; - EndPointManager.RemoveListener (this); - - lock (_ctxRegistrySync) - cleanupContextQueue (true); - - cleanupContextRegistry (); - cleanupConnections (); - cleanupWaitQueue (new HttpListenerException (995, "The listener is stopped.")); - } - - #endregion - - #region Explicit Interface Implementations - - /// - /// Releases all resources used by the listener. - /// - void IDisposable.Dispose () - { - if (_disposed) - return; - - close (true); - } - - #endregion - } -} + #region ֶ + + /// + /// Defines the _defaultRealm + /// + private static readonly string _defaultRealm; + + /// + /// Defines the _authSchemes + /// + private AuthenticationSchemes _authSchemes; + + /// + /// Defines the _authSchemeSelector + /// + private Func _authSchemeSelector; + + /// + /// Defines the _certFolderPath + /// + private string _certFolderPath; + + /// + /// Defines the _connections + /// + private Dictionary _connections; + + /// + /// Defines the _connectionsSync + /// + private object _connectionsSync; + + /// + /// Defines the _ctxQueue + /// + private List _ctxQueue; + + /// + /// Defines the _ctxQueueSync + /// + private object _ctxQueueSync; + + /// + /// Defines the _ctxRegistry + /// + private Dictionary _ctxRegistry; + + /// + /// Defines the _ctxRegistrySync + /// + private object _ctxRegistrySync; + + /// + /// Defines the _disposed + /// + private bool _disposed; + + /// + /// Defines the _ignoreWriteExceptions + /// + private bool _ignoreWriteExceptions; + + /// + /// Defines the _listening + /// + private volatile bool _listening; + + /// + /// Defines the _logger + /// + private Logger _logger; + + /// + /// Defines the _prefixes + /// + private HttpListenerPrefixCollection _prefixes; + + /// + /// Defines the _realm + /// + private string _realm; + + /// + /// Defines the _reuseAddress + /// + private bool _reuseAddress; + + /// + /// Defines the _sslConfig + /// + private ServerSslConfiguration _sslConfig; + + /// + /// Defines the _userCredFinder + /// + private Func _userCredFinder; + + /// + /// Defines the _waitQueue + /// + private List _waitQueue; + + /// + /// Defines the _waitQueueSync + /// + private object _waitQueueSync; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + public HttpListener() + { + _authSchemes = AuthenticationSchemes.Anonymous; + + _connections = new Dictionary(); + _connectionsSync = ((ICollection)_connections).SyncRoot; + + _ctxQueue = new List(); + _ctxQueueSync = ((ICollection)_ctxQueue).SyncRoot; + + _ctxRegistry = new Dictionary(); + _ctxRegistrySync = ((ICollection)_ctxRegistry).SyncRoot; + + _logger = new Logger(); + + _prefixes = new HttpListenerPrefixCollection(this); + + _waitQueue = new List(); + _waitQueueSync = ((ICollection)_waitQueue).SyncRoot; + } + + /// + /// Initializes static members of the class. + /// + static HttpListener() + { + _defaultRealm = "SECRET AREA"; + } + + #endregion 캯 + + #region + + /// + /// Gets a value indicating whether the listener can be used with the current operating system. + /// + public static bool IsSupported + { + get + { + return true; + } + } + + /// + /// Gets or sets the scheme used to authenticate the clients. + /// + public AuthenticationSchemes AuthenticationSchemes + { + get + { + CheckDisposed(); + return _authSchemes; + } + + set + { + CheckDisposed(); + _authSchemes = value; + } + } + + /// + /// Gets or sets the delegate called to select the scheme used to authenticate the clients. + /// + public Func AuthenticationSchemeSelector + { + get + { + CheckDisposed(); + return _authSchemeSelector; + } + + set + { + CheckDisposed(); + _authSchemeSelector = value; + } + } + + /// + /// Gets or sets the path to the folder in which stores the certificate files used to + /// authenticate the server on the secure connection. + /// + public string CertificateFolderPath + { + get + { + CheckDisposed(); + return _certFolderPath; + } + + set + { + CheckDisposed(); + _certFolderPath = value; + } + } + + /// + /// Gets or sets a value indicating whether the listener returns exceptions that occur when + /// sending the response to the client. + /// + public bool IgnoreWriteExceptions + { + get + { + CheckDisposed(); + return _ignoreWriteExceptions; + } + + set + { + CheckDisposed(); + _ignoreWriteExceptions = value; + } + } + + /// + /// Gets a value indicating whether the listener has been started. + /// + public bool IsListening + { + get + { + return _listening; + } + } + + /// + /// Gets the logging functions. + /// + public Logger Log + { + get + { + return _logger; + } + } + + /// + /// Gets the URI prefixes handled by the listener. + /// + public HttpListenerPrefixCollection Prefixes + { + get + { + CheckDisposed(); + return _prefixes; + } + } + + /// + /// Gets or sets the name of the realm associated with the listener. + /// + public string Realm + { + get + { + CheckDisposed(); + return _realm; + } + + set + { + CheckDisposed(); + _realm = value; + } + } + + /// + /// Gets or sets the SSL configuration used to authenticate the server and + /// optionally the client for secure connection. + /// + public ServerSslConfiguration SslConfiguration + { + get + { + CheckDisposed(); + return _sslConfig ?? (_sslConfig = new ServerSslConfiguration()); + } + + set + { + CheckDisposed(); + _sslConfig = value; + } + } + + /// + /// Gets or sets a value indicating whether when NTLM authentication is used, + /// the authentication information of first request is used to authenticate + /// additional requests on the same connection. + /// + public bool UnsafeConnectionNtlmAuthentication + { + get + { + throw new NotSupportedException(); + } + + set + { + throw new NotSupportedException(); + } + } + + /// + /// Gets or sets the delegate called to find the credentials for an identity used to + /// authenticate a client. + /// + public Func UserCredentialsFinder + { + get + { + CheckDisposed(); + return _userCredFinder; + } + + set + { + CheckDisposed(); + _userCredFinder = value; + } + } + + /// + /// Gets a value indicating whether IsDisposed + /// + internal bool IsDisposed + { + get + { + return _disposed; + } + } + + /// + /// Gets or sets a value indicating whether ReuseAddress + /// + internal bool ReuseAddress + { + get + { + return _reuseAddress; + } + + set + { + _reuseAddress = value; + } + } + + #endregion + + #region + + /// + /// Shuts down the listener immediately. + /// + public void Abort() + { + if (_disposed) + return; + + close(true); + } + + /// + /// Begins getting an incoming request asynchronously. + /// + /// The callback + /// The state + /// The + public IAsyncResult BeginGetContext(AsyncCallback callback, Object state) + { + CheckDisposed(); + if (_prefixes.Count == 0) + throw new InvalidOperationException("The listener has no URI prefix on which listens."); + + if (!_listening) + throw new InvalidOperationException("The listener hasn't been started."); + + return BeginGetContext(new HttpListenerAsyncResult(callback, state)); + } + + /// + /// Shuts down the listener. + /// + public void Close() + { + if (_disposed) + return; + + close(false); + } + + /// + /// Ends an asynchronous operation to get an incoming request. + /// + /// The asyncResult + /// The + public HttpListenerContext EndGetContext(IAsyncResult asyncResult) + { + CheckDisposed(); + if (asyncResult == null) + throw new ArgumentNullException("asyncResult"); + + var ares = asyncResult as HttpListenerAsyncResult; + if (ares == null) + throw new ArgumentException("A wrong IAsyncResult.", "asyncResult"); + + if (ares.EndCalled) + throw new InvalidOperationException("This IAsyncResult cannot be reused."); + + ares.EndCalled = true; + if (!ares.IsCompleted) + ares.AsyncWaitHandle.WaitOne(); + + return ares.GetContext(); // This may throw an exception. + } + + /// + /// Gets an incoming request. + /// + /// The + public HttpListenerContext GetContext() + { + CheckDisposed(); + if (_prefixes.Count == 0) + throw new InvalidOperationException("The listener has no URI prefix on which listens."); + + if (!_listening) + throw new InvalidOperationException("The listener hasn't been started."); + + var ares = BeginGetContext(new HttpListenerAsyncResult(null, null)); + ares.InGet = true; + + return EndGetContext(ares); + } + + /// + /// Starts receiving incoming requests. + /// + public void Start() + { + CheckDisposed(); + if (_listening) + return; + + EndPointManager.AddListener(this); + _listening = true; + } + + /// + /// Stops receiving incoming requests. + /// + public void Stop() + { + CheckDisposed(); + if (!_listening) + return; + + _listening = false; + EndPointManager.RemoveListener(this); + + lock (_ctxRegistrySync) + cleanupContextQueue(true); + + cleanupContextRegistry(); + cleanupConnections(); + cleanupWaitQueue(new HttpListenerException(995, "The listener is stopped.")); + } + + /// + /// The AddConnection + /// + /// The connection + /// The + internal bool AddConnection(HttpConnection connection) + { + if (!_listening) + return false; + + lock (_connectionsSync) + { + if (!_listening) + return false; + + _connections[connection] = connection; + return true; + } + } + + /// + /// The BeginGetContext + /// + /// The asyncResult + /// The + internal HttpListenerAsyncResult BeginGetContext(HttpListenerAsyncResult asyncResult) + { + lock (_ctxRegistrySync) + { + if (!_listening) + throw new HttpListenerException(995); + + var ctx = getContextFromQueue(); + if (ctx == null) + _waitQueue.Add(asyncResult); + else + asyncResult.Complete(ctx, true); + + return asyncResult; + } + } + + /// + /// The CheckDisposed + /// + internal void CheckDisposed() + { + if (_disposed) + throw new ObjectDisposedException(GetType().ToString()); + } + + /// + /// The GetRealm + /// + /// The + internal string GetRealm() + { + var realm = _realm; + return realm != null && realm.Length > 0 ? realm : _defaultRealm; + } + + /// + /// The GetUserCredentialsFinder + /// + /// The + internal Func GetUserCredentialsFinder() + { + return _userCredFinder; + } + + /// + /// The RegisterContext + /// + /// The context + /// The + internal bool RegisterContext(HttpListenerContext context) + { + if (!_listening) + return false; + + lock (_ctxRegistrySync) + { + if (!_listening) + return false; + + _ctxRegistry[context] = context; + + var ares = getAsyncResultFromQueue(); + if (ares == null) + _ctxQueue.Add(context); + else + ares.Complete(context); + + return true; + } + } + + /// + /// The RemoveConnection + /// + /// The connection + internal void RemoveConnection(HttpConnection connection) + { + lock (_connectionsSync) + _connections.Remove(connection); + } + + /// + /// The SelectAuthenticationScheme + /// + /// The request + /// The + internal AuthenticationSchemes SelectAuthenticationScheme(HttpListenerRequest request) + { + var selector = _authSchemeSelector; + if (selector == null) + return _authSchemes; + + try + { + return selector(request); + } + catch + { + return AuthenticationSchemes.None; + } + } + + /// + /// The UnregisterContext + /// + /// The context + internal void UnregisterContext(HttpListenerContext context) + { + lock (_ctxRegistrySync) + _ctxRegistry.Remove(context); + } + + /// + /// The cleanupConnections + /// + private void cleanupConnections() + { + HttpConnection[] conns = null; + lock (_connectionsSync) + { + if (_connections.Count == 0) + return; + + // Need to copy this since closing will call the RemoveConnection method. + var keys = _connections.Keys; + conns = new HttpConnection[keys.Count]; + keys.CopyTo(conns, 0); + _connections.Clear(); + } + + for (var i = conns.Length - 1; i >= 0; i--) + conns[i].Close(true); + } + + /// + /// The cleanupContextQueue + /// + /// The sendServiceUnavailable + private void cleanupContextQueue(bool sendServiceUnavailable) + { + HttpListenerContext[] ctxs = null; + lock (_ctxQueueSync) + { + if (_ctxQueue.Count == 0) + return; + + ctxs = _ctxQueue.ToArray(); + _ctxQueue.Clear(); + } + + if (!sendServiceUnavailable) + return; + + foreach (var ctx in ctxs) + { + var res = ctx.Response; + res.StatusCode = (int)HttpStatusCode.ServiceUnavailable; + res.Close(); + } + } + + /// + /// The cleanupContextRegistry + /// + private void cleanupContextRegistry() + { + HttpListenerContext[] ctxs = null; + lock (_ctxRegistrySync) + { + if (_ctxRegistry.Count == 0) + return; + + // Need to copy this since closing will call the UnregisterContext method. + var keys = _ctxRegistry.Keys; + ctxs = new HttpListenerContext[keys.Count]; + keys.CopyTo(ctxs, 0); + _ctxRegistry.Clear(); + } + + for (var i = ctxs.Length - 1; i >= 0; i--) + ctxs[i].Connection.Close(true); + } + + /// + /// The cleanupWaitQueue + /// + /// The exception + private void cleanupWaitQueue(Exception exception) + { + HttpListenerAsyncResult[] aress = null; + lock (_waitQueueSync) + { + if (_waitQueue.Count == 0) + return; + + aress = _waitQueue.ToArray(); + _waitQueue.Clear(); + } + + foreach (var ares in aress) + ares.Complete(exception); + } + + /// + /// The close + /// + /// The force + private void close(bool force) + { + if (_listening) + { + _listening = false; + EndPointManager.RemoveListener(this); + } + + lock (_ctxRegistrySync) + cleanupContextQueue(!force); + + cleanupContextRegistry(); + cleanupConnections(); + cleanupWaitQueue(new ObjectDisposedException(GetType().ToString())); + + _disposed = true; + } + + /// + /// The getAsyncResultFromQueue + /// + /// The + private HttpListenerAsyncResult getAsyncResultFromQueue() + { + if (_waitQueue.Count == 0) + return null; + + var ares = _waitQueue[0]; + _waitQueue.RemoveAt(0); + + return ares; + } + + /// + /// The getContextFromQueue + /// + /// The + private HttpListenerContext getContextFromQueue() + { + if (_ctxQueue.Count == 0) + return null; + + var ctx = _ctxQueue[0]; + _ctxQueue.RemoveAt(0); + + return ctx; + } + + /// + /// Releases all resources used by the listener. + /// + void IDisposable.Dispose() + { + if (_disposed) + return; + + close(true); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/HttpListenerAsyncResult.cs b/src/WebSocket/WebSocketCore/Net/HttpListenerAsyncResult.cs index fae541fb7..f26daa68c 100644 --- a/src/WebSocket/WebSocketCore/Net/HttpListenerAsyncResult.cs +++ b/src/WebSocket/WebSocketCore/Net/HttpListenerAsyncResult.cs @@ -1,4 +1,3 @@ -#region License /* * HttpListenerAsyncResult.cs * @@ -28,171 +27,262 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Gonzalo Paniagua Javier */ -#endregion -#region Contributors /* * Contributors: * - Nicholas Devenish */ -#endregion using System; using System.Threading; namespace WebSocketCore.Net { - internal class HttpListenerAsyncResult : IAsyncResult - { - #region Private Fields - - private AsyncCallback _callback; - private bool _completed; - private HttpListenerContext _context; - private bool _endCalled; - private Exception _exception; - private bool _inGet; - private object _state; - private object _sync; - private bool _syncCompleted; - private ManualResetEvent _waitHandle; - - #endregion - - #region Internal Constructors - - internal HttpListenerAsyncResult (AsyncCallback callback, object state) + /// + /// Defines the + /// + internal class HttpListenerAsyncResult : IAsyncResult { - _callback = callback; - _state = state; - _sync = new object (); + #region ֶ + + /// + /// Defines the _callback + /// + private AsyncCallback _callback; + + /// + /// Defines the _completed + /// + private bool _completed; + + /// + /// Defines the _context + /// + private HttpListenerContext _context; + + /// + /// Defines the _endCalled + /// + private bool _endCalled; + + /// + /// Defines the _exception + /// + private Exception _exception; + + /// + /// Defines the _inGet + /// + private bool _inGet; + + /// + /// Defines the _state + /// + private object _state; + + /// + /// Defines the _sync + /// + private object _sync; + + /// + /// Defines the _syncCompleted + /// + private bool _syncCompleted; + + /// + /// Defines the _waitHandle + /// + private ManualResetEvent _waitHandle; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The callback + /// The state + internal HttpListenerAsyncResult(AsyncCallback callback, object state) + { + _callback = callback; + _state = state; + _sync = new object(); + } + + #endregion 캯 + + #region + + /// + /// Gets the AsyncState + /// + public object AsyncState + { + get + { + return _state; + } + } + + /// + /// Gets the AsyncWaitHandle + /// + public WaitHandle AsyncWaitHandle + { + get + { + lock (_sync) + return _waitHandle ?? (_waitHandle = new ManualResetEvent(_completed)); + } + } + + /// + /// Gets a value indicating whether CompletedSynchronously + /// + public bool CompletedSynchronously + { + get + { + return _syncCompleted; + } + } + + /// + /// Gets a value indicating whether IsCompleted + /// + public bool IsCompleted + { + get + { + lock (_sync) + return _completed; + } + } + + /// + /// Gets or sets a value indicating whether EndCalled + /// + internal bool EndCalled + { + get + { + return _endCalled; + } + + set + { + _endCalled = value; + } + } + + /// + /// Gets or sets a value indicating whether InGet + /// + internal bool InGet + { + get + { + return _inGet; + } + + set + { + _inGet = value; + } + } + + #endregion + + #region + + /// + /// The Complete + /// + /// The exception + internal void Complete(Exception exception) + { + _exception = _inGet && (exception is ObjectDisposedException) + ? new HttpListenerException(995, "The listener is closed.") + : exception; + + complete(this); + } + + /// + /// The Complete + /// + /// The context + internal void Complete(HttpListenerContext context) + { + Complete(context, false); + } + + /// + /// The Complete + /// + /// The context + /// The syncCompleted + internal void Complete(HttpListenerContext context, bool syncCompleted) + { + _context = context; + _syncCompleted = syncCompleted; + + complete(this); + } + + /// + /// The GetContext + /// + /// The + internal HttpListenerContext GetContext() + { + if (_exception != null) + throw _exception; + + return _context; + } + + /// + /// The complete + /// + /// The asyncResult + private static void complete(HttpListenerAsyncResult asyncResult) + { + lock (asyncResult._sync) + { + asyncResult._completed = true; + + var waitHandle = asyncResult._waitHandle; + if (waitHandle != null) + waitHandle.Set(); + } + + var callback = asyncResult._callback; + if (callback == null) + return; + + ThreadPool.QueueUserWorkItem( + state => + { + try + { + callback(asyncResult); + } + catch + { + } + }, + null + ); + } + + #endregion } - - #endregion - - #region Internal Properties - - internal bool EndCalled { - get { - return _endCalled; - } - - set { - _endCalled = value; - } - } - - internal bool InGet { - get { - return _inGet; - } - - set { - _inGet = value; - } - } - - #endregion - - #region Public Properties - - public object AsyncState { - get { - return _state; - } - } - - public WaitHandle AsyncWaitHandle { - get { - lock (_sync) - return _waitHandle ?? (_waitHandle = new ManualResetEvent (_completed)); - } - } - - public bool CompletedSynchronously { - get { - return _syncCompleted; - } - } - - public bool IsCompleted { - get { - lock (_sync) - return _completed; - } - } - - #endregion - - #region Private Methods - - private static void complete (HttpListenerAsyncResult asyncResult) - { - lock (asyncResult._sync) { - asyncResult._completed = true; - - var waitHandle = asyncResult._waitHandle; - if (waitHandle != null) - waitHandle.Set (); - } - - var callback = asyncResult._callback; - if (callback == null) - return; - - ThreadPool.QueueUserWorkItem ( - state => { - try { - callback (asyncResult); - } - catch { - } - }, - null - ); - } - - #endregion - - #region Internal Methods - - internal void Complete (Exception exception) - { - _exception = _inGet && (exception is ObjectDisposedException) - ? new HttpListenerException (995, "The listener is closed.") - : exception; - - complete (this); - } - - internal void Complete (HttpListenerContext context) - { - Complete (context, false); - } - - internal void Complete (HttpListenerContext context, bool syncCompleted) - { - _context = context; - _syncCompleted = syncCompleted; - - complete (this); - } - - internal HttpListenerContext GetContext () - { - if (_exception != null) - throw _exception; - - return _context; - } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/HttpListenerContext.cs b/src/WebSocket/WebSocketCore/Net/HttpListenerContext.cs index 2ee89167a..9e2aa7e40 100644 --- a/src/WebSocket/WebSocketCore/Net/HttpListenerContext.cs +++ b/src/WebSocket/WebSocketCore/Net/HttpListenerContext.cs @@ -1,4 +1,3 @@ -#region License /* * HttpListenerContext.cs * @@ -28,14 +27,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Gonzalo Paniagua Javier */ -#endregion using System; using System.Security.Principal; @@ -43,214 +39,257 @@ namespace WebSocketCore.Net { - /// - /// Provides the access to the HTTP request and response objects used by - /// the . - /// - /// - /// This class cannot be inherited. - /// - public sealed class HttpListenerContext - { - #region Private Fields - - private HttpConnection _connection; - private string _error; - private int _errorStatus; - private HttpListener _listener; - private HttpListenerRequest _request; - private HttpListenerResponse _response; - private IPrincipal _user; - private HttpListenerWebSocketContext _websocketContext; - - #endregion - - #region Internal Constructors - - internal HttpListenerContext (HttpConnection connection) - { - _connection = connection; - _errorStatus = 400; - _request = new HttpListenerRequest (this); - _response = new HttpListenerResponse (this); - } - - #endregion - - #region Internal Properties - - internal HttpConnection Connection { - get { - return _connection; - } - } - - internal string ErrorMessage { - get { - return _error; - } - - set { - _error = value; - } - } - - internal int ErrorStatus { - get { - return _errorStatus; - } - - set { - _errorStatus = value; - } - } - - internal bool HasError { - get { - return _error != null; - } - } - - internal HttpListener Listener { - get { - return _listener; - } - - set { - _listener = value; - } - } - - #endregion - - #region Public Properties - - /// - /// Gets the HTTP request object that represents a client request. - /// - /// - /// A that represents the client request. - /// - public HttpListenerRequest Request { - get { - return _request; - } - } - /// - /// Gets the HTTP response object used to send a response to the client. + /// Provides the access to the HTTP request and response objects used by + /// the . /// - /// - /// A that represents a response to the client request. - /// - public HttpListenerResponse Response { - get { - return _response; - } - } - - /// - /// Gets the client information (identity, authentication, and security roles). - /// - /// - /// A instance that represents the client information. - /// - public IPrincipal User { - get { - return _user; - } - } - - #endregion - - #region Internal Methods - - internal bool Authenticate () - { - var schm = _listener.SelectAuthenticationScheme (_request); - if (schm == AuthenticationSchemes.Anonymous) - return true; - - if (schm == AuthenticationSchemes.None) { - _response.Close (HttpStatusCode.Forbidden); - return false; - } - - var realm = _listener.GetRealm (); - var user = - HttpUtility.CreateUser ( - _request.Headers["Authorization"], - schm, - realm, - _request.HttpMethod, - _listener.GetUserCredentialsFinder () - ); - - if (user == null || !user.Identity.IsAuthenticated) { - _response.CloseWithAuthChallenge (new AuthenticationChallenge (schm, realm).ToString ()); - return false; - } - - _user = user; - return true; - } - - internal bool Register () - { - return _listener.RegisterContext (this); - } - - internal void Unregister () - { - _listener.UnregisterContext (this); - } - - #endregion - - #region Public Methods - - /// - /// Accepts a WebSocket handshake request. - /// - /// - /// A that represents - /// the WebSocket handshake request. - /// - /// - /// A that represents the subprotocol supported on - /// this WebSocket connection. - /// - /// - /// - /// is empty. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// - /// This method has already been called. - /// - public HttpListenerWebSocketContext AcceptWebSocket (string protocol) + public sealed class HttpListenerContext { - if (_websocketContext != null) - throw new InvalidOperationException ("The accepting is already in progress."); - - if (protocol != null) { - if (protocol.Length == 0) - throw new ArgumentException ("An empty string.", "protocol"); - - if (!protocol.IsToken ()) - throw new ArgumentException ("Contains an invalid character.", "protocol"); - } - - _websocketContext = new HttpListenerWebSocketContext (this, protocol); - return _websocketContext; + #region ֶ + + /// + /// Defines the _connection + /// + private HttpConnection _connection; + + /// + /// Defines the _error + /// + private string _error; + + /// + /// Defines the _errorStatus + /// + private int _errorStatus; + + /// + /// Defines the _listener + /// + private HttpListener _listener; + + /// + /// Defines the _request + /// + private HttpListenerRequest _request; + + /// + /// Defines the _response + /// + private HttpListenerResponse _response; + + /// + /// Defines the _user + /// + private IPrincipal _user; + + /// + /// Defines the _websocketContext + /// + private HttpListenerWebSocketContext _websocketContext; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The connection + internal HttpListenerContext(HttpConnection connection) + { + _connection = connection; + _errorStatus = 400; + _request = new HttpListenerRequest(this); + _response = new HttpListenerResponse(this); + } + + #endregion 캯 + + #region + + /// + /// Gets the HTTP request object that represents a client request. + /// + public HttpListenerRequest Request + { + get + { + return _request; + } + } + + /// + /// Gets the HTTP response object used to send a response to the client. + /// + public HttpListenerResponse Response + { + get + { + return _response; + } + } + + /// + /// Gets the client information (identity, authentication, and security roles). + /// + public IPrincipal User + { + get + { + return _user; + } + } + + /// + /// Gets the Connection + /// + internal HttpConnection Connection + { + get + { + return _connection; + } + } + + /// + /// Gets or sets the ErrorMessage + /// + internal string ErrorMessage + { + get + { + return _error; + } + + set + { + _error = value; + } + } + + /// + /// Gets or sets the ErrorStatus + /// + internal int ErrorStatus + { + get + { + return _errorStatus; + } + + set + { + _errorStatus = value; + } + } + + /// + /// Gets a value indicating whether HasError + /// + internal bool HasError + { + get + { + return _error != null; + } + } + + /// + /// Gets or sets the Listener + /// + internal HttpListener Listener + { + get + { + return _listener; + } + + set + { + _listener = value; + } + } + + #endregion + + #region + + /// + /// Accepts a WebSocket handshake request. + /// + /// The protocol + /// The + public HttpListenerWebSocketContext AcceptWebSocket(string protocol) + { + if (_websocketContext != null) + throw new InvalidOperationException("The accepting is already in progress."); + + if (protocol != null) + { + if (protocol.Length == 0) + throw new ArgumentException("An empty string.", "protocol"); + + if (!protocol.IsToken()) + throw new ArgumentException("Contains an invalid character.", "protocol"); + } + + _websocketContext = new HttpListenerWebSocketContext(this, protocol); + return _websocketContext; + } + + /// + /// The Authenticate + /// + /// The + internal bool Authenticate() + { + var schm = _listener.SelectAuthenticationScheme(_request); + if (schm == AuthenticationSchemes.Anonymous) + return true; + + if (schm == AuthenticationSchemes.None) + { + _response.Close(HttpStatusCode.Forbidden); + return false; + } + + var realm = _listener.GetRealm(); + var user = + HttpUtility.CreateUser( + _request.Headers["Authorization"], + schm, + realm, + _request.HttpMethod, + _listener.GetUserCredentialsFinder() + ); + + if (user == null || !user.Identity.IsAuthenticated) + { + _response.CloseWithAuthChallenge(new AuthenticationChallenge(schm, realm).ToString()); + return false; + } + + _user = user; + return true; + } + + /// + /// The Register + /// + /// The + internal bool Register() + { + return _listener.RegisterContext(this); + } + + /// + /// The Unregister + /// + internal void Unregister() + { + _listener.UnregisterContext(this); + } + + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/HttpListenerException.cs b/src/WebSocket/WebSocketCore/Net/HttpListenerException.cs index 6905e409f..60e876f0a 100644 --- a/src/WebSocket/WebSocketCore/Net/HttpListenerException.cs +++ b/src/WebSocket/WebSocketCore/Net/HttpListenerException.cs @@ -1,4 +1,3 @@ -#region License /* * HttpListenerException.cs * @@ -28,14 +27,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Gonzalo Paniagua Javier */ -#endregion using System; using System.ComponentModel; @@ -43,85 +39,67 @@ namespace WebSocketCore.Net { - /// - /// The exception that is thrown when a gets an error - /// processing an HTTP request. - /// - [Serializable] - public class HttpListenerException : Win32Exception - { - #region Protected Constructors - /// - /// Initializes a new instance of the class from - /// the specified and . + /// The exception that is thrown when a gets an error + /// processing an HTTP request. /// - /// - /// A that contains the serialized object data. - /// - /// - /// A that specifies the source for the deserialization. - /// - protected HttpListenerException ( - SerializationInfo serializationInfo, StreamingContext streamingContext) - : base (serializationInfo, streamingContext) + [Serializable] + public class HttpListenerException : Win32Exception { - } + #region 캯 - #endregion + /// + /// Initializes a new instance of the class. + /// + public HttpListenerException() + { + } - #region Public Constructors + /// + /// Initializes a new instance of the class. + /// + /// The errorCode + public HttpListenerException(int errorCode) + : base(errorCode) + { + } - /// - /// Initializes a new instance of the class. - /// - public HttpListenerException () - { - } + /// + /// Initializes a new instance of the class. + /// + /// The errorCode + /// The message + public HttpListenerException(int errorCode, string message) + : base(errorCode, message) + { + } - /// - /// Initializes a new instance of the class - /// with the specified . - /// - /// - /// An that identifies the error. - /// - public HttpListenerException (int errorCode) - : base (errorCode) - { - } + /// + /// Initializes a new instance of the class. + /// + /// The serializationInfo + /// The streamingContext + protected HttpListenerException( + SerializationInfo serializationInfo, StreamingContext streamingContext) + : base(serializationInfo, streamingContext) + { + } - /// - /// Initializes a new instance of the class - /// with the specified and . - /// - /// - /// An that identifies the error. - /// - /// - /// A that describes the error. - /// - public HttpListenerException (int errorCode, string message) - : base (errorCode, message) - { - } + #endregion 캯 - #endregion + #region - #region Public Properties + /// + /// Gets the error code that identifies the error that occurred. + /// + public override int ErrorCode + { + get + { + return NativeErrorCode; + } + } - /// - /// Gets the error code that identifies the error that occurred. - /// - /// - /// An that identifies the error. - /// - public override int ErrorCode { - get { - return NativeErrorCode; - } + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/HttpListenerPrefix.cs b/src/WebSocket/WebSocketCore/Net/HttpListenerPrefix.cs index 9624a5504..62df8317a 100644 --- a/src/WebSocket/WebSocketCore/Net/HttpListenerPrefix.cs +++ b/src/WebSocket/WebSocketCore/Net/HttpListenerPrefix.cs @@ -1,4 +1,3 @@ -#region License /* * HttpListenerPrefix.cs * @@ -28,201 +27,250 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Gonzalo Paniagua Javier * - Oleg Mihailik */ -#endregion using System; using System.Net; namespace WebSocketCore.Net { - internal sealed class HttpListenerPrefix - { - #region Private Fields - - private string _host; - private HttpListener _listener; - private string _original; - private string _path; - private string _port; - private string _prefix; - private bool _secure; - - #endregion - - #region Internal Constructors - /// - /// Initializes a new instance of the class with - /// the specified . + /// Defines the /// - /// - /// This constructor must be called after calling the CheckPrefix method. - /// - /// - /// A that represents the URI prefix. - /// - internal HttpListenerPrefix (string uriPrefix) - { - _original = uriPrefix; - parse (uriPrefix); - } - - #endregion - - #region Public Properties - - public string Host { - get { - return _host; - } - } - - public bool IsSecure { - get { - return _secure; - } - } - - public HttpListener Listener { - get { - return _listener; - } - - set { - _listener = value; - } - } - - public string Original { - get { - return _original; - } - } - - public string Path { - get { - return _path; - } - } - - public string Port { - get { - return _port; - } - } - - #endregion - - #region Private Methods - - private void parse (string uriPrefix) + internal sealed class HttpListenerPrefix { - if (uriPrefix.StartsWith ("https")) - _secure = true; - - var len = uriPrefix.Length; - var startHost = uriPrefix.IndexOf (':') + 3; - var root = uriPrefix.IndexOf ('/', startHost + 1, len - startHost - 1); - - var colon = uriPrefix.LastIndexOf (':', root - 1, root - startHost - 1); - if (uriPrefix[root - 1] != ']' && colon > startHost) { - _host = uriPrefix.Substring (startHost, colon - startHost); - _port = uriPrefix.Substring (colon + 1, root - colon - 1); - } - else { - _host = uriPrefix.Substring (startHost, root - startHost); - _port = _secure ? "443" : "80"; - } - - _path = uriPrefix.Substring (root); - - _prefix = - String.Format ("http{0}://{1}:{2}{3}", _secure ? "s" : "", _host, _port, _path); + #region ֶ + + /// + /// Defines the _host + /// + private string _host; + + /// + /// Defines the _listener + /// + private HttpListener _listener; + + /// + /// Defines the _original + /// + private string _original; + + /// + /// Defines the _path + /// + private string _path; + + /// + /// Defines the _port + /// + private string _port; + + /// + /// Defines the _prefix + /// + private string _prefix; + + /// + /// Defines the _secure + /// + private bool _secure; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The uriPrefix + internal HttpListenerPrefix(string uriPrefix) + { + _original = uriPrefix; + parse(uriPrefix); + } + + #endregion 캯 + + #region + + /// + /// Gets the Host + /// + public string Host + { + get + { + return _host; + } + } + + /// + /// Gets a value indicating whether IsSecure + /// + public bool IsSecure + { + get + { + return _secure; + } + } + + /// + /// Gets or sets the Listener + /// + public HttpListener Listener + { + get + { + return _listener; + } + + set + { + _listener = value; + } + } + + /// + /// Gets the Original + /// + public string Original + { + get + { + return _original; + } + } + + /// + /// Gets the Path + /// + public string Path + { + get + { + return _path; + } + } + + /// + /// Gets the Port + /// + public string Port + { + get + { + return _port; + } + } + + #endregion + + #region + + /// + /// The CheckPrefix + /// + /// The uriPrefix + public static void CheckPrefix(string uriPrefix) + { + if (uriPrefix == null) + throw new ArgumentNullException("uriPrefix"); + + var len = uriPrefix.Length; + if (len == 0) + throw new ArgumentException("An empty string.", "uriPrefix"); + + if (!(uriPrefix.StartsWith("http://") || uriPrefix.StartsWith("https://"))) + throw new ArgumentException("The scheme isn't 'http' or 'https'.", "uriPrefix"); + + var startHost = uriPrefix.IndexOf(':') + 3; + if (startHost >= len) + throw new ArgumentException("No host is specified.", "uriPrefix"); + + if (uriPrefix[startHost] == ':') + throw new ArgumentException("No host is specified.", "uriPrefix"); + + var root = uriPrefix.IndexOf('/', startHost, len - startHost); + if (root == startHost) + throw new ArgumentException("No host is specified.", "uriPrefix"); + + if (root == -1 || uriPrefix[len - 1] != '/') + throw new ArgumentException("Ends without '/'.", "uriPrefix"); + + if (uriPrefix[root - 1] == ':') + throw new ArgumentException("No port is specified.", "uriPrefix"); + + if (root == len - 2) + throw new ArgumentException("No path is specified.", "uriPrefix"); + } + + /// + /// Determines whether this instance and the specified have the same value. + /// + /// The obj + /// The + public override bool Equals(Object obj) + { + var pref = obj as HttpListenerPrefix; + return pref != null && pref._prefix == _prefix; + } + + /// + /// Gets the hash code for this instance. + /// + /// The + public override int GetHashCode() + { + return _prefix.GetHashCode(); + } + + /// + /// The ToString + /// + /// The + public override string ToString() + { + return _prefix; + } + + /// + /// The parse + /// + /// The uriPrefix + private void parse(string uriPrefix) + { + if (uriPrefix.StartsWith("https")) + _secure = true; + + var len = uriPrefix.Length; + var startHost = uriPrefix.IndexOf(':') + 3; + var root = uriPrefix.IndexOf('/', startHost + 1, len - startHost - 1); + + var colon = uriPrefix.LastIndexOf(':', root - 1, root - startHost - 1); + if (uriPrefix[root - 1] != ']' && colon > startHost) + { + _host = uriPrefix.Substring(startHost, colon - startHost); + _port = uriPrefix.Substring(colon + 1, root - colon - 1); + } + else + { + _host = uriPrefix.Substring(startHost, root - startHost); + _port = _secure ? "443" : "80"; + } + + _path = uriPrefix.Substring(root); + + _prefix = + String.Format("http{0}://{1}:{2}{3}", _secure ? "s" : "", _host, _port, _path); + } + + #endregion } - - #endregion - - #region Public Methods - - public static void CheckPrefix (string uriPrefix) - { - if (uriPrefix == null) - throw new ArgumentNullException ("uriPrefix"); - - var len = uriPrefix.Length; - if (len == 0) - throw new ArgumentException ("An empty string.", "uriPrefix"); - - if (!(uriPrefix.StartsWith ("http://") || uriPrefix.StartsWith ("https://"))) - throw new ArgumentException ("The scheme isn't 'http' or 'https'.", "uriPrefix"); - - var startHost = uriPrefix.IndexOf (':') + 3; - if (startHost >= len) - throw new ArgumentException ("No host is specified.", "uriPrefix"); - - if (uriPrefix[startHost] == ':') - throw new ArgumentException ("No host is specified.", "uriPrefix"); - - var root = uriPrefix.IndexOf ('/', startHost, len - startHost); - if (root == startHost) - throw new ArgumentException ("No host is specified.", "uriPrefix"); - - if (root == -1 || uriPrefix[len - 1] != '/') - throw new ArgumentException ("Ends without '/'.", "uriPrefix"); - - if (uriPrefix[root - 1] == ':') - throw new ArgumentException ("No port is specified.", "uriPrefix"); - - if (root == len - 2) - throw new ArgumentException ("No path is specified.", "uriPrefix"); - } - - /// - /// Determines whether this instance and the specified have the same value. - /// - /// - /// This method will be required to detect duplicates in any collection. - /// - /// - /// An to compare to this instance. - /// - /// - /// true if is a and - /// its value is the same as this instance; otherwise, false. - /// - public override bool Equals (Object obj) - { - var pref = obj as HttpListenerPrefix; - return pref != null && pref._prefix == _prefix; - } - - /// - /// Gets the hash code for this instance. - /// - /// - /// This method will be required to detect duplicates in any collection. - /// - /// - /// An that represents the hash code. - /// - public override int GetHashCode () - { - return _prefix.GetHashCode (); - } - - public override string ToString () - { - return _prefix; - } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/HttpListenerPrefixCollection.cs b/src/WebSocket/WebSocketCore/Net/HttpListenerPrefixCollection.cs index ca2ac2283..d4a814e78 100644 --- a/src/WebSocket/WebSocketCore/Net/HttpListenerPrefixCollection.cs +++ b/src/WebSocket/WebSocketCore/Net/HttpListenerPrefixCollection.cs @@ -1,4 +1,3 @@ -#region License /* * HttpListenerPrefixCollection.cs * @@ -28,14 +27,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Gonzalo Paniagua Javier */ -#endregion using System; using System.Collections; @@ -43,236 +39,178 @@ namespace WebSocketCore.Net { - /// - /// Provides the collection used to store the URI prefixes for the . - /// - /// - /// The responds to the request which has a requested URI that - /// the prefixes most closely match. - /// - public class HttpListenerPrefixCollection : ICollection, IEnumerable, IEnumerable - { - #region Private Fields - - private HttpListener _listener; - private List _prefixes; - - #endregion - - #region Internal Constructors - - internal HttpListenerPrefixCollection (HttpListener listener) - { - _listener = listener; - _prefixes = new List (); - } - - #endregion - - #region Public Properties - - /// - /// Gets the number of prefixes in the collection. - /// - /// - /// An that represents the number of prefixes. - /// - public int Count { - get { - return _prefixes.Count; - } - } - - /// - /// Gets a value indicating whether the access to the collection is read-only. - /// - /// - /// Always returns false. - /// - public bool IsReadOnly { - get { - return false; - } - } - /// - /// Gets a value indicating whether the access to the collection is synchronized. + /// Provides the collection used to store the URI prefixes for the . /// - /// - /// Always returns false. - /// - public bool IsSynchronized { - get { - return false; - } - } - - #endregion - - #region Public Methods - - /// - /// Adds the specified to the collection. - /// - /// - /// A that represents the URI prefix to add. The prefix must be - /// a well-formed URI prefix with http or https scheme, and must end with a '/'. - /// - /// - /// is . - /// - /// - /// is invalid. - /// - /// - /// The associated with this collection is closed. - /// - public void Add (string uriPrefix) - { - _listener.CheckDisposed (); - HttpListenerPrefix.CheckPrefix (uriPrefix); - if (_prefixes.Contains (uriPrefix)) - return; - - _prefixes.Add (uriPrefix); - if (_listener.IsListening) - EndPointManager.AddPrefix (uriPrefix, _listener); - } - - /// - /// Removes all URI prefixes from the collection. - /// - /// - /// The associated with this collection is closed. - /// - public void Clear () + public class HttpListenerPrefixCollection : ICollection, IEnumerable, IEnumerable { - _listener.CheckDisposed (); - _prefixes.Clear (); - if (_listener.IsListening) - EndPointManager.RemoveListener (_listener); + #region ֶ + + /// + /// Defines the _listener + /// + private HttpListener _listener; + + /// + /// Defines the _prefixes + /// + private List _prefixes; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The listener + internal HttpListenerPrefixCollection(HttpListener listener) + { + _listener = listener; + _prefixes = new List(); + } + + #endregion 캯 + + #region + + /// + /// Gets the number of prefixes in the collection. + /// + public int Count + { + get + { + return _prefixes.Count; + } + } + + /// + /// Gets a value indicating whether the access to the collection is read-only. + /// + public bool IsReadOnly + { + get + { + return false; + } + } + + /// + /// Gets a value indicating whether the access to the collection is synchronized. + /// + public bool IsSynchronized + { + get + { + return false; + } + } + + #endregion + + #region + + /// + /// Adds the specified to the collection. + /// + /// The uriPrefix + public void Add(string uriPrefix) + { + _listener.CheckDisposed(); + HttpListenerPrefix.CheckPrefix(uriPrefix); + if (_prefixes.Contains(uriPrefix)) + return; + + _prefixes.Add(uriPrefix); + if (_listener.IsListening) + EndPointManager.AddPrefix(uriPrefix, _listener); + } + + /// + /// Removes all URI prefixes from the collection. + /// + public void Clear() + { + _listener.CheckDisposed(); + _prefixes.Clear(); + if (_listener.IsListening) + EndPointManager.RemoveListener(_listener); + } + + /// + /// Returns a value indicating whether the collection contains the specified + /// . + /// + /// The uriPrefix + /// The + public bool Contains(string uriPrefix) + { + _listener.CheckDisposed(); + if (uriPrefix == null) + throw new ArgumentNullException("uriPrefix"); + + return _prefixes.Contains(uriPrefix); + } + + /// + /// Copies the contents of the collection to the specified . + /// + /// The array + /// The offset + public void CopyTo(Array array, int offset) + { + _listener.CheckDisposed(); + ((ICollection)_prefixes).CopyTo(array, offset); + } + + /// + /// Copies the contents of the collection to the specified array of . + /// + /// The array + /// The offset + public void CopyTo(string[] array, int offset) + { + _listener.CheckDisposed(); + _prefixes.CopyTo(array, offset); + } + + /// + /// Gets the enumerator used to iterate through the . + /// + /// The + public IEnumerator GetEnumerator() + { + return _prefixes.GetEnumerator(); + } + + /// + /// Removes the specified from the collection. + /// + /// The uriPrefix + /// The + public bool Remove(string uriPrefix) + { + _listener.CheckDisposed(); + if (uriPrefix == null) + throw new ArgumentNullException("uriPrefix"); + + var ret = _prefixes.Remove(uriPrefix); + if (ret && _listener.IsListening) + EndPointManager.RemovePrefix(uriPrefix, _listener); + + return ret; + } + + /// + /// Gets the enumerator used to iterate through the . + /// + /// The + IEnumerator IEnumerable.GetEnumerator() + { + return _prefixes.GetEnumerator(); + } + + #endregion } - - /// - /// Returns a value indicating whether the collection contains the specified - /// . - /// - /// - /// true if the collection contains ; - /// otherwise, false. - /// - /// - /// A that represents the URI prefix to test. - /// - /// - /// is . - /// - /// - /// The associated with this collection is closed. - /// - public bool Contains (string uriPrefix) - { - _listener.CheckDisposed (); - if (uriPrefix == null) - throw new ArgumentNullException ("uriPrefix"); - - return _prefixes.Contains (uriPrefix); - } - - /// - /// Copies the contents of the collection to the specified . - /// - /// - /// An that receives the URI prefix strings in the collection. - /// - /// - /// An that represents the zero-based index in - /// at which copying begins. - /// - /// - /// The associated with this collection is closed. - /// - public void CopyTo (Array array, int offset) - { - _listener.CheckDisposed (); - ((ICollection) _prefixes).CopyTo (array, offset); - } - - /// - /// Copies the contents of the collection to the specified array of . - /// - /// - /// An array of that receives the URI prefix strings in the collection. - /// - /// - /// An that represents the zero-based index in - /// at which copying begins. - /// - /// - /// The associated with this collection is closed. - /// - public void CopyTo (string[] array, int offset) - { - _listener.CheckDisposed (); - _prefixes.CopyTo (array, offset); - } - - /// - /// Gets the enumerator used to iterate through the . - /// - /// - /// An instance used to iterate - /// through the collection. - /// - public IEnumerator GetEnumerator () - { - return _prefixes.GetEnumerator (); - } - - /// - /// Removes the specified from the collection. - /// - /// - /// true if is successfully found and removed; - /// otherwise, false. - /// - /// - /// A that represents the URI prefix to remove. - /// - /// - /// is . - /// - /// - /// The associated with this collection is closed. - /// - public bool Remove (string uriPrefix) - { - _listener.CheckDisposed (); - if (uriPrefix == null) - throw new ArgumentNullException ("uriPrefix"); - - var ret = _prefixes.Remove (uriPrefix); - if (ret && _listener.IsListening) - EndPointManager.RemovePrefix (uriPrefix, _listener); - - return ret; - } - - #endregion - - #region Explicit Interface Implementations - - /// - /// Gets the enumerator used to iterate through the . - /// - /// - /// An instance used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator () - { - return _prefixes.GetEnumerator (); - } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/HttpListenerRequest.cs b/src/WebSocket/WebSocketCore/Net/HttpListenerRequest.cs index 14a4e90a4..be3b85a1d 100644 --- a/src/WebSocket/WebSocketCore/Net/HttpListenerRequest.cs +++ b/src/WebSocket/WebSocketCore/Net/HttpListenerRequest.cs @@ -1,4 +1,3 @@ -#region License /* * HttpListenerRequest.cs * @@ -28,14 +27,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Gonzalo Paniagua Javier */ -#endregion using System; using System.Collections.Generic; @@ -47,864 +43,864 @@ namespace WebSocketCore.Net { - /// - /// Represents an incoming request to a instance. - /// - /// - /// This class cannot be inherited. - /// - public sealed class HttpListenerRequest - { - #region Private Fields - - private static readonly byte[] _100continue; - private string[] _acceptTypes; - private bool _chunked; - private HttpConnection _connection; - private Encoding _contentEncoding; - private long _contentLength; - private HttpListenerContext _context; - private CookieCollection _cookies; - private WebHeaderCollection _headers; - private string _httpMethod; - private Stream _inputStream; - private Version _protocolVersion; - private NameValueCollection _queryString; - private string _rawUrl; - private Guid _requestTraceIdentifier; - private Uri _url; - private Uri _urlReferrer; - private bool _urlSet; - private string _userHostName; - private string[] _userLanguages; - - #endregion - - #region Static Constructor - - static HttpListenerRequest () - { - _100continue = Encoding.ASCII.GetBytes ("HTTP/1.1 100 Continue\r\n\r\n"); - } - - #endregion - - #region Internal Constructors - - internal HttpListenerRequest (HttpListenerContext context) - { - _context = context; - - _connection = context.Connection; - _contentLength = -1; - _headers = new WebHeaderCollection (); - _requestTraceIdentifier = Guid.NewGuid (); - } - - #endregion - - #region Public Properties - - /// - /// Gets the media types that are acceptable for the client. - /// - /// - /// - /// An array of that contains the names of the media - /// types specified in the value of the Accept header. - /// - /// - /// if the header is not present. - /// - /// - public string[] AcceptTypes { - get { - var val = _headers["Accept"]; - if (val == null) - return null; - - if (_acceptTypes == null) { - _acceptTypes = val - .SplitHeaderValue (',') - .Trim () - .ToList () - .ToArray (); - } - - return _acceptTypes; - } - } - - /// - /// Gets an error code that identifies a problem with the certificate - /// provided by the client. - /// - /// - /// An that represents an error code. - /// - /// - /// This property is not supported. - /// - public int ClientCertificateError { - get { - throw new NotSupportedException (); - } - } - - /// - /// Gets the encoding for the entity body data included in the request. - /// - /// - /// - /// A converted from the charset value of the - /// Content-Type header. - /// - /// - /// if the charset value is not available. - /// - /// - public Encoding ContentEncoding { - get { - if (_contentEncoding == null) - _contentEncoding = getContentEncoding () ?? Encoding.UTF8; - - return _contentEncoding; - } - } - - /// - /// Gets the length in bytes of the entity body data included in the - /// request. - /// - /// - /// - /// A converted from the value of the Content-Length - /// header. - /// - /// - /// -1 if the header is not present. - /// - /// - public long ContentLength64 { - get { - return _contentLength; - } - } - - /// - /// Gets the media type of the entity body data included in the request. - /// - /// - /// - /// A that represents the value of the Content-Type - /// header. - /// - /// - /// if the header is not present. - /// - /// - public string ContentType { - get { - return _headers["Content-Type"]; - } - } - - /// - /// Gets the cookies included in the request. - /// - /// - /// - /// A that contains the cookies. - /// - /// - /// An empty collection if not included. - /// - /// - public CookieCollection Cookies { - get { - if (_cookies == null) - _cookies = _headers.GetCookies (false); - - return _cookies; - } - } - - /// - /// Gets a value indicating whether the request has the entity body data. - /// - /// - /// true if the request has the entity body data; otherwise, - /// false. - /// - public bool HasEntityBody { - get { - return _contentLength > 0 || _chunked; - } - } - - /// - /// Gets the headers included in the request. - /// - /// - /// A that contains the headers. - /// - public NameValueCollection Headers { - get { - return _headers; - } - } - - /// - /// Gets the HTTP method specified by the client. - /// - /// - /// A that represents the HTTP method specified in - /// the request line. - /// - public string HttpMethod { - get { - return _httpMethod; - } - } - - /// - /// Gets a stream that contains the entity body data included in - /// the request. - /// - /// - /// - /// A that contains the entity body data. - /// - /// - /// if the entity body data is not available. - /// - /// - public Stream InputStream { - get { - if (_inputStream == null) - _inputStream = getInputStream () ?? Stream.Null; - - return _inputStream; - } - } - - /// - /// Gets a value indicating whether the client is authenticated. - /// - /// - /// true if the client is authenticated; otherwise, false. - /// - public bool IsAuthenticated { - get { - return _context.User != null; - } - } - - /// - /// Gets a value indicating whether the request is sent from the local - /// computer. - /// - /// - /// true if the request is sent from the same computer as the server; - /// otherwise, false. - /// - public bool IsLocal { - get { - return _connection.IsLocal; - } - } - - /// - /// Gets a value indicating whether a secure connection is used to send - /// the request. - /// - /// - /// true if the connection is secure; otherwise, false. - /// - public bool IsSecureConnection { - get { - return _connection.IsSecure; - } - } - - /// - /// Gets a value indicating whether the request is a WebSocket handshake - /// request. - /// - /// - /// true if the request is a WebSocket handshake request; otherwise, - /// false. - /// - public bool IsWebSocketRequest { - get { - return _httpMethod == "GET" - && _protocolVersion > HttpVersion.Version10 - && _headers.Upgrades ("websocket"); - } - } - - /// - /// Gets a value indicating whether a persistent connection is requested. - /// - /// - /// true if the request specifies that the connection is kept open; - /// otherwise, false. - /// - public bool KeepAlive { - get { - return _headers.KeepsAlive (_protocolVersion); - } - } - - /// - /// Gets the endpoint to which the request is sent. - /// - /// - /// A that represents the server IP - /// address and port number. - /// - public System.Net.IPEndPoint LocalEndPoint { - get { - return _connection.LocalEndPoint; - } - } - - /// - /// Gets the HTTP version specified by the client. - /// - /// - /// A that represents the HTTP version specified in - /// the request line. - /// - public Version ProtocolVersion { - get { - return _protocolVersion; - } - } - - /// - /// Gets the query string included in the request. - /// - /// - /// - /// A that contains the query - /// parameters. - /// - /// - /// An empty collection if not included. - /// - /// - public NameValueCollection QueryString { - get { - if (_queryString == null) { - var url = Url; - _queryString = HttpUtility.InternalParseQueryString ( - url != null ? url.Query : null, - Encoding.UTF8 - ); - } - - return _queryString; - } - } - - /// - /// Gets the raw URL specified by the client. - /// - /// - /// A that represents the request target specified in - /// the request line. - /// - public string RawUrl { - get { - return _rawUrl; - } - } - - /// - /// Gets the endpoint from which the request is sent. - /// - /// - /// A that represents the client IP - /// address and port number. - /// - public System.Net.IPEndPoint RemoteEndPoint { - get { - return _connection.RemoteEndPoint; - } - } - - /// - /// Gets the trace identifier of the request. - /// - /// - /// A that represents the trace identifier. - /// - public Guid RequestTraceIdentifier { - get { - return _requestTraceIdentifier; - } - } - - /// - /// Gets the URL requested by the client. - /// - /// - /// - /// A that represents the URL parsed from the request. - /// - /// - /// if the URL cannot be parsed. - /// - /// - public Uri Url { - get { - if (!_urlSet) { - _url = HttpUtility.CreateRequestUrl ( - _rawUrl, - _userHostName ?? UserHostAddress, - IsWebSocketRequest, - IsSecureConnection - ); - - _urlSet = true; - } - - return _url; - } - } - - /// - /// Gets the URI of the resource from which the requested URL was obtained. - /// - /// - /// - /// A converted from the value of the Referer header. - /// - /// - /// if the header value is not available. - /// - /// - public Uri UrlReferrer { - get { - var val = _headers["Referer"]; - if (val == null) - return null; - - if (_urlReferrer == null) - _urlReferrer = val.ToUri (); - - return _urlReferrer; - } - } - - /// - /// Gets the user agent from which the request is originated. - /// - /// - /// - /// A that represents the value of the User-Agent - /// header. - /// - /// - /// if the header is not present. - /// - /// - public string UserAgent { - get { - return _headers["User-Agent"]; - } - } - - /// - /// Gets the IP address and port number to which the request is sent. - /// - /// - /// A that represents the server IP address and port - /// number. - /// - public string UserHostAddress { - get { - return _connection.LocalEndPoint.ToString (); - } - } - - /// - /// Gets the server host name requested by the client. - /// - /// - /// - /// A that represents the value of the Host header. - /// - /// - /// It includes the port number if provided. - /// - /// - /// if the header is not present. - /// - /// - public string UserHostName { - get { - return _userHostName; - } - } - /// - /// Gets the natural languages that are acceptable for the client. + /// Represents an incoming request to a instance. /// - /// - /// - /// An array of that contains the names of the - /// natural languages specified in the value of the Accept-Language - /// header. - /// - /// - /// if the header is not present. - /// - /// - public string[] UserLanguages { - get { - var val = _headers["Accept-Language"]; - if (val == null) - return null; - - if (_userLanguages == null) - _userLanguages = val.Split (',').Trim ().ToList ().ToArray (); - - return _userLanguages; - } - } - - #endregion - - #region Private Methods - - private void finishInitialization10 () + public sealed class HttpListenerRequest { - var transferEnc = _headers["Transfer-Encoding"]; - if (transferEnc != null) { - _context.ErrorMessage = "Invalid Transfer-Encoding header"; - return; - } + #region ֶ + + /// + /// Defines the _100continue + /// + private static readonly byte[] _100continue; + + /// + /// Defines the _acceptTypes + /// + private string[] _acceptTypes; + + /// + /// Defines the _chunked + /// + private bool _chunked; + + /// + /// Defines the _connection + /// + private HttpConnection _connection; + + /// + /// Defines the _contentEncoding + /// + private Encoding _contentEncoding; + + /// + /// Defines the _contentLength + /// + private long _contentLength; + + /// + /// Defines the _context + /// + private HttpListenerContext _context; + + /// + /// Defines the _cookies + /// + private CookieCollection _cookies; + + /// + /// Defines the _headers + /// + private WebHeaderCollection _headers; + + /// + /// Defines the _httpMethod + /// + private string _httpMethod; + + /// + /// Defines the _inputStream + /// + private Stream _inputStream; + + /// + /// Defines the _protocolVersion + /// + private Version _protocolVersion; + + /// + /// Defines the _queryString + /// + private NameValueCollection _queryString; + + /// + /// Defines the _rawUrl + /// + private string _rawUrl; + + /// + /// Defines the _requestTraceIdentifier + /// + private Guid _requestTraceIdentifier; + + /// + /// Defines the _url + /// + private Uri _url; + + /// + /// Defines the _urlReferrer + /// + private Uri _urlReferrer; + + /// + /// Defines the _urlSet + /// + private bool _urlSet; + + /// + /// Defines the _userHostName + /// + private string _userHostName; + + /// + /// Defines the _userLanguages + /// + private string[] _userLanguages; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The context + internal HttpListenerRequest(HttpListenerContext context) + { + _context = context; + + _connection = context.Connection; + _contentLength = -1; + _headers = new WebHeaderCollection(); + _requestTraceIdentifier = Guid.NewGuid(); + } - if (_httpMethod == "POST") { - if (_contentLength == -1) { - _context.ErrorMessage = "Content-Length header required"; - return; + /// + /// Initializes static members of the class. + /// + static HttpListenerRequest() + { + _100continue = Encoding.ASCII.GetBytes("HTTP/1.1 100 Continue\r\n\r\n"); } - if (_contentLength == 0) { - _context.ErrorMessage = "Invalid Content-Length header"; - return; + #endregion 캯 + + #region + + /// + /// Gets the media types that are acceptable for the client. + /// + public string[] AcceptTypes + { + get + { + var val = _headers["Accept"]; + if (val == null) + return null; + + if (_acceptTypes == null) + { + _acceptTypes = val + .SplitHeaderValue(',') + .Trim() + .ToList() + .ToArray(); + } + + return _acceptTypes; + } } - } - } - private Encoding getContentEncoding () - { - var val = _headers["Content-Type"]; - if (val == null) - return null; + /// + /// Gets the ClientCertificateError + /// Gets an error code that identifies a problem with the certificate + /// provided by the client. + /// + public int ClientCertificateError + { + get + { + throw new NotSupportedException(); + } + } - Encoding ret; - HttpUtility.TryGetEncoding (val, out ret); + /// + /// Gets the encoding for the entity body data included in the request. + /// + public Encoding ContentEncoding + { + get + { + if (_contentEncoding == null) + _contentEncoding = getContentEncoding() ?? Encoding.UTF8; + + return _contentEncoding; + } + } - return ret; - } + /// + /// Gets the length in bytes of the entity body data included in the + /// request. + /// + public long ContentLength64 + { + get + { + return _contentLength; + } + } - private RequestStream getInputStream () - { - return _contentLength > 0 || _chunked - ? _connection.GetRequestStream (_contentLength, _chunked) - : null; - } + /// + /// Gets the media type of the entity body data included in the request. + /// + public string ContentType + { + get + { + return _headers["Content-Type"]; + } + } - #endregion + /// + /// Gets the cookies included in the request. + /// + public CookieCollection Cookies + { + get + { + if (_cookies == null) + _cookies = _headers.GetCookies(false); + + return _cookies; + } + } - #region Internal Methods + /// + /// Gets a value indicating whether the request has the entity body data. + /// + public bool HasEntityBody + { + get + { + return _contentLength > 0 || _chunked; + } + } - internal void AddHeader (string headerField) - { - var start = headerField[0]; - if (start == ' ' || start == '\t') { - _context.ErrorMessage = "Invalid header field"; - return; - } + /// + /// Gets the headers included in the request. + /// + public NameValueCollection Headers + { + get + { + return _headers; + } + } - var colon = headerField.IndexOf (':'); - if (colon < 1) { - _context.ErrorMessage = "Invalid header field"; - return; - } + /// + /// Gets the HTTP method specified by the client. + /// + public string HttpMethod + { + get + { + return _httpMethod; + } + } - var name = headerField.Substring (0, colon).Trim (); - if (name.Length == 0 || !name.IsToken ()) { - _context.ErrorMessage = "Invalid header name"; - return; - } + /// + /// Gets the InputStream + /// Gets a stream that contains the entity body data included in + /// the request. + /// + public Stream InputStream + { + get + { + if (_inputStream == null) + _inputStream = getInputStream() ?? Stream.Null; + + return _inputStream; + } + } - var val = colon < headerField.Length - 1 - ? headerField.Substring (colon + 1).Trim () - : String.Empty; + /// + /// Gets a value indicating whether the client is authenticated. + /// + public bool IsAuthenticated + { + get + { + return _context.User != null; + } + } - _headers.InternalSet (name, val, false); + /// + /// Gets a value indicating whether the request is sent from the local + /// computer. + /// + public bool IsLocal + { + get + { + return _connection.IsLocal; + } + } - var lower = name.ToLower (CultureInfo.InvariantCulture); - if (lower == "host") { - if (_userHostName != null) { - _context.ErrorMessage = "Invalid Host header"; - return; + /// + /// Gets a value indicating whether a secure connection is used to send + /// the request. + /// + public bool IsSecureConnection + { + get + { + return _connection.IsSecure; + } } - if (val.Length == 0) { - _context.ErrorMessage = "Invalid Host header"; - return; + /// + /// Gets a value indicating whether the request is a WebSocket handshake + /// request. + /// + public bool IsWebSocketRequest + { + get + { + return _httpMethod == "GET" + && _protocolVersion > HttpVersion.Version10 + && _headers.Upgrades("websocket"); + } } - _userHostName = val; - return; - } + /// + /// Gets a value indicating whether a persistent connection is requested. + /// + public bool KeepAlive + { + get + { + return _headers.KeepsAlive(_protocolVersion); + } + } - if (lower == "content-length") { - if (_contentLength > -1) { - _context.ErrorMessage = "Invalid Content-Length header"; - return; + /// + /// Gets the endpoint to which the request is sent. + /// + public System.Net.IPEndPoint LocalEndPoint + { + get + { + return _connection.LocalEndPoint; + } } - long len; - if (!Int64.TryParse (val, out len)) { - _context.ErrorMessage = "Invalid Content-Length header"; - return; + /// + /// Gets the HTTP version specified by the client. + /// + public Version ProtocolVersion + { + get + { + return _protocolVersion; + } } - if (len < 0) { - _context.ErrorMessage = "Invalid Content-Length header"; - return; + /// + /// Gets the query string included in the request. + /// + public NameValueCollection QueryString + { + get + { + if (_queryString == null) + { + var url = Url; + _queryString = HttpUtility.InternalParseQueryString( + url != null ? url.Query : null, + Encoding.UTF8 + ); + } + + return _queryString; + } } - _contentLength = len; - return; - } - } + /// + /// Gets the raw URL specified by the client. + /// + public string RawUrl + { + get + { + return _rawUrl; + } + } - internal void FinishInitialization () - { - if (_protocolVersion == HttpVersion.Version10) { - finishInitialization10 (); - return; - } + /// + /// Gets the endpoint from which the request is sent. + /// + public System.Net.IPEndPoint RemoteEndPoint + { + get + { + return _connection.RemoteEndPoint; + } + } - if (_userHostName == null) { - _context.ErrorMessage = "Host header required"; - return; - } + /// + /// Gets the trace identifier of the request. + /// + public Guid RequestTraceIdentifier + { + get + { + return _requestTraceIdentifier; + } + } - var transferEnc = _headers["Transfer-Encoding"]; - if (transferEnc != null) { - var comparison = StringComparison.OrdinalIgnoreCase; - if (!transferEnc.Equals ("chunked", comparison)) { - _context.ErrorMessage = String.Empty; - _context.ErrorStatus = 501; + /// + /// Gets the URL requested by the client. + /// + public Uri Url + { + get + { + if (!_urlSet) + { + _url = HttpUtility.CreateRequestUrl( + _rawUrl, + _userHostName ?? UserHostAddress, + IsWebSocketRequest, + IsSecureConnection + ); + + _urlSet = true; + } + + return _url; + } + } - return; + /// + /// Gets the URI of the resource from which the requested URL was obtained. + /// + public Uri UrlReferrer + { + get + { + var val = _headers["Referer"]; + if (val == null) + return null; + + if (_urlReferrer == null) + _urlReferrer = val.ToUri(); + + return _urlReferrer; + } } - _chunked = true; - } + /// + /// Gets the user agent from which the request is originated. + /// + public string UserAgent + { + get + { + return _headers["User-Agent"]; + } + } - if (_httpMethod == "POST" || _httpMethod == "PUT") { - if (_contentLength <= 0 && !_chunked) { - _context.ErrorMessage = String.Empty; - _context.ErrorStatus = 411; + /// + /// Gets the IP address and port number to which the request is sent. + /// + public string UserHostAddress + { + get + { + return _connection.LocalEndPoint.ToString(); + } + } - return; + /// + /// Gets the server host name requested by the client. + /// + public string UserHostName + { + get + { + return _userHostName; + } } - } - var expect = _headers["Expect"]; - if (expect != null) { - var comparison = StringComparison.OrdinalIgnoreCase; - if (!expect.Equals ("100-continue", comparison)) { - _context.ErrorMessage = "Invalid Expect header"; - return; + /// + /// Gets the natural languages that are acceptable for the client. + /// + public string[] UserLanguages + { + get + { + var val = _headers["Accept-Language"]; + if (val == null) + return null; + + if (_userLanguages == null) + _userLanguages = val.Split(',').Trim().ToList().ToArray(); + + return _userLanguages; + } } - var output = _connection.GetResponseStream (); - output.InternalWrite (_100continue, 0, _100continue.Length); - } - } + #endregion - internal bool FlushInput () - { - var input = InputStream; - if (input == Stream.Null) - return true; + #region - var len = 2048; - if (_contentLength > 0 && _contentLength < len) - len = (int) _contentLength; + /// + /// Begins getting the certificate provided by the client asynchronously. + /// + /// The requestCallback + /// The state + /// The + public IAsyncResult BeginGetClientCertificate( + AsyncCallback requestCallback, object state + ) + { + throw new NotSupportedException(); + } - var buff = new byte[len]; + /// + /// Ends an asynchronous operation to get the certificate provided by the + /// client. + /// + /// The asyncResult + /// The + public X509Certificate2 EndGetClientCertificate(IAsyncResult asyncResult) + { + throw new NotSupportedException(); + } - while (true) { - try { - var ares = input.BeginRead (buff, 0, len, null, null); - if (!ares.IsCompleted) { - var timeout = 100; - if (!ares.AsyncWaitHandle.WaitOne (timeout)) - return false; - } + /// + /// Gets the certificate provided by the client. + /// + /// The + public X509Certificate2 GetClientCertificate() + { + throw new NotSupportedException(); + } - if (input.EndRead (ares) <= 0) - return true; + /// + /// Returns a string that represents the current instance. + /// + /// The + public override string ToString() + { + var buff = new StringBuilder(64); + + buff + .AppendFormat( + "{0} {1} HTTP/{2}\r\n", _httpMethod, _rawUrl, _protocolVersion + ) + .Append(_headers.ToString()); + + return buff.ToString(); } - catch { - return false; + + /// + /// The AddHeader + /// + /// The headerField + internal void AddHeader(string headerField) + { + var start = headerField[0]; + if (start == ' ' || start == '\t') + { + _context.ErrorMessage = "Invalid header field"; + return; + } + + var colon = headerField.IndexOf(':'); + if (colon < 1) + { + _context.ErrorMessage = "Invalid header field"; + return; + } + + var name = headerField.Substring(0, colon).Trim(); + if (name.Length == 0 || !name.IsToken()) + { + _context.ErrorMessage = "Invalid header name"; + return; + } + + var val = colon < headerField.Length - 1 + ? headerField.Substring(colon + 1).Trim() + : String.Empty; + + _headers.InternalSet(name, val, false); + + var lower = name.ToLower(CultureInfo.InvariantCulture); + if (lower == "host") + { + if (_userHostName != null) + { + _context.ErrorMessage = "Invalid Host header"; + return; + } + + if (val.Length == 0) + { + _context.ErrorMessage = "Invalid Host header"; + return; + } + + _userHostName = val; + return; + } + + if (lower == "content-length") + { + if (_contentLength > -1) + { + _context.ErrorMessage = "Invalid Content-Length header"; + return; + } + + long len; + if (!Int64.TryParse(val, out len)) + { + _context.ErrorMessage = "Invalid Content-Length header"; + return; + } + + if (len < 0) + { + _context.ErrorMessage = "Invalid Content-Length header"; + return; + } + + _contentLength = len; + return; + } } - } - } - internal bool IsUpgradeRequest (string protocol) - { - return _headers.Upgrades (protocol); - } + /// + /// The FinishInitialization + /// + internal void FinishInitialization() + { + if (_protocolVersion == HttpVersion.Version10) + { + finishInitialization10(); + return; + } + + if (_userHostName == null) + { + _context.ErrorMessage = "Host header required"; + return; + } + + var transferEnc = _headers["Transfer-Encoding"]; + if (transferEnc != null) + { + var comparison = StringComparison.OrdinalIgnoreCase; + if (!transferEnc.Equals("chunked", comparison)) + { + _context.ErrorMessage = String.Empty; + _context.ErrorStatus = 501; + + return; + } + + _chunked = true; + } + + if (_httpMethod == "POST" || _httpMethod == "PUT") + { + if (_contentLength <= 0 && !_chunked) + { + _context.ErrorMessage = String.Empty; + _context.ErrorStatus = 411; + + return; + } + } + + var expect = _headers["Expect"]; + if (expect != null) + { + var comparison = StringComparison.OrdinalIgnoreCase; + if (!expect.Equals("100-continue", comparison)) + { + _context.ErrorMessage = "Invalid Expect header"; + return; + } + + var output = _connection.GetResponseStream(); + output.InternalWrite(_100continue, 0, _100continue.Length); + } + } - internal void SetRequestLine (string requestLine) - { - var parts = requestLine.Split (new[] { ' ' }, 3); - if (parts.Length < 3) { - _context.ErrorMessage = "Invalid request line (parts)"; - return; - } - - var method = parts[0]; - if (method.Length == 0) { - _context.ErrorMessage = "Invalid request line (method)"; - return; - } - - var target = parts[1]; - if (target.Length == 0) { - _context.ErrorMessage = "Invalid request line (target)"; - return; - } - - var rawVer = parts[2]; - if (rawVer.Length != 8) { - _context.ErrorMessage = "Invalid request line (version)"; - return; - } - - if (rawVer.IndexOf ("HTTP/") != 0) { - _context.ErrorMessage = "Invalid request line (version)"; - return; - } - - Version ver; - if (!rawVer.Substring (5).TryCreateVersion (out ver)) { - _context.ErrorMessage = "Invalid request line (version)"; - return; - } - - if (ver.Major < 1) { - _context.ErrorMessage = "Invalid request line (version)"; - return; - } - - if (!method.IsHttpMethod (ver)) { - _context.ErrorMessage = "Invalid request line (method)"; - return; - } - - _httpMethod = method; - _rawUrl = target; - _protocolVersion = ver; - } + /// + /// The FlushInput + /// + /// The + internal bool FlushInput() + { + var input = InputStream; + if (input == Stream.Null) + return true; + + var len = 2048; + if (_contentLength > 0 && _contentLength < len) + len = (int)_contentLength; + + var buff = new byte[len]; + + while (true) + { + try + { + var ares = input.BeginRead(buff, 0, len, null, null); + if (!ares.IsCompleted) + { + var timeout = 100; + if (!ares.AsyncWaitHandle.WaitOne(timeout)) + return false; + } + + if (input.EndRead(ares) <= 0) + return true; + } + catch + { + return false; + } + } + } - #endregion + /// + /// The IsUpgradeRequest + /// + /// The protocol + /// The + internal bool IsUpgradeRequest(string protocol) + { + return _headers.Upgrades(protocol); + } - #region Public Methods + /// + /// The SetRequestLine + /// + /// The requestLine + internal void SetRequestLine(string requestLine) + { + var parts = requestLine.Split(new[] { ' ' }, 3); + if (parts.Length < 3) + { + _context.ErrorMessage = "Invalid request line (parts)"; + return; + } + + var method = parts[0]; + if (method.Length == 0) + { + _context.ErrorMessage = "Invalid request line (method)"; + return; + } + + var target = parts[1]; + if (target.Length == 0) + { + _context.ErrorMessage = "Invalid request line (target)"; + return; + } + + var rawVer = parts[2]; + if (rawVer.Length != 8) + { + _context.ErrorMessage = "Invalid request line (version)"; + return; + } + + if (rawVer.IndexOf("HTTP/") != 0) + { + _context.ErrorMessage = "Invalid request line (version)"; + return; + } + + Version ver; + if (!rawVer.Substring(5).TryCreateVersion(out ver)) + { + _context.ErrorMessage = "Invalid request line (version)"; + return; + } + + if (ver.Major < 1) + { + _context.ErrorMessage = "Invalid request line (version)"; + return; + } + + if (!method.IsHttpMethod(ver)) + { + _context.ErrorMessage = "Invalid request line (method)"; + return; + } + + _httpMethod = method; + _rawUrl = target; + _protocolVersion = ver; + } - /// - /// Begins getting the certificate provided by the client asynchronously. - /// - /// - /// An instance that indicates the status of the - /// operation. - /// - /// - /// An delegate that invokes the method called - /// when the operation is complete. - /// - /// - /// An that represents a user defined object to pass to - /// the callback delegate. - /// - /// - /// This method is not supported. - /// - public IAsyncResult BeginGetClientCertificate ( - AsyncCallback requestCallback, object state - ) - { - throw new NotSupportedException (); - } + /// + /// The finishInitialization10 + /// + private void finishInitialization10() + { + var transferEnc = _headers["Transfer-Encoding"]; + if (transferEnc != null) + { + _context.ErrorMessage = "Invalid Transfer-Encoding header"; + return; + } + + if (_httpMethod == "POST") + { + if (_contentLength == -1) + { + _context.ErrorMessage = "Content-Length header required"; + return; + } + + if (_contentLength == 0) + { + _context.ErrorMessage = "Invalid Content-Length header"; + return; + } + } + } - /// - /// Ends an asynchronous operation to get the certificate provided by the - /// client. - /// - /// - /// A that represents an X.509 certificate - /// provided by the client. - /// - /// - /// An instance returned when the operation - /// started. - /// - /// - /// This method is not supported. - /// - public X509Certificate2 EndGetClientCertificate (IAsyncResult asyncResult) - { - throw new NotSupportedException (); - } + /// + /// The getContentEncoding + /// + /// The + private Encoding getContentEncoding() + { + var val = _headers["Content-Type"]; + if (val == null) + return null; - /// - /// Gets the certificate provided by the client. - /// - /// - /// A that represents an X.509 certificate - /// provided by the client. - /// - /// - /// This method is not supported. - /// - public X509Certificate2 GetClientCertificate () - { - throw new NotSupportedException (); - } + Encoding ret; + HttpUtility.TryGetEncoding(val, out ret); - /// - /// Returns a string that represents the current instance. - /// - /// - /// A that contains the request line and headers - /// included in the request. - /// - public override string ToString () - { - var buff = new StringBuilder (64); + return ret; + } - buff - .AppendFormat ( - "{0} {1} HTTP/{2}\r\n", _httpMethod, _rawUrl, _protocolVersion - ) - .Append (_headers.ToString ()); + /// + /// The getInputStream + /// + /// The + private RequestStream getInputStream() + { + return _contentLength > 0 || _chunked + ? _connection.GetRequestStream(_contentLength, _chunked) + : null; + } - return buff.ToString (); + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/HttpListenerResponse.cs b/src/WebSocket/WebSocketCore/Net/HttpListenerResponse.cs index b8d340e8a..4c79a80f3 100644 --- a/src/WebSocket/WebSocketCore/Net/HttpListenerResponse.cs +++ b/src/WebSocket/WebSocketCore/Net/HttpListenerResponse.cs @@ -1,4 +1,3 @@ -#region License /* * HttpListenerResponse.cs * @@ -28,21 +27,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Gonzalo Paniagua Javier */ -#endregion -#region Contributors /* * Contributors: * - Nicholas Devenish */ -#endregion using System; using System.Collections.Generic; @@ -52,795 +46,707 @@ namespace WebSocketCore.Net { - /// - /// Provides the access to a response to a request received by the . - /// - /// - /// The HttpListenerResponse class cannot be inherited. - /// - public sealed class HttpListenerResponse : IDisposable - { - #region Private Fields - - private bool _closeConnection; - private Encoding _contentEncoding; - private long _contentLength; - private string _contentType; - private HttpListenerContext _context; - private CookieCollection _cookies; - private bool _disposed; - private WebHeaderCollection _headers; - private bool _headersSent; - private bool _keepAlive; - private string _location; - private ResponseStream _outputStream; - private bool _sendChunked; - private int _statusCode; - private string _statusDescription; - private Version _version; - - #endregion - - #region Internal Constructors - - internal HttpListenerResponse (HttpListenerContext context) - { - _context = context; - _keepAlive = true; - _statusCode = 200; - _statusDescription = "OK"; - _version = HttpVersion.Version11; - } - - #endregion - - #region Internal Properties - - internal bool CloseConnection { - get { - return _closeConnection; - } - - set { - _closeConnection = value; - } - } - - internal bool HeadersSent { - get { - return _headersSent; - } - - set { - _headersSent = value; - } - } - - #endregion - - #region Public Properties - - /// - /// Gets or sets the encoding for the entity body data included in the response. - /// - /// - /// A that represents the encoding for the entity body data, - /// or if no encoding is specified. - /// - /// - /// This object is closed. - /// - public Encoding ContentEncoding { - get { - return _contentEncoding; - } - - set { - checkDisposed (); - _contentEncoding = value; - } - } - - /// - /// Gets or sets the number of bytes in the entity body data included in the response. - /// - /// - /// A that represents the value of the Content-Length entity-header. - /// - /// - /// The value specified for a set operation is less than zero. - /// - /// - /// The response has already been sent. - /// - /// - /// This object is closed. - /// - public long ContentLength64 { - get { - return _contentLength; - } - - set { - checkDisposedOrHeadersSent (); - if (value < 0) - throw new ArgumentOutOfRangeException ("Less than zero.", "value"); - - _contentLength = value; - } - } - - /// - /// Gets or sets the media type of the entity body included in the response. - /// - /// - /// A that represents the media type of the entity body, - /// or if no media type is specified. This value is - /// used for the value of the Content-Type entity-header. - /// - /// - /// The value specified for a set operation is empty. - /// - /// - /// This object is closed. - /// - public string ContentType { - get { - return _contentType; - } - - set { - checkDisposed (); - if (value != null && value.Length == 0) - throw new ArgumentException ("An empty string.", "value"); - - _contentType = value; - } - } - - /// - /// Gets or sets the cookies sent with the response. - /// - /// - /// A that contains the cookies sent with the response. - /// - public CookieCollection Cookies { - get { - return _cookies ?? (_cookies = new CookieCollection ()); - } - - set { - _cookies = value; - } - } - - /// - /// Gets or sets the HTTP headers sent to the client. - /// - /// - /// A that contains the headers sent to the client. - /// - /// - /// The value specified for a set operation isn't valid for a response. - /// - public WebHeaderCollection Headers { - get { - return _headers ?? (_headers = new WebHeaderCollection (HttpHeaderType.Response, false)); - } - - set { - if (value != null && value.State != HttpHeaderType.Response) - throw new InvalidOperationException ( - "The specified headers aren't valid for a response."); - - _headers = value; - } - } - - /// - /// Gets or sets a value indicating whether the server requests a persistent connection. - /// - /// - /// true if the server requests a persistent connection; otherwise, false. - /// The default value is true. - /// - /// - /// The response has already been sent. - /// - /// - /// This object is closed. - /// - public bool KeepAlive { - get { - return _keepAlive; - } - - set { - checkDisposedOrHeadersSent (); - _keepAlive = value; - } - } - - /// - /// Gets a to use to write the entity body data. - /// - /// - /// A to use to write the entity body data. - /// - /// - /// This object is closed. - /// - public Stream OutputStream { - get { - checkDisposed (); - return _outputStream ?? (_outputStream = _context.Connection.GetResponseStream ()); - } - } - - /// - /// Gets or sets the HTTP version used in the response. - /// - /// - /// A that represents the version used in the response. - /// - /// - /// The value specified for a set operation is . - /// - /// - /// The value specified for a set operation doesn't have its Major property set to 1 or - /// doesn't have its Minor property set to either 0 or 1. - /// - /// - /// The response has already been sent. - /// - /// - /// This object is closed. - /// - public Version ProtocolVersion { - get { - return _version; - } - - set { - checkDisposedOrHeadersSent (); - if (value == null) - throw new ArgumentNullException ("value"); - - if (value.Major != 1 || (value.Minor != 0 && value.Minor != 1)) - throw new ArgumentException ("Not 1.0 or 1.1.", "value"); - - _version = value; - } - } - /// - /// Gets or sets the URL to which the client is redirected to locate a requested resource. + /// Provides the access to a response to a request received by the . /// - /// - /// A that represents the value of the Location response-header, - /// or if no redirect location is specified. - /// - /// - /// The value specified for a set operation isn't an absolute URL. - /// - /// - /// This object is closed. - /// - public string RedirectLocation { - get { - return _location; - } - - set { - checkDisposed (); - if (value == null) { - _location = null; - return; + public sealed class HttpListenerResponse : IDisposable + { + #region ֶ + + /// + /// Defines the _closeConnection + /// + private bool _closeConnection; + + /// + /// Defines the _contentEncoding + /// + private Encoding _contentEncoding; + + /// + /// Defines the _contentLength + /// + private long _contentLength; + + /// + /// Defines the _contentType + /// + private string _contentType; + + /// + /// Defines the _context + /// + private HttpListenerContext _context; + + /// + /// Defines the _cookies + /// + private CookieCollection _cookies; + + /// + /// Defines the _disposed + /// + private bool _disposed; + + /// + /// Defines the _headers + /// + private WebHeaderCollection _headers; + + /// + /// Defines the _headersSent + /// + private bool _headersSent; + + /// + /// Defines the _keepAlive + /// + private bool _keepAlive; + + /// + /// Defines the _location + /// + private string _location; + + /// + /// Defines the _outputStream + /// + private ResponseStream _outputStream; + + /// + /// Defines the _sendChunked + /// + private bool _sendChunked; + + /// + /// Defines the _statusCode + /// + private int _statusCode; + + /// + /// Defines the _statusDescription + /// + private string _statusDescription; + + /// + /// Defines the _version + /// + private Version _version; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The context + internal HttpListenerResponse(HttpListenerContext context) + { + _context = context; + _keepAlive = true; + _statusCode = 200; + _statusDescription = "OK"; + _version = HttpVersion.Version11; } - Uri uri = null; - if (!value.MaybeUri () || !Uri.TryCreate (value, UriKind.Absolute, out uri)) - throw new ArgumentException ("Not an absolute URL.", "value"); - - _location = value; - } - } - - /// - /// Gets or sets a value indicating whether the response uses the chunked transfer encoding. - /// - /// - /// true if the response uses the chunked transfer encoding; - /// otherwise, false. The default value is false. - /// - /// - /// The response has already been sent. - /// - /// - /// This object is closed. - /// - public bool SendChunked { - get { - return _sendChunked; - } - - set { - checkDisposedOrHeadersSent (); - _sendChunked = value; - } - } - - /// - /// Gets or sets the HTTP status code returned to the client. - /// - /// - /// An that represents the status code for the response to - /// the request. The default value is same as . - /// - /// - /// The response has already been sent. - /// - /// - /// This object is closed. - /// - /// - /// The value specified for a set operation is invalid. Valid values are - /// between 100 and 999 inclusive. - /// - public int StatusCode { - get { - return _statusCode; - } - - set { - checkDisposedOrHeadersSent (); - if (value < 100 || value > 999) - throw new System.Net.ProtocolViolationException ( - "A value isn't between 100 and 999 inclusive."); - - _statusCode = value; - _statusDescription = value.GetStatusDescription (); - } - } + #endregion 캯 + + #region + + /// + /// Gets or sets the encoding for the entity body data included in the response. + /// + public Encoding ContentEncoding + { + get + { + return _contentEncoding; + } + + set + { + checkDisposed(); + _contentEncoding = value; + } + } - /// - /// Gets or sets the description of the HTTP status code returned to the client. - /// - /// - /// A that represents the description of the status code. The default - /// value is the RFC 2616 - /// description for the property value, - /// or if an RFC 2616 description doesn't exist. - /// - /// - /// The value specified for a set operation contains invalid characters. - /// - /// - /// The response has already been sent. - /// - /// - /// This object is closed. - /// - public string StatusDescription { - get { - return _statusDescription; - } - - set { - checkDisposedOrHeadersSent (); - if (value == null || value.Length == 0) { - _statusDescription = _statusCode.GetStatusDescription (); - return; + /// + /// Gets or sets the number of bytes in the entity body data included in the response. + /// + public long ContentLength64 + { + get + { + return _contentLength; + } + + set + { + checkDisposedOrHeadersSent(); + if (value < 0) + throw new ArgumentOutOfRangeException("Less than zero.", "value"); + + _contentLength = value; + } } - if (!value.IsText () || value.IndexOfAny (new[] { '\r', '\n' }) > -1) - throw new ArgumentException ("Contains invalid characters.", "value"); + /// + /// Gets or sets the media type of the entity body included in the response. + /// + public string ContentType + { + get + { + return _contentType; + } + + set + { + checkDisposed(); + if (value != null && value.Length == 0) + throw new ArgumentException("An empty string.", "value"); + + _contentType = value; + } + } - _statusDescription = value; - } - } + /// + /// Gets or sets the cookies sent with the response. + /// + public CookieCollection Cookies + { + get + { + return _cookies ?? (_cookies = new CookieCollection()); + } + + set + { + _cookies = value; + } + } - #endregion + /// + /// Gets or sets the HTTP headers sent to the client. + /// + public WebHeaderCollection Headers + { + get + { + return _headers ?? (_headers = new WebHeaderCollection(HttpHeaderType.Response, false)); + } + + set + { + if (value != null && value.State != HttpHeaderType.Response) + throw new InvalidOperationException( + "The specified headers aren't valid for a response."); + + _headers = value; + } + } - #region Private Methods + /// + /// Gets or sets a value indicating whether the server requests a persistent connection. + /// + public bool KeepAlive + { + get + { + return _keepAlive; + } + + set + { + checkDisposedOrHeadersSent(); + _keepAlive = value; + } + } - private bool canAddOrUpdate (Cookie cookie) - { - if (_cookies == null || _cookies.Count == 0) - return true; + /// + /// Gets the OutputStream + /// Gets a to use to write the entity body data. + /// + public Stream OutputStream + { + get + { + checkDisposed(); + return _outputStream ?? (_outputStream = _context.Connection.GetResponseStream()); + } + } - var found = findCookie (cookie).ToList (); - if (found.Count == 0) - return true; + /// + /// Gets or sets the HTTP version used in the response. + /// + public Version ProtocolVersion + { + get + { + return _version; + } + + set + { + checkDisposedOrHeadersSent(); + if (value == null) + throw new ArgumentNullException("value"); + + if (value.Major != 1 || (value.Minor != 0 && value.Minor != 1)) + throw new ArgumentException("Not 1.0 or 1.1.", "value"); + + _version = value; + } + } - var ver = cookie.Version; - foreach (var c in found) - if (c.Version == ver) - return true; + /// + /// Gets or sets the URL to which the client is redirected to locate a requested resource. + /// + public string RedirectLocation + { + get + { + return _location; + } + + set + { + checkDisposed(); + if (value == null) + { + _location = null; + return; + } + + Uri uri = null; + if (!value.MaybeUri() || !Uri.TryCreate(value, UriKind.Absolute, out uri)) + throw new ArgumentException("Not an absolute URL.", "value"); + + _location = value; + } + } - return false; - } + /// + /// Gets or sets a value indicating whether the response uses the chunked transfer encoding. + /// + public bool SendChunked + { + get + { + return _sendChunked; + } + + set + { + checkDisposedOrHeadersSent(); + _sendChunked = value; + } + } - private void checkDisposed () - { - if (_disposed) - throw new ObjectDisposedException (GetType ().ToString ()); - } + /// + /// Gets or sets the HTTP status code returned to the client. + /// + public int StatusCode + { + get + { + return _statusCode; + } + + set + { + checkDisposedOrHeadersSent(); + if (value < 100 || value > 999) + throw new System.Net.ProtocolViolationException( + "A value isn't between 100 and 999 inclusive."); + + _statusCode = value; + _statusDescription = value.GetStatusDescription(); + } + } - private void checkDisposedOrHeadersSent () - { - if (_disposed) - throw new ObjectDisposedException (GetType ().ToString ()); + /// + /// Gets or sets the description of the HTTP status code returned to the client. + /// + public string StatusDescription + { + get + { + return _statusDescription; + } + + set + { + checkDisposedOrHeadersSent(); + if (value == null || value.Length == 0) + { + _statusDescription = _statusCode.GetStatusDescription(); + return; + } + + if (!value.IsText() || value.IndexOfAny(new[] { '\r', '\n' }) > -1) + throw new ArgumentException("Contains invalid characters.", "value"); + + _statusDescription = value; + } + } - if (_headersSent) - throw new InvalidOperationException ("Cannot be changed after the headers are sent."); - } + /// + /// Gets or sets a value indicating whether CloseConnection + /// + internal bool CloseConnection + { + get + { + return _closeConnection; + } + + set + { + _closeConnection = value; + } + } - private void close (bool force) - { - _disposed = true; - _context.Connection.Close (force); - } + /// + /// Gets or sets a value indicating whether HeadersSent + /// + internal bool HeadersSent + { + get + { + return _headersSent; + } + + set + { + _headersSent = value; + } + } - private IEnumerable findCookie (Cookie cookie) - { - var name = cookie.Name; - var domain = cookie.Domain; - var path = cookie.Path; - if (_cookies != null) - foreach (Cookie c in _cookies) - if (c.Name.Equals (name, StringComparison.OrdinalIgnoreCase) && - c.Domain.Equals (domain, StringComparison.OrdinalIgnoreCase) && - c.Path.Equals (path, StringComparison.Ordinal)) - yield return c; - } + #endregion - #endregion + #region - #region Internal Methods + /// + /// Closes the connection to the client without returning a response. + /// + public void Abort() + { + if (_disposed) + return; - internal WebHeaderCollection WriteHeadersTo (MemoryStream destination) - { - var headers = new WebHeaderCollection (HttpHeaderType.Response, true); - if (_headers != null) - headers.Add (_headers); - - if (_contentType != null) { - var type = _contentType.IndexOf ("charset=", StringComparison.Ordinal) == -1 && - _contentEncoding != null - ? String.Format ("{0}; charset={1}", _contentType, _contentEncoding.WebName) - : _contentType; - - headers.InternalSet ("Content-Type", type, true); - } - - if (headers["Server"] == null) - headers.InternalSet ("Server", "websocket-sharp/1.0", true); - - var prov = CultureInfo.InvariantCulture; - if (headers["Date"] == null) - headers.InternalSet ("Date", DateTime.UtcNow.ToString ("r", prov), true); - - if (!_sendChunked) - headers.InternalSet ("Content-Length", _contentLength.ToString (prov), true); - else - headers.InternalSet ("Transfer-Encoding", "chunked", true); - - /* - * Apache forces closing the connection for these status codes: - * - 400 Bad Request - * - 408 Request Timeout - * - 411 Length Required - * - 413 Request Entity Too Large - * - 414 Request-Uri Too Long - * - 500 Internal Server Error - * - 503 Service Unavailable - */ - var closeConn = !_context.Request.KeepAlive || - !_keepAlive || - _statusCode == 400 || - _statusCode == 408 || - _statusCode == 411 || - _statusCode == 413 || - _statusCode == 414 || - _statusCode == 500 || - _statusCode == 503; - - var reuses = _context.Connection.Reuses; - if (closeConn || reuses >= 100) { - headers.InternalSet ("Connection", "close", true); - } - else { - headers.InternalSet ( - "Keep-Alive", String.Format ("timeout=15,max={0}", 100 - reuses), true); - - if (_context.Request.ProtocolVersion < HttpVersion.Version11) - headers.InternalSet ("Connection", "keep-alive", true); - } - - if (_location != null) - headers.InternalSet ("Location", _location, true); - - if (_cookies != null) - foreach (Cookie cookie in _cookies) - headers.InternalSet ("Set-Cookie", cookie.ToResponseString (), true); - - var enc = _contentEncoding ?? Encoding.Default; - var writer = new StreamWriter (destination, enc, 256); - writer.Write ("HTTP/{0} {1} {2}\r\n", _version, _statusCode, _statusDescription); - writer.Write (headers.ToStringMultiValue (true)); - writer.Flush (); - - // Assumes that the destination was at position 0. - destination.Position = enc.GetPreamble ().Length; - - return headers; - } + close(true); + } - #endregion + /// + /// Adds an HTTP header with the specified and + /// to the headers for the response. + /// + /// The name + /// The value + public void AddHeader(string name, string value) + { + Headers.Set(name, value); + } - #region Public Methods + /// + /// Appends the specified to the cookies sent with the response. + /// + /// The cookie + public void AppendCookie(Cookie cookie) + { + Cookies.Add(cookie); + } - /// - /// Closes the connection to the client without returning a response. - /// - public void Abort () - { - if (_disposed) - return; + /// + /// Appends a to the specified HTTP header sent with the response. + /// + /// The name + /// The value + public void AppendHeader(string name, string value) + { + Headers.Add(name, value); + } - close (true); - } + /// + /// Returns the response to the client and releases the resources used by + /// this instance. + /// + public void Close() + { + if (_disposed) + return; - /// - /// Adds an HTTP header with the specified and - /// to the headers for the response. - /// - /// - /// A that represents the name of the header to add. - /// - /// - /// A that represents the value of the header to add. - /// - /// - /// is or empty. - /// - /// - /// - /// or contains invalid characters. - /// - /// - /// -or- - /// - /// - /// is a restricted header name. - /// - /// - /// - /// The length of is greater than 65,535 characters. - /// - /// - /// The header cannot be allowed to add to the current headers. - /// - public void AddHeader (string name, string value) - { - Headers.Set (name, value); - } + close(false); + } - /// - /// Appends the specified to the cookies sent with the response. - /// - /// - /// A to append. - /// - /// - /// is . - /// - public void AppendCookie (Cookie cookie) - { - Cookies.Add (cookie); - } + /// + /// Returns the response with the specified array of to the client and + /// releases the resources used by this instance. + /// + /// The responseEntity + /// The willBlock + public void Close(byte[] responseEntity, bool willBlock) + { + checkDisposed(); + if (responseEntity == null) + throw new ArgumentNullException("responseEntity"); + + var len = responseEntity.Length; + var output = OutputStream; + if (willBlock) + { + output.Write(responseEntity, 0, len); + close(false); + + return; + } + + output.BeginWrite( + responseEntity, + 0, + len, + ar => + { + output.EndWrite(ar); + close(false); + }, + null); + } - /// - /// Appends a to the specified HTTP header sent with the response. - /// - /// - /// A that represents the name of the header to append - /// to. - /// - /// - /// A that represents the value to append to the header. - /// - /// - /// is or empty. - /// - /// - /// - /// or contains invalid characters. - /// - /// - /// -or- - /// - /// - /// is a restricted header name. - /// - /// - /// - /// The length of is greater than 65,535 characters. - /// - /// - /// The current headers cannot allow the header to append a value. - /// - public void AppendHeader (string name, string value) - { - Headers.Add (name, value); - } + /// + /// Copies some properties from the specified to + /// this response. + /// + /// The templateResponse + public void CopyFrom(HttpListenerResponse templateResponse) + { + if (templateResponse == null) + throw new ArgumentNullException("templateResponse"); + + if (templateResponse._headers != null) + { + if (_headers != null) + _headers.Clear(); + + Headers.Add(templateResponse._headers); + } + else if (_headers != null) + { + _headers = null; + } + + _contentLength = templateResponse._contentLength; + _statusCode = templateResponse._statusCode; + _statusDescription = templateResponse._statusDescription; + _keepAlive = templateResponse._keepAlive; + _version = templateResponse._version; + } - /// - /// Returns the response to the client and releases the resources used by - /// this instance. - /// - public void Close () - { - if (_disposed) - return; + /// + /// Configures the response to redirect the client's request to + /// the specified . + /// + /// The url + public void Redirect(string url) + { + checkDisposedOrHeadersSent(); + if (url == null) + throw new ArgumentNullException("url"); + + Uri uri = null; + if (!url.MaybeUri() || !Uri.TryCreate(url, UriKind.Absolute, out uri)) + throw new ArgumentException("Not an absolute URL.", "url"); + + _location = url; + _statusCode = 302; + _statusDescription = "Found"; + } - close (false); - } + /// + /// Adds or updates a in the cookies sent with the response. + /// + /// The cookie + public void SetCookie(Cookie cookie) + { + if (cookie == null) + throw new ArgumentNullException("cookie"); - /// - /// Returns the response with the specified array of to the client and - /// releases the resources used by this instance. - /// - /// - /// An array of that contains the response entity body data. - /// - /// - /// true if this method blocks execution while flushing the stream to the client; - /// otherwise, false. - /// - /// - /// is . - /// - /// - /// This object is closed. - /// - public void Close (byte[] responseEntity, bool willBlock) - { - checkDisposed (); - if (responseEntity == null) - throw new ArgumentNullException ("responseEntity"); - - var len = responseEntity.Length; - var output = OutputStream; - if (willBlock) { - output.Write (responseEntity, 0, len); - close (false); - - return; - } - - output.BeginWrite ( - responseEntity, - 0, - len, - ar => { - output.EndWrite (ar); - close (false); - }, - null); - } + if (!canAddOrUpdate(cookie)) + throw new ArgumentException("Cannot be replaced.", "cookie"); - /// - /// Copies some properties from the specified to - /// this response. - /// - /// - /// A to copy. - /// - /// - /// is . - /// - public void CopyFrom (HttpListenerResponse templateResponse) - { - if (templateResponse == null) - throw new ArgumentNullException ("templateResponse"); - - if (templateResponse._headers != null) { - if (_headers != null) - _headers.Clear (); - - Headers.Add (templateResponse._headers); - } - else if (_headers != null) { - _headers = null; - } - - _contentLength = templateResponse._contentLength; - _statusCode = templateResponse._statusCode; - _statusDescription = templateResponse._statusDescription; - _keepAlive = templateResponse._keepAlive; - _version = templateResponse._version; - } + Cookies.Add(cookie); + } - /// - /// Configures the response to redirect the client's request to - /// the specified . - /// - /// - /// This method sets the property to - /// , the property to - /// 302, and the property to - /// "Found". - /// - /// - /// A that represents the URL to redirect the client's request to. - /// - /// - /// is . - /// - /// - /// isn't an absolute URL. - /// - /// - /// The response has already been sent. - /// - /// - /// This object is closed. - /// - public void Redirect (string url) - { - checkDisposedOrHeadersSent (); - if (url == null) - throw new ArgumentNullException ("url"); + /// + /// The WriteHeadersTo + /// + /// The destination + /// The + internal WebHeaderCollection WriteHeadersTo(MemoryStream destination) + { + var headers = new WebHeaderCollection(HttpHeaderType.Response, true); + if (_headers != null) + headers.Add(_headers); + + if (_contentType != null) + { + var type = _contentType.IndexOf("charset=", StringComparison.Ordinal) == -1 && + _contentEncoding != null + ? String.Format("{0}; charset={1}", _contentType, _contentEncoding.WebName) + : _contentType; + + headers.InternalSet("Content-Type", type, true); + } + + if (headers["Server"] == null) + headers.InternalSet("Server", "websocket-sharp/1.0", true); + + var prov = CultureInfo.InvariantCulture; + if (headers["Date"] == null) + headers.InternalSet("Date", DateTime.UtcNow.ToString("r", prov), true); + + if (!_sendChunked) + headers.InternalSet("Content-Length", _contentLength.ToString(prov), true); + else + headers.InternalSet("Transfer-Encoding", "chunked", true); + + /* + * Apache forces closing the connection for these status codes: + * - 400 Bad Request + * - 408 Request Timeout + * - 411 Length Required + * - 413 Request Entity Too Large + * - 414 Request-Uri Too Long + * - 500 Internal Server Error + * - 503 Service Unavailable + */ + var closeConn = !_context.Request.KeepAlive || + !_keepAlive || + _statusCode == 400 || + _statusCode == 408 || + _statusCode == 411 || + _statusCode == 413 || + _statusCode == 414 || + _statusCode == 500 || + _statusCode == 503; + + var reuses = _context.Connection.Reuses; + if (closeConn || reuses >= 100) + { + headers.InternalSet("Connection", "close", true); + } + else + { + headers.InternalSet( + "Keep-Alive", String.Format("timeout=15,max={0}", 100 - reuses), true); + + if (_context.Request.ProtocolVersion < HttpVersion.Version11) + headers.InternalSet("Connection", "keep-alive", true); + } + + if (_location != null) + headers.InternalSet("Location", _location, true); + + if (_cookies != null) + foreach (Cookie cookie in _cookies) + headers.InternalSet("Set-Cookie", cookie.ToResponseString(), true); + + var enc = _contentEncoding ?? Encoding.Default; + var writer = new StreamWriter(destination, enc, 256); + writer.Write("HTTP/{0} {1} {2}\r\n", _version, _statusCode, _statusDescription); + writer.Write(headers.ToStringMultiValue(true)); + writer.Flush(); + + // Assumes that the destination was at position 0. + destination.Position = enc.GetPreamble().Length; + + return headers; + } - Uri uri = null; - if (!url.MaybeUri () || !Uri.TryCreate (url, UriKind.Absolute, out uri)) - throw new ArgumentException ("Not an absolute URL.", "url"); + /// + /// The canAddOrUpdate + /// + /// The cookie + /// The + private bool canAddOrUpdate(Cookie cookie) + { + if (_cookies == null || _cookies.Count == 0) + return true; + + var found = findCookie(cookie).ToList(); + if (found.Count == 0) + return true; + + var ver = cookie.Version; + foreach (var c in found) + if (c.Version == ver) + return true; + + return false; + } - _location = url; - _statusCode = 302; - _statusDescription = "Found"; - } + /// + /// The checkDisposed + /// + private void checkDisposed() + { + if (_disposed) + throw new ObjectDisposedException(GetType().ToString()); + } - /// - /// Adds or updates a in the cookies sent with the response. - /// - /// - /// A to set. - /// - /// - /// is . - /// - /// - /// already exists in the cookies and couldn't be replaced. - /// - public void SetCookie (Cookie cookie) - { - if (cookie == null) - throw new ArgumentNullException ("cookie"); + /// + /// The checkDisposedOrHeadersSent + /// + private void checkDisposedOrHeadersSent() + { + if (_disposed) + throw new ObjectDisposedException(GetType().ToString()); - if (!canAddOrUpdate (cookie)) - throw new ArgumentException ("Cannot be replaced.", "cookie"); + if (_headersSent) + throw new InvalidOperationException("Cannot be changed after the headers are sent."); + } - Cookies.Add (cookie); - } + /// + /// The close + /// + /// The force + private void close(bool force) + { + _disposed = true; + _context.Connection.Close(force); + } - #endregion + /// + /// The findCookie + /// + /// The cookie + /// The + private IEnumerable findCookie(Cookie cookie) + { + var name = cookie.Name; + var domain = cookie.Domain; + var path = cookie.Path; + if (_cookies != null) + foreach (Cookie c in _cookies) + if (c.Name.Equals(name, StringComparison.OrdinalIgnoreCase) && + c.Domain.Equals(domain, StringComparison.OrdinalIgnoreCase) && + c.Path.Equals(path, StringComparison.Ordinal)) + yield return c; + } - #region Explicit Interface Implementations + /// + /// Releases all resources used by the . + /// + void IDisposable.Dispose() + { + if (_disposed) + return; - /// - /// Releases all resources used by the . - /// - void IDisposable.Dispose () - { - if (_disposed) - return; + close(true); // Same as the Abort method. + } - close (true); // Same as the Abort method. + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/HttpRequestHeader.cs b/src/WebSocket/WebSocketCore/Net/HttpRequestHeader.cs index a6bead02a..dbeb884d3 100644 --- a/src/WebSocket/WebSocketCore/Net/HttpRequestHeader.cs +++ b/src/WebSocket/WebSocketCore/Net/HttpRequestHeader.cs @@ -1,4 +1,3 @@ -#region License /* * HttpRequestHeader.cs * @@ -28,206 +27,251 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Gonzalo Paniagua Javier */ -#endregion namespace WebSocketCore.Net { - /// - /// Contains the HTTP headers that may be specified in a client request. - /// - /// - /// The HttpRequestHeader enumeration contains the HTTP request headers defined in - /// RFC 2616 for the HTTP/1.1 and - /// RFC 6455 for the WebSocket. - /// - public enum HttpRequestHeader - { - /// - /// Indicates the Cache-Control header. - /// - CacheControl, - /// - /// Indicates the Connection header. - /// - Connection, - /// - /// Indicates the Date header. - /// - Date, - /// - /// Indicates the Keep-Alive header. - /// - KeepAlive, - /// - /// Indicates the Pragma header. - /// - Pragma, - /// - /// Indicates the Trailer header. - /// - Trailer, - /// - /// Indicates the Transfer-Encoding header. - /// - TransferEncoding, - /// - /// Indicates the Upgrade header. - /// - Upgrade, - /// - /// Indicates the Via header. - /// - Via, - /// - /// Indicates the Warning header. - /// - Warning, - /// - /// Indicates the Allow header. - /// - Allow, - /// - /// Indicates the Content-Length header. - /// - ContentLength, - /// - /// Indicates the Content-Type header. - /// - ContentType, - /// - /// Indicates the Content-Encoding header. - /// - ContentEncoding, - /// - /// Indicates the Content-Language header. - /// - ContentLanguage, - /// - /// Indicates the Content-Location header. - /// - ContentLocation, - /// - /// Indicates the Content-MD5 header. - /// - ContentMd5, - /// - /// Indicates the Content-Range header. - /// - ContentRange, - /// - /// Indicates the Expires header. - /// - Expires, - /// - /// Indicates the Last-Modified header. - /// - LastModified, - /// - /// Indicates the Accept header. - /// - Accept, - /// - /// Indicates the Accept-Charset header. - /// - AcceptCharset, - /// - /// Indicates the Accept-Encoding header. - /// - AcceptEncoding, - /// - /// Indicates the Accept-Language header. - /// - AcceptLanguage, - /// - /// Indicates the Authorization header. - /// - Authorization, - /// - /// Indicates the Cookie header. - /// - Cookie, - /// - /// Indicates the Expect header. - /// - Expect, - /// - /// Indicates the From header. - /// - From, - /// - /// Indicates the Host header. - /// - Host, - /// - /// Indicates the If-Match header. - /// - IfMatch, - /// - /// Indicates the If-Modified-Since header. - /// - IfModifiedSince, - /// - /// Indicates the If-None-Match header. - /// - IfNoneMatch, - /// - /// Indicates the If-Range header. - /// - IfRange, - /// - /// Indicates the If-Unmodified-Since header. - /// - IfUnmodifiedSince, - /// - /// Indicates the Max-Forwards header. - /// - MaxForwards, - /// - /// Indicates the Proxy-Authorization header. - /// - ProxyAuthorization, - /// - /// Indicates the Referer header. - /// - Referer, - /// - /// Indicates the Range header. - /// - Range, - /// - /// Indicates the TE header. - /// - Te, - /// - /// Indicates the Translate header. - /// - Translate, - /// - /// Indicates the User-Agent header. - /// - UserAgent, - /// - /// Indicates the Sec-WebSocket-Key header. - /// - SecWebSocketKey, - /// - /// Indicates the Sec-WebSocket-Extensions header. - /// - SecWebSocketExtensions, - /// - /// Indicates the Sec-WebSocket-Protocol header. - /// - SecWebSocketProtocol, + #region ö + /// - /// Indicates the Sec-WebSocket-Version header. - /// - SecWebSocketVersion - } -} + /// Contains the HTTP headers that may be specified in a client request. + ///
+ /// + /// The HttpRequestHeader enumeration contains the HTTP request headers defined in + /// RFC 2616 for the HTTP/1.1 and + /// RFC 6455 for the WebSocket. + /// + public enum HttpRequestHeader + { + /// + /// Indicates the Cache-Control header. + /// + CacheControl, + + /// + /// Indicates the Connection header. + /// + Connection, + + /// + /// Indicates the Date header. + /// + Date, + + /// + /// Indicates the Keep-Alive header. + /// + KeepAlive, + + /// + /// Indicates the Pragma header. + /// + Pragma, + + /// + /// Indicates the Trailer header. + /// + Trailer, + + /// + /// Indicates the Transfer-Encoding header. + /// + TransferEncoding, + + /// + /// Indicates the Upgrade header. + /// + Upgrade, + + /// + /// Indicates the Via header. + /// + Via, + + /// + /// Indicates the Warning header. + /// + Warning, + + /// + /// Indicates the Allow header. + /// + Allow, + + /// + /// Indicates the Content-Length header. + /// + ContentLength, + + /// + /// Indicates the Content-Type header. + /// + ContentType, + + /// + /// Indicates the Content-Encoding header. + /// + ContentEncoding, + + /// + /// Indicates the Content-Language header. + /// + ContentLanguage, + + /// + /// Indicates the Content-Location header. + /// + ContentLocation, + + /// + /// Indicates the Content-MD5 header. + /// + ContentMd5, + + /// + /// Indicates the Content-Range header. + /// + ContentRange, + + /// + /// Indicates the Expires header. + /// + Expires, + + /// + /// Indicates the Last-Modified header. + /// + LastModified, + + /// + /// Indicates the Accept header. + /// + Accept, + + /// + /// Indicates the Accept-Charset header. + /// + AcceptCharset, + + /// + /// Indicates the Accept-Encoding header. + /// + AcceptEncoding, + + /// + /// Indicates the Accept-Language header. + /// + AcceptLanguage, + + /// + /// Indicates the Authorization header. + /// + Authorization, + + /// + /// Indicates the Cookie header. + /// + Cookie, + + /// + /// Indicates the Expect header. + /// + Expect, + + /// + /// Indicates the From header. + /// + From, + + /// + /// Indicates the Host header. + /// + Host, + + /// + /// Indicates the If-Match header. + /// + IfMatch, + + /// + /// Indicates the If-Modified-Since header. + /// + IfModifiedSince, + + /// + /// Indicates the If-None-Match header. + /// + IfNoneMatch, + + /// + /// Indicates the If-Range header. + /// + IfRange, + + /// + /// Indicates the If-Unmodified-Since header. + /// + IfUnmodifiedSince, + + /// + /// Indicates the Max-Forwards header. + /// + MaxForwards, + + /// + /// Indicates the Proxy-Authorization header. + /// + ProxyAuthorization, + + /// + /// Indicates the Referer header. + /// + Referer, + + /// + /// Indicates the Range header. + /// + Range, + + /// + /// Indicates the TE header. + /// + Te, + + /// + /// Indicates the Translate header. + /// + Translate, + + /// + /// Indicates the User-Agent header. + /// + UserAgent, + + /// + /// Indicates the Sec-WebSocket-Key header. + /// + SecWebSocketKey, + + /// + /// Indicates the Sec-WebSocket-Extensions header. + /// + SecWebSocketExtensions, + + /// + /// Indicates the Sec-WebSocket-Protocol header. + /// + SecWebSocketProtocol, + + /// + /// Indicates the Sec-WebSocket-Version header. + /// + SecWebSocketVersion + } + + #endregion ö +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/HttpResponseHeader.cs b/src/WebSocket/WebSocketCore/Net/HttpResponseHeader.cs index 7eef77970..bf122870d 100644 --- a/src/WebSocket/WebSocketCore/Net/HttpResponseHeader.cs +++ b/src/WebSocket/WebSocketCore/Net/HttpResponseHeader.cs @@ -1,4 +1,3 @@ -#region License /* * HttpResponseHeader.cs * @@ -28,162 +27,196 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Gonzalo Paniagua Javier */ -#endregion namespace WebSocketCore.Net { - /// - /// Contains the HTTP headers that can be specified in a server response. - /// - /// - /// The HttpResponseHeader enumeration contains the HTTP response headers defined in - /// RFC 2616 for the HTTP/1.1 and - /// RFC 6455 for the WebSocket. - /// - public enum HttpResponseHeader - { - /// - /// Indicates the Cache-Control header. - /// - CacheControl, - /// - /// Indicates the Connection header. - /// - Connection, - /// - /// Indicates the Date header. - /// - Date, - /// - /// Indicates the Keep-Alive header. - /// - KeepAlive, - /// - /// Indicates the Pragma header. - /// - Pragma, - /// - /// Indicates the Trailer header. - /// - Trailer, - /// - /// Indicates the Transfer-Encoding header. - /// - TransferEncoding, - /// - /// Indicates the Upgrade header. - /// - Upgrade, - /// - /// Indicates the Via header. - /// - Via, - /// - /// Indicates the Warning header. - /// - Warning, - /// - /// Indicates the Allow header. - /// - Allow, - /// - /// Indicates the Content-Length header. - /// - ContentLength, - /// - /// Indicates the Content-Type header. - /// - ContentType, - /// - /// Indicates the Content-Encoding header. - /// - ContentEncoding, - /// - /// Indicates the Content-Language header. - /// - ContentLanguage, - /// - /// Indicates the Content-Location header. - /// - ContentLocation, - /// - /// Indicates the Content-MD5 header. - /// - ContentMd5, - /// - /// Indicates the Content-Range header. - /// - ContentRange, - /// - /// Indicates the Expires header. - /// - Expires, - /// - /// Indicates the Last-Modified header. - /// - LastModified, - /// - /// Indicates the Accept-Ranges header. - /// - AcceptRanges, - /// - /// Indicates the Age header. - /// - Age, - /// - /// Indicates the ETag header. - /// - ETag, - /// - /// Indicates the Location header. - /// - Location, - /// - /// Indicates the Proxy-Authenticate header. - /// - ProxyAuthenticate, - /// - /// Indicates the Retry-After header. - /// - RetryAfter, - /// - /// Indicates the Server header. - /// - Server, - /// - /// Indicates the Set-Cookie header. - /// - SetCookie, - /// - /// Indicates the Vary header. - /// - Vary, - /// - /// Indicates the WWW-Authenticate header. - /// - WwwAuthenticate, - /// - /// Indicates the Sec-WebSocket-Extensions header. - /// - SecWebSocketExtensions, - /// - /// Indicates the Sec-WebSocket-Accept header. - /// - SecWebSocketAccept, - /// - /// Indicates the Sec-WebSocket-Protocol header. - /// - SecWebSocketProtocol, + #region ö + /// - /// Indicates the Sec-WebSocket-Version header. - /// - SecWebSocketVersion - } -} + /// Contains the HTTP headers that can be specified in a server response. + /// + /// + /// The HttpResponseHeader enumeration contains the HTTP response headers defined in + /// RFC 2616 for the HTTP/1.1 and + /// RFC 6455 for the WebSocket. + /// + public enum HttpResponseHeader + { + /// + /// Indicates the Cache-Control header. + /// + CacheControl, + + /// + /// Indicates the Connection header. + /// + Connection, + + /// + /// Indicates the Date header. + /// + Date, + + /// + /// Indicates the Keep-Alive header. + /// + KeepAlive, + + /// + /// Indicates the Pragma header. + /// + Pragma, + + /// + /// Indicates the Trailer header. + /// + Trailer, + + /// + /// Indicates the Transfer-Encoding header. + /// + TransferEncoding, + + /// + /// Indicates the Upgrade header. + /// + Upgrade, + + /// + /// Indicates the Via header. + /// + Via, + + /// + /// Indicates the Warning header. + /// + Warning, + + /// + /// Indicates the Allow header. + /// + Allow, + + /// + /// Indicates the Content-Length header. + /// + ContentLength, + + /// + /// Indicates the Content-Type header. + /// + ContentType, + + /// + /// Indicates the Content-Encoding header. + /// + ContentEncoding, + + /// + /// Indicates the Content-Language header. + /// + ContentLanguage, + + /// + /// Indicates the Content-Location header. + /// + ContentLocation, + + /// + /// Indicates the Content-MD5 header. + /// + ContentMd5, + + /// + /// Indicates the Content-Range header. + /// + ContentRange, + + /// + /// Indicates the Expires header. + /// + Expires, + + /// + /// Indicates the Last-Modified header. + /// + LastModified, + + /// + /// Indicates the Accept-Ranges header. + /// + AcceptRanges, + + /// + /// Indicates the Age header. + /// + Age, + + /// + /// Indicates the ETag header. + /// + ETag, + + /// + /// Indicates the Location header. + /// + Location, + + /// + /// Indicates the Proxy-Authenticate header. + /// + ProxyAuthenticate, + + /// + /// Indicates the Retry-After header. + /// + RetryAfter, + + /// + /// Indicates the Server header. + /// + Server, + + /// + /// Indicates the Set-Cookie header. + /// + SetCookie, + + /// + /// Indicates the Vary header. + /// + Vary, + + /// + /// Indicates the WWW-Authenticate header. + /// + WwwAuthenticate, + + /// + /// Indicates the Sec-WebSocket-Extensions header. + /// + SecWebSocketExtensions, + + /// + /// Indicates the Sec-WebSocket-Accept header. + /// + SecWebSocketAccept, + + /// + /// Indicates the Sec-WebSocket-Protocol header. + /// + SecWebSocketProtocol, + + /// + /// Indicates the Sec-WebSocket-Version header. + /// + SecWebSocketVersion + } + + #endregion ö +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/HttpStatusCode.cs b/src/WebSocket/WebSocketCore/Net/HttpStatusCode.cs index 0cc4e366f..83d848899 100644 --- a/src/WebSocket/WebSocketCore/Net/HttpStatusCode.cs +++ b/src/WebSocket/WebSocketCore/Net/HttpStatusCode.cs @@ -1,4 +1,3 @@ -#region License /* * HttpStatusCode.cs * @@ -34,326 +33,374 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion namespace WebSocketCore.Net { - /// - /// Contains the values of the HTTP status codes. - /// - /// - /// The HttpStatusCode enumeration contains the values of the HTTP status codes defined in - /// RFC 2616 for the HTTP/1.1. - /// - public enum HttpStatusCode - { - /// - /// Equivalent to status code 100. - /// Indicates that the client should continue with its request. - /// - Continue = 100, - /// - /// Equivalent to status code 101. - /// Indicates that the server is switching the HTTP version or protocol on the connection. - /// - SwitchingProtocols = 101, - /// - /// Equivalent to status code 200. - /// Indicates that the client's request has succeeded. - /// - OK = 200, - /// - /// Equivalent to status code 201. - /// Indicates that the client's request has been fulfilled and resulted in a new resource being - /// created. - /// - Created = 201, - /// - /// Equivalent to status code 202. - /// Indicates that the client's request has been accepted for processing, but the processing - /// hasn't been completed. - /// - Accepted = 202, - /// - /// Equivalent to status code 203. - /// Indicates that the returned metainformation is from a local or a third-party copy instead of - /// the origin server. - /// - NonAuthoritativeInformation = 203, - /// - /// Equivalent to status code 204. - /// Indicates that the server has fulfilled the client's request but doesn't need to return - /// an entity-body. - /// - NoContent = 204, - /// - /// Equivalent to status code 205. - /// Indicates that the server has fulfilled the client's request, and the user agent should - /// reset the document view which caused the request to be sent. - /// - ResetContent = 205, - /// - /// Equivalent to status code 206. - /// Indicates that the server has fulfilled the partial GET request for the resource. - /// - PartialContent = 206, - /// - /// - /// Equivalent to status code 300. - /// Indicates that the requested resource corresponds to any of multiple representations. - /// - /// - /// MultipleChoices is a synonym for Ambiguous. - /// - /// - MultipleChoices = 300, - /// - /// - /// Equivalent to status code 300. - /// Indicates that the requested resource corresponds to any of multiple representations. - /// - /// - /// Ambiguous is a synonym for MultipleChoices. - /// - /// - Ambiguous = 300, - /// - /// - /// Equivalent to status code 301. - /// Indicates that the requested resource has been assigned a new permanent URI and - /// any future references to this resource should use one of the returned URIs. - /// - /// - /// MovedPermanently is a synonym for Moved. - /// - /// - MovedPermanently = 301, - /// - /// - /// Equivalent to status code 301. - /// Indicates that the requested resource has been assigned a new permanent URI and - /// any future references to this resource should use one of the returned URIs. - /// - /// - /// Moved is a synonym for MovedPermanently. - /// - /// - Moved = 301, - /// - /// - /// Equivalent to status code 302. - /// Indicates that the requested resource is located temporarily under a different URI. - /// - /// - /// Found is a synonym for Redirect. - /// - /// - Found = 302, - /// - /// - /// Equivalent to status code 302. - /// Indicates that the requested resource is located temporarily under a different URI. - /// - /// - /// Redirect is a synonym for Found. - /// - /// - Redirect = 302, - /// - /// - /// Equivalent to status code 303. - /// Indicates that the response to the request can be found under a different URI and - /// should be retrieved using a GET method on that resource. - /// - /// - /// SeeOther is a synonym for RedirectMethod. - /// - /// - SeeOther = 303, - /// - /// - /// Equivalent to status code 303. - /// Indicates that the response to the request can be found under a different URI and - /// should be retrieved using a GET method on that resource. - /// - /// - /// RedirectMethod is a synonym for SeeOther. - /// - /// - RedirectMethod = 303, - /// - /// Equivalent to status code 304. - /// Indicates that the client has performed a conditional GET request and access is allowed, - /// but the document hasn't been modified. - /// - NotModified = 304, - /// - /// Equivalent to status code 305. - /// Indicates that the requested resource must be accessed through the proxy given by - /// the Location field. - /// - UseProxy = 305, - /// - /// Equivalent to status code 306. - /// This status code was used in a previous version of the specification, is no longer used, - /// and is reserved for future use. - /// - Unused = 306, - /// - /// - /// Equivalent to status code 307. - /// Indicates that the requested resource is located temporarily under a different URI. - /// - /// - /// TemporaryRedirect is a synonym for RedirectKeepVerb. - /// - /// - TemporaryRedirect = 307, - /// - /// - /// Equivalent to status code 307. - /// Indicates that the requested resource is located temporarily under a different URI. - /// - /// - /// RedirectKeepVerb is a synonym for TemporaryRedirect. - /// - /// - RedirectKeepVerb = 307, - /// - /// Equivalent to status code 400. - /// Indicates that the client's request couldn't be understood by the server due to - /// malformed syntax. - /// - BadRequest = 400, - /// - /// Equivalent to status code 401. - /// Indicates that the client's request requires user authentication. - /// - Unauthorized = 401, - /// - /// Equivalent to status code 402. - /// This status code is reserved for future use. - /// - PaymentRequired = 402, - /// - /// Equivalent to status code 403. - /// Indicates that the server understood the client's request but is refusing to fulfill it. - /// - Forbidden = 403, - /// - /// Equivalent to status code 404. - /// Indicates that the server hasn't found anything matching the request URI. - /// - NotFound = 404, - /// - /// Equivalent to status code 405. - /// Indicates that the method specified in the request line isn't allowed for the resource - /// identified by the request URI. - /// - MethodNotAllowed = 405, - /// - /// Equivalent to status code 406. - /// Indicates that the server doesn't have the appropriate resource to respond to the Accept - /// headers in the client's request. - /// - NotAcceptable = 406, - /// - /// Equivalent to status code 407. - /// Indicates that the client must first authenticate itself with the proxy. - /// - ProxyAuthenticationRequired = 407, - /// - /// Equivalent to status code 408. - /// Indicates that the client didn't produce a request within the time that the server was - /// prepared to wait. - /// - RequestTimeout = 408, - /// - /// Equivalent to status code 409. - /// Indicates that the client's request couldn't be completed due to a conflict on the server. - /// - Conflict = 409, - /// - /// Equivalent to status code 410. - /// Indicates that the requested resource is no longer available at the server and - /// no forwarding address is known. - /// - Gone = 410, - /// - /// Equivalent to status code 411. - /// Indicates that the server refuses to accept the client's request without a defined - /// Content-Length. - /// - LengthRequired = 411, - /// - /// Equivalent to status code 412. - /// Indicates that the precondition given in one or more of the request headers evaluated to - /// false when it was tested on the server. - /// - PreconditionFailed = 412, - /// - /// Equivalent to status code 413. - /// Indicates that the entity of the client's request is larger than the server is willing or - /// able to process. - /// - RequestEntityTooLarge = 413, - /// - /// Equivalent to status code 414. - /// Indicates that the request URI is longer than the server is willing to interpret. - /// - RequestUriTooLong = 414, - /// - /// Equivalent to status code 415. - /// Indicates that the entity of the client's request is in a format not supported by - /// the requested resource for the requested method. - /// - UnsupportedMediaType = 415, - /// - /// Equivalent to status code 416. - /// Indicates that none of the range specifier values in a Range request header overlap - /// the current extent of the selected resource. - /// - RequestedRangeNotSatisfiable = 416, - /// - /// Equivalent to status code 417. - /// Indicates that the expectation given in an Expect request header couldn't be met by - /// the server. - /// - ExpectationFailed = 417, - /// - /// Equivalent to status code 500. - /// Indicates that the server encountered an unexpected condition which prevented it from - /// fulfilling the client's request. - /// - InternalServerError = 500, - /// - /// Equivalent to status code 501. - /// Indicates that the server doesn't support the functionality required to fulfill the client's - /// request. - /// - NotImplemented = 501, - /// - /// Equivalent to status code 502. - /// Indicates that a gateway or proxy server received an invalid response from the upstream - /// server. - /// - BadGateway = 502, - /// - /// Equivalent to status code 503. - /// Indicates that the server is currently unable to handle the client's request due to - /// a temporary overloading or maintenance of the server. - /// - ServiceUnavailable = 503, - /// - /// Equivalent to status code 504. - /// Indicates that a gateway or proxy server didn't receive a timely response from the upstream - /// server or some other auxiliary server. - /// - GatewayTimeout = 504, + #region ö + /// - /// Equivalent to status code 505. - /// Indicates that the server doesn't support the HTTP version used in the client's request. - /// - HttpVersionNotSupported = 505, - } -} + /// Contains the values of the HTTP status codes. + /// + /// + /// The HttpStatusCode enumeration contains the values of the HTTP status codes defined in + /// RFC 2616 for the HTTP/1.1. + /// + public enum HttpStatusCode + { + /// + /// Equivalent to status code 100. + /// Indicates that the client should continue with its request. + /// + Continue = 100, + + /// + /// Equivalent to status code 101. + /// Indicates that the server is switching the HTTP version or protocol on the connection. + /// + SwitchingProtocols = 101, + + /// + /// Equivalent to status code 200. + /// Indicates that the client's request has succeeded. + /// + OK = 200, + + /// + /// Equivalent to status code 201. + /// Indicates that the client's request has been fulfilled and resulted in a new resource being + /// created. + /// + Created = 201, + + /// + /// Equivalent to status code 202. + /// Indicates that the client's request has been accepted for processing, but the processing + /// hasn't been completed. + /// + Accepted = 202, + + /// + /// Equivalent to status code 203. + /// Indicates that the returned metainformation is from a local or a third-party copy instead of + /// the origin server. + /// + NonAuthoritativeInformation = 203, + + /// + /// Equivalent to status code 204. + /// Indicates that the server has fulfilled the client's request but doesn't need to return + /// an entity-body. + /// + NoContent = 204, + + /// + /// Equivalent to status code 205. + /// Indicates that the server has fulfilled the client's request, and the user agent should + /// reset the document view which caused the request to be sent. + /// + ResetContent = 205, + + /// + /// Equivalent to status code 206. + /// Indicates that the server has fulfilled the partial GET request for the resource. + /// + PartialContent = 206, + + /// + /// + /// Equivalent to status code 300. + /// Indicates that the requested resource corresponds to any of multiple representations. + /// + /// + /// MultipleChoices is a synonym for Ambiguous. + /// + /// + MultipleChoices = 300, + + /// + /// + /// Equivalent to status code 300. + /// Indicates that the requested resource corresponds to any of multiple representations. + /// + /// + /// Ambiguous is a synonym for MultipleChoices. + /// + /// + Ambiguous = 300, + + /// + /// + /// Equivalent to status code 301. + /// Indicates that the requested resource has been assigned a new permanent URI and + /// any future references to this resource should use one of the returned URIs. + /// + /// + /// MovedPermanently is a synonym for Moved. + /// + /// + MovedPermanently = 301, + + /// + /// + /// Equivalent to status code 301. + /// Indicates that the requested resource has been assigned a new permanent URI and + /// any future references to this resource should use one of the returned URIs. + /// + /// + /// Moved is a synonym for MovedPermanently. + /// + /// + Moved = 301, + + /// + /// + /// Equivalent to status code 302. + /// Indicates that the requested resource is located temporarily under a different URI. + /// + /// + /// Found is a synonym for Redirect. + /// + /// + Found = 302, + + /// + /// + /// Equivalent to status code 302. + /// Indicates that the requested resource is located temporarily under a different URI. + /// + /// + /// Redirect is a synonym for Found. + /// + /// + Redirect = 302, + + /// + /// + /// Equivalent to status code 303. + /// Indicates that the response to the request can be found under a different URI and + /// should be retrieved using a GET method on that resource. + /// + /// + /// SeeOther is a synonym for RedirectMethod. + /// + /// + SeeOther = 303, + + /// + /// + /// Equivalent to status code 303. + /// Indicates that the response to the request can be found under a different URI and + /// should be retrieved using a GET method on that resource. + /// + /// + /// RedirectMethod is a synonym for SeeOther. + /// + /// + RedirectMethod = 303, + + /// + /// Equivalent to status code 304. + /// Indicates that the client has performed a conditional GET request and access is allowed, + /// but the document hasn't been modified. + /// + NotModified = 304, + + /// + /// Equivalent to status code 305. + /// Indicates that the requested resource must be accessed through the proxy given by + /// the Location field. + /// + UseProxy = 305, + + /// + /// Equivalent to status code 306. + /// This status code was used in a previous version of the specification, is no longer used, + /// and is reserved for future use. + /// + Unused = 306, + + /// + /// + /// Equivalent to status code 307. + /// Indicates that the requested resource is located temporarily under a different URI. + /// + /// + /// TemporaryRedirect is a synonym for RedirectKeepVerb. + /// + /// + TemporaryRedirect = 307, + + /// + /// + /// Equivalent to status code 307. + /// Indicates that the requested resource is located temporarily under a different URI. + /// + /// + /// RedirectKeepVerb is a synonym for TemporaryRedirect. + /// + /// + RedirectKeepVerb = 307, + + /// + /// Equivalent to status code 400. + /// Indicates that the client's request couldn't be understood by the server due to + /// malformed syntax. + /// + BadRequest = 400, + + /// + /// Equivalent to status code 401. + /// Indicates that the client's request requires user authentication. + /// + Unauthorized = 401, + + /// + /// Equivalent to status code 402. + /// This status code is reserved for future use. + /// + PaymentRequired = 402, + + /// + /// Equivalent to status code 403. + /// Indicates that the server understood the client's request but is refusing to fulfill it. + /// + Forbidden = 403, + + /// + /// Equivalent to status code 404. + /// Indicates that the server hasn't found anything matching the request URI. + /// + NotFound = 404, + + /// + /// Equivalent to status code 405. + /// Indicates that the method specified in the request line isn't allowed for the resource + /// identified by the request URI. + /// + MethodNotAllowed = 405, + + /// + /// Equivalent to status code 406. + /// Indicates that the server doesn't have the appropriate resource to respond to the Accept + /// headers in the client's request. + /// + NotAcceptable = 406, + + /// + /// Equivalent to status code 407. + /// Indicates that the client must first authenticate itself with the proxy. + /// + ProxyAuthenticationRequired = 407, + + /// + /// Equivalent to status code 408. + /// Indicates that the client didn't produce a request within the time that the server was + /// prepared to wait. + /// + RequestTimeout = 408, + + /// + /// Equivalent to status code 409. + /// Indicates that the client's request couldn't be completed due to a conflict on the server. + /// + Conflict = 409, + + /// + /// Equivalent to status code 410. + /// Indicates that the requested resource is no longer available at the server and + /// no forwarding address is known. + /// + Gone = 410, + + /// + /// Equivalent to status code 411. + /// Indicates that the server refuses to accept the client's request without a defined + /// Content-Length. + /// + LengthRequired = 411, + + /// + /// Equivalent to status code 412. + /// Indicates that the precondition given in one or more of the request headers evaluated to + /// false when it was tested on the server. + /// + PreconditionFailed = 412, + + /// + /// Equivalent to status code 413. + /// Indicates that the entity of the client's request is larger than the server is willing or + /// able to process. + /// + RequestEntityTooLarge = 413, + + /// + /// Equivalent to status code 414. + /// Indicates that the request URI is longer than the server is willing to interpret. + /// + RequestUriTooLong = 414, + + /// + /// Equivalent to status code 415. + /// Indicates that the entity of the client's request is in a format not supported by + /// the requested resource for the requested method. + /// + UnsupportedMediaType = 415, + + /// + /// Equivalent to status code 416. + /// Indicates that none of the range specifier values in a Range request header overlap + /// the current extent of the selected resource. + /// + RequestedRangeNotSatisfiable = 416, + + /// + /// Equivalent to status code 417. + /// Indicates that the expectation given in an Expect request header couldn't be met by + /// the server. + /// + ExpectationFailed = 417, + + /// + /// Equivalent to status code 500. + /// Indicates that the server encountered an unexpected condition which prevented it from + /// fulfilling the client's request. + /// + InternalServerError = 500, + + /// + /// Equivalent to status code 501. + /// Indicates that the server doesn't support the functionality required to fulfill the client's + /// request. + /// + NotImplemented = 501, + + /// + /// Equivalent to status code 502. + /// Indicates that a gateway or proxy server received an invalid response from the upstream + /// server. + /// + BadGateway = 502, + + /// + /// Equivalent to status code 503. + /// Indicates that the server is currently unable to handle the client's request due to + /// a temporary overloading or maintenance of the server. + /// + ServiceUnavailable = 503, + + /// + /// Equivalent to status code 504. + /// Indicates that a gateway or proxy server didn't receive a timely response from the upstream + /// server or some other auxiliary server. + /// + GatewayTimeout = 504, + + /// + /// Equivalent to status code 505. + /// Indicates that the server doesn't support the HTTP version used in the client's request. + /// + HttpVersionNotSupported = 505, + } + + #endregion ö +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/HttpStreamAsyncResult.cs b/src/WebSocket/WebSocketCore/Net/HttpStreamAsyncResult.cs index ce3d2914e..fda10b48d 100644 --- a/src/WebSocket/WebSocketCore/Net/HttpStreamAsyncResult.cs +++ b/src/WebSocket/WebSocketCore/Net/HttpStreamAsyncResult.cs @@ -1,4 +1,3 @@ -#region License /* * HttpStreamAsyncResult.cs * @@ -28,157 +27,259 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Gonzalo Paniagua Javier */ -#endregion using System; using System.Threading; namespace WebSocketCore.Net { - internal class HttpStreamAsyncResult : IAsyncResult - { - #region Private Fields - - private byte[] _buffer; - private AsyncCallback _callback; - private bool _completed; - private int _count; - private Exception _exception; - private int _offset; - private object _state; - private object _sync; - private int _syncRead; - private ManualResetEvent _waitHandle; - - #endregion - - #region Internal Constructors - - internal HttpStreamAsyncResult (AsyncCallback callback, object state) + /// + /// Defines the + /// + internal class HttpStreamAsyncResult : IAsyncResult { - _callback = callback; - _state = state; - _sync = new object (); + #region ֶ + + /// + /// Defines the _buffer + /// + private byte[] _buffer; + + /// + /// Defines the _callback + /// + private AsyncCallback _callback; + + /// + /// Defines the _completed + /// + private bool _completed; + + /// + /// Defines the _count + /// + private int _count; + + /// + /// Defines the _exception + /// + private Exception _exception; + + /// + /// Defines the _offset + /// + private int _offset; + + /// + /// Defines the _state + /// + private object _state; + + /// + /// Defines the _sync + /// + private object _sync; + + /// + /// Defines the _syncRead + /// + private int _syncRead; + + /// + /// Defines the _waitHandle + /// + private ManualResetEvent _waitHandle; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The callback + /// The state + internal HttpStreamAsyncResult(AsyncCallback callback, object state) + { + _callback = callback; + _state = state; + _sync = new object(); + } + + #endregion 캯 + + #region + + /// + /// Gets the AsyncState + /// + public object AsyncState + { + get + { + return _state; + } + } + + /// + /// Gets the AsyncWaitHandle + /// + public WaitHandle AsyncWaitHandle + { + get + { + lock (_sync) + return _waitHandle ?? (_waitHandle = new ManualResetEvent(_completed)); + } + } + + /// + /// Gets a value indicating whether CompletedSynchronously + /// + public bool CompletedSynchronously + { + get + { + return _syncRead == _count; + } + } + + /// + /// Gets a value indicating whether IsCompleted + /// + public bool IsCompleted + { + get + { + lock (_sync) + return _completed; + } + } + + /// + /// Gets or sets the Buffer + /// + internal byte[] Buffer + { + get + { + return _buffer; + } + + set + { + _buffer = value; + } + } + + /// + /// Gets or sets the Count + /// + internal int Count + { + get + { + return _count; + } + + set + { + _count = value; + } + } + + /// + /// Gets the Exception + /// + internal Exception Exception + { + get + { + return _exception; + } + } + + /// + /// Gets a value indicating whether HasException + /// + internal bool HasException + { + get + { + return _exception != null; + } + } + + /// + /// Gets or sets the Offset + /// + internal int Offset + { + get + { + return _offset; + } + + set + { + _offset = value; + } + } + + /// + /// Gets or sets the SyncRead + /// + internal int SyncRead + { + get + { + return _syncRead; + } + + set + { + _syncRead = value; + } + } + + #endregion + + #region + + /// + /// The Complete + /// + internal void Complete() + { + lock (_sync) + { + if (_completed) + return; + + _completed = true; + if (_waitHandle != null) + _waitHandle.Set(); + + if (_callback != null) + _callback.BeginInvoke(this, ar => _callback.EndInvoke(ar), null); + } + } + + /// + /// The Complete + /// + /// The exception + internal void Complete(Exception exception) + { + _exception = exception; + Complete(); + } + + #endregion } - - #endregion - - #region Internal Properties - - internal byte[] Buffer { - get { - return _buffer; - } - - set { - _buffer = value; - } - } - - internal int Count { - get { - return _count; - } - - set { - _count = value; - } - } - - internal Exception Exception { - get { - return _exception; - } - } - - internal bool HasException { - get { - return _exception != null; - } - } - - internal int Offset { - get { - return _offset; - } - - set { - _offset = value; - } - } - - internal int SyncRead { - get { - return _syncRead; - } - - set { - _syncRead = value; - } - } - - #endregion - - #region Public Properties - - public object AsyncState { - get { - return _state; - } - } - - public WaitHandle AsyncWaitHandle { - get { - lock (_sync) - return _waitHandle ?? (_waitHandle = new ManualResetEvent (_completed)); - } - } - - public bool CompletedSynchronously { - get { - return _syncRead == _count; - } - } - - public bool IsCompleted { - get { - lock (_sync) - return _completed; - } - } - - #endregion - - #region Internal Methods - - internal void Complete () - { - lock (_sync) { - if (_completed) - return; - - _completed = true; - if (_waitHandle != null) - _waitHandle.Set (); - - if (_callback != null) - _callback.BeginInvoke (this, ar => _callback.EndInvoke (ar), null); - } - } - - internal void Complete (Exception exception) - { - _exception = exception; - Complete (); - } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/HttpUtility.cs b/src/WebSocket/WebSocketCore/Net/HttpUtility.cs index d842f2b0b..fe1c14a2d 100644 --- a/src/WebSocket/WebSocketCore/Net/HttpUtility.cs +++ b/src/WebSocket/WebSocketCore/Net/HttpUtility.cs @@ -1,4 +1,3 @@ -#region License /* * HttpUtility.cs * @@ -28,9 +27,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Patrik Torstensson @@ -38,7 +35,6 @@ * - Tim Coleman * - Gonzalo Paniagua Javier */ -#endregion using System; using System.Collections; @@ -51,1238 +47,1555 @@ namespace WebSocketCore.Net { - internal sealed class HttpUtility - { - #region Private Fields - - private static Dictionary _entities; - private static char[] _hexChars = "0123456789abcdef".ToCharArray (); - private static object _sync = new object (); - - #endregion - - #region Private Methods - - private static int getChar (byte[] bytes, int offset, int length) + /// + /// Defines the + /// + internal sealed class HttpUtility { - var val = 0; - var end = length + offset; - for (var i = offset; i < end; i++) { - var current = getInt (bytes[i]); - if (current == -1) - return -1; + #region 字段 + + /// + /// Defines the _entities + /// + private static Dictionary _entities; + + /// + /// Defines the _hexChars + /// + private static char[] _hexChars = "0123456789abcdef".ToCharArray(); + + /// + /// Defines the _sync + /// + private static object _sync = new object(); + + #endregion 字段 + + #region 方法 + + /// + /// The HtmlAttributeEncode + /// + /// The s + /// The + public static string HtmlAttributeEncode(string s) + { + if (s == null || s.Length == 0 || !s.Contains('&', '"', '<', '>')) + return s; + + var output = new StringBuilder(); + foreach (var c in s) + output.Append( + c == '&' + ? "&" + : c == '"' + ? """ + : c == '<' + ? "<" + : c == '>' + ? ">" + : c.ToString()); + + return output.ToString(); + } - val = (val << 4) + current; - } + /// + /// The HtmlAttributeEncode + /// + /// The s + /// The output + public static void HtmlAttributeEncode(string s, TextWriter output) + { + if (output == null) + throw new ArgumentNullException("output"); + + output.Write(HtmlAttributeEncode(s)); + } - return val; - } + /// + /// Decodes an HTML-encoded and returns the decoded . + /// + /// The s + /// The + public static string HtmlDecode(string s) + { + if (s == null || s.Length == 0 || !s.Contains('&')) + return s; + + var entity = new StringBuilder(); + var output = new StringBuilder(); + + // 0 -> nothing, + // 1 -> right after '&' + // 2 -> between '&' and ';' but no '#' + // 3 -> '#' found after '&' and getting numbers + var state = 0; + + var number = 0; + var haveTrailingDigits = false; + foreach (var c in s) + { + if (state == 0) + { + if (c == '&') + { + entity.Append(c); + state = 1; + } + else + { + output.Append(c); + } + + continue; + } + + if (c == '&') + { + state = 1; + if (haveTrailingDigits) + { + entity.Append(number.ToString(CultureInfo.InvariantCulture)); + haveTrailingDigits = false; + } + + output.Append(entity.ToString()); + entity.Length = 0; + entity.Append('&'); + + continue; + } + + if (state == 1) + { + if (c == ';') + { + state = 0; + output.Append(entity.ToString()); + output.Append(c); + entity.Length = 0; + } + else + { + number = 0; + if (c != '#') + state = 2; + else + state = 3; + + entity.Append(c); + } + } + else if (state == 2) + { + entity.Append(c); + if (c == ';') + { + var key = entity.ToString(); + var entities = getEntities(); + if (key.Length > 1 && entities.ContainsKey(key.Substring(1, key.Length - 2))) + key = entities[key.Substring(1, key.Length - 2)].ToString(); + + output.Append(key); + state = 0; + entity.Length = 0; + } + } + else if (state == 3) + { + if (c == ';') + { + if (number > 65535) + { + output.Append("&#"); + output.Append(number.ToString(CultureInfo.InvariantCulture)); + output.Append(";"); + } + else + { + output.Append((char)number); + } + + state = 0; + entity.Length = 0; + haveTrailingDigits = false; + } + else if (Char.IsDigit(c)) + { + number = number * 10 + ((int)c - '0'); + haveTrailingDigits = true; + } + else + { + state = 2; + if (haveTrailingDigits) + { + entity.Append(number.ToString(CultureInfo.InvariantCulture)); + haveTrailingDigits = false; + } + + entity.Append(c); + } + } + } - private static int getChar (string s, int offset, int length) - { - var val = 0; - var end = length + offset; - for (var i = offset; i < end; i++) { - var c = s[i]; - if (c > 127) - return -1; + if (entity.Length > 0) + output.Append(entity.ToString()); + else if (haveTrailingDigits) + output.Append(number.ToString(CultureInfo.InvariantCulture)); - var current = getInt ((byte) c); - if (current == -1) - return -1; + return output.ToString(); + } - val = (val << 4) + current; - } + /// + /// Decodes an HTML-encoded and sends the decoded + /// to the specified . + /// + /// The s + /// The output + public static void HtmlDecode(string s, TextWriter output) + { + if (output == null) + throw new ArgumentNullException("output"); + + output.Write(HtmlDecode(s)); + } - return val; - } + /// + /// HTML-encodes a and returns the encoded . + /// + /// The s + /// The + public static string HtmlEncode(string s) + { + if (s == null || s.Length == 0) + return s; + + var needEncode = false; + foreach (var c in s) + { + if (c == '&' || c == '"' || c == '<' || c == '>' || c > 159) + { + needEncode = true; + break; + } + } - private static char[] getChars (MemoryStream buffer, Encoding encoding) - { - return encoding.GetChars (buffer.GetBuffer (), 0, (int) buffer.Length); - } + if (!needEncode) + return s; + + var output = new StringBuilder(); + foreach (var c in s) + { + if (c == '&') + { + output.Append("&"); + } + else if (c == '"') + { + output.Append("""); + } + else if (c == '<') + { + output.Append("<"); + } + else if (c == '>') + { + output.Append(">"); + } + else if (c > 159) + { + // MS starts encoding with &# from 160 and stops at 255. + // We don't do that. One reason is the 65308/65310 unicode + // characters that look like '<' and '>'. + output.Append("&#"); + output.Append(((int)c).ToString(CultureInfo.InvariantCulture)); + output.Append(";"); + } + else + { + output.Append(c); + } + } - private static Dictionary getEntities () - { - lock (_sync) { - if (_entities == null) - initEntities (); + return output.ToString(); + } - return _entities; - } - } + /// + /// HTML-encodes a and sends the encoded + /// to the specified . + /// + /// The s + /// The output + public static void HtmlEncode(string s, TextWriter output) + { + if (output == null) + throw new ArgumentNullException("output"); + + output.Write(HtmlEncode(s)); + } - private static int getInt (byte b) - { - var c = (char) b; - return c >= '0' && c <= '9' - ? c - '0' - : c >= 'a' && c <= 'f' - ? c - 'a' + 10 - : c >= 'A' && c <= 'F' - ? c - 'A' + 10 - : -1; - } + /// + /// The ParseQueryString + /// + /// The query + /// The + public static NameValueCollection ParseQueryString(string query) + { + return ParseQueryString(query, Encoding.UTF8); + } - private static void initEntities () - { - // Build the dictionary of HTML entity references. - // This list comes from the HTML 4.01 W3C recommendation. - _entities = new Dictionary (); - _entities.Add ("nbsp", '\u00A0'); - _entities.Add ("iexcl", '\u00A1'); - _entities.Add ("cent", '\u00A2'); - _entities.Add ("pound", '\u00A3'); - _entities.Add ("curren", '\u00A4'); - _entities.Add ("yen", '\u00A5'); - _entities.Add ("brvbar", '\u00A6'); - _entities.Add ("sect", '\u00A7'); - _entities.Add ("uml", '\u00A8'); - _entities.Add ("copy", '\u00A9'); - _entities.Add ("ordf", '\u00AA'); - _entities.Add ("laquo", '\u00AB'); - _entities.Add ("not", '\u00AC'); - _entities.Add ("shy", '\u00AD'); - _entities.Add ("reg", '\u00AE'); - _entities.Add ("macr", '\u00AF'); - _entities.Add ("deg", '\u00B0'); - _entities.Add ("plusmn", '\u00B1'); - _entities.Add ("sup2", '\u00B2'); - _entities.Add ("sup3", '\u00B3'); - _entities.Add ("acute", '\u00B4'); - _entities.Add ("micro", '\u00B5'); - _entities.Add ("para", '\u00B6'); - _entities.Add ("middot", '\u00B7'); - _entities.Add ("cedil", '\u00B8'); - _entities.Add ("sup1", '\u00B9'); - _entities.Add ("ordm", '\u00BA'); - _entities.Add ("raquo", '\u00BB'); - _entities.Add ("frac14", '\u00BC'); - _entities.Add ("frac12", '\u00BD'); - _entities.Add ("frac34", '\u00BE'); - _entities.Add ("iquest", '\u00BF'); - _entities.Add ("Agrave", '\u00C0'); - _entities.Add ("Aacute", '\u00C1'); - _entities.Add ("Acirc", '\u00C2'); - _entities.Add ("Atilde", '\u00C3'); - _entities.Add ("Auml", '\u00C4'); - _entities.Add ("Aring", '\u00C5'); - _entities.Add ("AElig", '\u00C6'); - _entities.Add ("Ccedil", '\u00C7'); - _entities.Add ("Egrave", '\u00C8'); - _entities.Add ("Eacute", '\u00C9'); - _entities.Add ("Ecirc", '\u00CA'); - _entities.Add ("Euml", '\u00CB'); - _entities.Add ("Igrave", '\u00CC'); - _entities.Add ("Iacute", '\u00CD'); - _entities.Add ("Icirc", '\u00CE'); - _entities.Add ("Iuml", '\u00CF'); - _entities.Add ("ETH", '\u00D0'); - _entities.Add ("Ntilde", '\u00D1'); - _entities.Add ("Ograve", '\u00D2'); - _entities.Add ("Oacute", '\u00D3'); - _entities.Add ("Ocirc", '\u00D4'); - _entities.Add ("Otilde", '\u00D5'); - _entities.Add ("Ouml", '\u00D6'); - _entities.Add ("times", '\u00D7'); - _entities.Add ("Oslash", '\u00D8'); - _entities.Add ("Ugrave", '\u00D9'); - _entities.Add ("Uacute", '\u00DA'); - _entities.Add ("Ucirc", '\u00DB'); - _entities.Add ("Uuml", '\u00DC'); - _entities.Add ("Yacute", '\u00DD'); - _entities.Add ("THORN", '\u00DE'); - _entities.Add ("szlig", '\u00DF'); - _entities.Add ("agrave", '\u00E0'); - _entities.Add ("aacute", '\u00E1'); - _entities.Add ("acirc", '\u00E2'); - _entities.Add ("atilde", '\u00E3'); - _entities.Add ("auml", '\u00E4'); - _entities.Add ("aring", '\u00E5'); - _entities.Add ("aelig", '\u00E6'); - _entities.Add ("ccedil", '\u00E7'); - _entities.Add ("egrave", '\u00E8'); - _entities.Add ("eacute", '\u00E9'); - _entities.Add ("ecirc", '\u00EA'); - _entities.Add ("euml", '\u00EB'); - _entities.Add ("igrave", '\u00EC'); - _entities.Add ("iacute", '\u00ED'); - _entities.Add ("icirc", '\u00EE'); - _entities.Add ("iuml", '\u00EF'); - _entities.Add ("eth", '\u00F0'); - _entities.Add ("ntilde", '\u00F1'); - _entities.Add ("ograve", '\u00F2'); - _entities.Add ("oacute", '\u00F3'); - _entities.Add ("ocirc", '\u00F4'); - _entities.Add ("otilde", '\u00F5'); - _entities.Add ("ouml", '\u00F6'); - _entities.Add ("divide", '\u00F7'); - _entities.Add ("oslash", '\u00F8'); - _entities.Add ("ugrave", '\u00F9'); - _entities.Add ("uacute", '\u00FA'); - _entities.Add ("ucirc", '\u00FB'); - _entities.Add ("uuml", '\u00FC'); - _entities.Add ("yacute", '\u00FD'); - _entities.Add ("thorn", '\u00FE'); - _entities.Add ("yuml", '\u00FF'); - _entities.Add ("fnof", '\u0192'); - _entities.Add ("Alpha", '\u0391'); - _entities.Add ("Beta", '\u0392'); - _entities.Add ("Gamma", '\u0393'); - _entities.Add ("Delta", '\u0394'); - _entities.Add ("Epsilon", '\u0395'); - _entities.Add ("Zeta", '\u0396'); - _entities.Add ("Eta", '\u0397'); - _entities.Add ("Theta", '\u0398'); - _entities.Add ("Iota", '\u0399'); - _entities.Add ("Kappa", '\u039A'); - _entities.Add ("Lambda", '\u039B'); - _entities.Add ("Mu", '\u039C'); - _entities.Add ("Nu", '\u039D'); - _entities.Add ("Xi", '\u039E'); - _entities.Add ("Omicron", '\u039F'); - _entities.Add ("Pi", '\u03A0'); - _entities.Add ("Rho", '\u03A1'); - _entities.Add ("Sigma", '\u03A3'); - _entities.Add ("Tau", '\u03A4'); - _entities.Add ("Upsilon", '\u03A5'); - _entities.Add ("Phi", '\u03A6'); - _entities.Add ("Chi", '\u03A7'); - _entities.Add ("Psi", '\u03A8'); - _entities.Add ("Omega", '\u03A9'); - _entities.Add ("alpha", '\u03B1'); - _entities.Add ("beta", '\u03B2'); - _entities.Add ("gamma", '\u03B3'); - _entities.Add ("delta", '\u03B4'); - _entities.Add ("epsilon", '\u03B5'); - _entities.Add ("zeta", '\u03B6'); - _entities.Add ("eta", '\u03B7'); - _entities.Add ("theta", '\u03B8'); - _entities.Add ("iota", '\u03B9'); - _entities.Add ("kappa", '\u03BA'); - _entities.Add ("lambda", '\u03BB'); - _entities.Add ("mu", '\u03BC'); - _entities.Add ("nu", '\u03BD'); - _entities.Add ("xi", '\u03BE'); - _entities.Add ("omicron", '\u03BF'); - _entities.Add ("pi", '\u03C0'); - _entities.Add ("rho", '\u03C1'); - _entities.Add ("sigmaf", '\u03C2'); - _entities.Add ("sigma", '\u03C3'); - _entities.Add ("tau", '\u03C4'); - _entities.Add ("upsilon", '\u03C5'); - _entities.Add ("phi", '\u03C6'); - _entities.Add ("chi", '\u03C7'); - _entities.Add ("psi", '\u03C8'); - _entities.Add ("omega", '\u03C9'); - _entities.Add ("thetasym", '\u03D1'); - _entities.Add ("upsih", '\u03D2'); - _entities.Add ("piv", '\u03D6'); - _entities.Add ("bull", '\u2022'); - _entities.Add ("hellip", '\u2026'); - _entities.Add ("prime", '\u2032'); - _entities.Add ("Prime", '\u2033'); - _entities.Add ("oline", '\u203E'); - _entities.Add ("frasl", '\u2044'); - _entities.Add ("weierp", '\u2118'); - _entities.Add ("image", '\u2111'); - _entities.Add ("real", '\u211C'); - _entities.Add ("trade", '\u2122'); - _entities.Add ("alefsym", '\u2135'); - _entities.Add ("larr", '\u2190'); - _entities.Add ("uarr", '\u2191'); - _entities.Add ("rarr", '\u2192'); - _entities.Add ("darr", '\u2193'); - _entities.Add ("harr", '\u2194'); - _entities.Add ("crarr", '\u21B5'); - _entities.Add ("lArr", '\u21D0'); - _entities.Add ("uArr", '\u21D1'); - _entities.Add ("rArr", '\u21D2'); - _entities.Add ("dArr", '\u21D3'); - _entities.Add ("hArr", '\u21D4'); - _entities.Add ("forall", '\u2200'); - _entities.Add ("part", '\u2202'); - _entities.Add ("exist", '\u2203'); - _entities.Add ("empty", '\u2205'); - _entities.Add ("nabla", '\u2207'); - _entities.Add ("isin", '\u2208'); - _entities.Add ("notin", '\u2209'); - _entities.Add ("ni", '\u220B'); - _entities.Add ("prod", '\u220F'); - _entities.Add ("sum", '\u2211'); - _entities.Add ("minus", '\u2212'); - _entities.Add ("lowast", '\u2217'); - _entities.Add ("radic", '\u221A'); - _entities.Add ("prop", '\u221D'); - _entities.Add ("infin", '\u221E'); - _entities.Add ("ang", '\u2220'); - _entities.Add ("and", '\u2227'); - _entities.Add ("or", '\u2228'); - _entities.Add ("cap", '\u2229'); - _entities.Add ("cup", '\u222A'); - _entities.Add ("int", '\u222B'); - _entities.Add ("there4", '\u2234'); - _entities.Add ("sim", '\u223C'); - _entities.Add ("cong", '\u2245'); - _entities.Add ("asymp", '\u2248'); - _entities.Add ("ne", '\u2260'); - _entities.Add ("equiv", '\u2261'); - _entities.Add ("le", '\u2264'); - _entities.Add ("ge", '\u2265'); - _entities.Add ("sub", '\u2282'); - _entities.Add ("sup", '\u2283'); - _entities.Add ("nsub", '\u2284'); - _entities.Add ("sube", '\u2286'); - _entities.Add ("supe", '\u2287'); - _entities.Add ("oplus", '\u2295'); - _entities.Add ("otimes", '\u2297'); - _entities.Add ("perp", '\u22A5'); - _entities.Add ("sdot", '\u22C5'); - _entities.Add ("lceil", '\u2308'); - _entities.Add ("rceil", '\u2309'); - _entities.Add ("lfloor", '\u230A'); - _entities.Add ("rfloor", '\u230B'); - _entities.Add ("lang", '\u2329'); - _entities.Add ("rang", '\u232A'); - _entities.Add ("loz", '\u25CA'); - _entities.Add ("spades", '\u2660'); - _entities.Add ("clubs", '\u2663'); - _entities.Add ("hearts", '\u2665'); - _entities.Add ("diams", '\u2666'); - _entities.Add ("quot", '\u0022'); - _entities.Add ("amp", '\u0026'); - _entities.Add ("lt", '\u003C'); - _entities.Add ("gt", '\u003E'); - _entities.Add ("OElig", '\u0152'); - _entities.Add ("oelig", '\u0153'); - _entities.Add ("Scaron", '\u0160'); - _entities.Add ("scaron", '\u0161'); - _entities.Add ("Yuml", '\u0178'); - _entities.Add ("circ", '\u02C6'); - _entities.Add ("tilde", '\u02DC'); - _entities.Add ("ensp", '\u2002'); - _entities.Add ("emsp", '\u2003'); - _entities.Add ("thinsp", '\u2009'); - _entities.Add ("zwnj", '\u200C'); - _entities.Add ("zwj", '\u200D'); - _entities.Add ("lrm", '\u200E'); - _entities.Add ("rlm", '\u200F'); - _entities.Add ("ndash", '\u2013'); - _entities.Add ("mdash", '\u2014'); - _entities.Add ("lsquo", '\u2018'); - _entities.Add ("rsquo", '\u2019'); - _entities.Add ("sbquo", '\u201A'); - _entities.Add ("ldquo", '\u201C'); - _entities.Add ("rdquo", '\u201D'); - _entities.Add ("bdquo", '\u201E'); - _entities.Add ("dagger", '\u2020'); - _entities.Add ("Dagger", '\u2021'); - _entities.Add ("permil", '\u2030'); - _entities.Add ("lsaquo", '\u2039'); - _entities.Add ("rsaquo", '\u203A'); - _entities.Add ("euro", '\u20AC'); - } + /// + /// The ParseQueryString + /// + /// The query + /// The encoding + /// The + public static NameValueCollection ParseQueryString(string query, Encoding encoding) + { + if (query == null) + throw new ArgumentNullException("query"); + + return InternalParseQueryString(query, encoding ?? Encoding.UTF8); + } - private static bool notEncoded (char c) - { - return c == '!' || - c == '\'' || - c == '(' || - c == ')' || - c == '*' || - c == '-' || - c == '.' || - c == '_'; - } + /// + /// The UrlDecode + /// + /// The bytes + /// The encoding + /// The + public static string UrlDecode(byte[] bytes, Encoding encoding) + { + int len; + return bytes == null + ? null + : (len = bytes.Length) == 0 + ? String.Empty + : InternalUrlDecode(bytes, 0, len, encoding ?? Encoding.UTF8); + } - private static void urlEncode (char c, Stream result, bool unicode) - { - if (c > 255) { - // FIXME: What happens when there is an internal error? - //if (!unicode) - // throw new ArgumentOutOfRangeException ("c", c, "Greater than 255."); - - result.WriteByte ((byte) '%'); - result.WriteByte ((byte) 'u'); - - var i = (int) c; - var idx = i >> 12; - result.WriteByte ((byte) _hexChars[idx]); - - idx = (i >> 8) & 0x0F; - result.WriteByte ((byte) _hexChars[idx]); - - idx = (i >> 4) & 0x0F; - result.WriteByte ((byte) _hexChars[idx]); - - idx = i & 0x0F; - result.WriteByte ((byte) _hexChars[idx]); - - return; - } - - if (c > ' ' && notEncoded (c)) { - result.WriteByte ((byte) c); - return; - } - - if (c == ' ') { - result.WriteByte ((byte) '+'); - return; - } - - if ((c < '0') || - (c < 'A' && c > '9') || - (c > 'Z' && c < 'a') || - (c > 'z')) { - if (unicode && c > 127) { - result.WriteByte ((byte) '%'); - result.WriteByte ((byte) 'u'); - result.WriteByte ((byte) '0'); - result.WriteByte ((byte) '0'); + /// + /// The UrlDecode + /// + /// The bytes + /// The offset + /// The count + /// The encoding + /// The + public static string UrlDecode(byte[] bytes, int offset, int count, Encoding encoding) + { + if (bytes == null) + return null; + + var len = bytes.Length; + if (len == 0 || count == 0) + return String.Empty; + + if (offset < 0 || offset >= len) + throw new ArgumentOutOfRangeException("offset"); + + if (count < 0 || count > len - offset) + throw new ArgumentOutOfRangeException("count"); + + return InternalUrlDecode(bytes, offset, count, encoding ?? Encoding.UTF8); } - else { - result.WriteByte ((byte) '%'); + + /// + /// The UrlDecode + /// + /// The s + /// The + public static string UrlDecode(string s) + { + return UrlDecode(s, Encoding.UTF8); } - var i = (int) c; - var idx = i >> 4; - result.WriteByte ((byte) _hexChars[idx]); + /// + /// The UrlDecode + /// + /// The s + /// The encoding + /// The + public static string UrlDecode(string s, Encoding encoding) + { + if (s == null || s.Length == 0 || !s.Contains('%', '+')) + return s; + + if (encoding == null) + encoding = Encoding.UTF8; + + var buff = new List(); + var len = s.Length; + for (var i = 0; i < len; i++) + { + var c = s[i]; + if (c == '%' && i + 2 < len && s[i + 1] != '%') + { + int xchar; + if (s[i + 1] == 'u' && i + 5 < len) + { + // Unicode hex sequence. + xchar = getChar(s, i + 2, 4); + if (xchar != -1) + { + writeCharBytes((char)xchar, buff, encoding); + i += 5; + } + else + { + writeCharBytes('%', buff, encoding); + } + } + else if ((xchar = getChar(s, i + 1, 2)) != -1) + { + writeCharBytes((char)xchar, buff, encoding); + i += 2; + } + else + { + writeCharBytes('%', buff, encoding); + } + + continue; + } + + if (c == '+') + { + writeCharBytes(' ', buff, encoding); + continue; + } + + writeCharBytes(c, buff, encoding); + } - idx = i & 0x0F; - result.WriteByte ((byte) _hexChars[idx]); + return encoding.GetString(buff.ToArray()); + } - return; - } + /// + /// The UrlDecodeToBytes + /// + /// The bytes + /// The + public static byte[] UrlDecodeToBytes(byte[] bytes) + { + int len; + return bytes != null && (len = bytes.Length) > 0 + ? InternalUrlDecodeToBytes(bytes, 0, len) + : bytes; + } - result.WriteByte ((byte) c); - } + /// + /// The UrlDecodeToBytes + /// + /// The bytes + /// The offset + /// The count + /// The + public static byte[] UrlDecodeToBytes(byte[] bytes, int offset, int count) + { + int len; + if (bytes == null || (len = bytes.Length) == 0) + return bytes; + + if (count == 0) + return new byte[0]; + + if (offset < 0 || offset >= len) + throw new ArgumentOutOfRangeException("offset"); + + if (count < 0 || count > len - offset) + throw new ArgumentOutOfRangeException("count"); + + return InternalUrlDecodeToBytes(bytes, offset, count); + } - private static void urlPathEncode (char c, Stream result) - { - if (c < 33 || c > 126) { - var bytes = Encoding.UTF8.GetBytes (c.ToString ()); - foreach (var b in bytes) { - result.WriteByte ((byte) '%'); + /// + /// The UrlDecodeToBytes + /// + /// The s + /// The + public static byte[] UrlDecodeToBytes(string s) + { + return UrlDecodeToBytes(s, Encoding.UTF8); + } - var i = (int) b; - var idx = i >> 4; - result.WriteByte ((byte) _hexChars[idx]); + /// + /// The UrlDecodeToBytes + /// + /// The s + /// The encoding + /// The + public static byte[] UrlDecodeToBytes(string s, Encoding encoding) + { + if (s == null) + return null; + + if (s.Length == 0) + return new byte[0]; + + var bytes = (encoding ?? Encoding.UTF8).GetBytes(s); + return InternalUrlDecodeToBytes(bytes, 0, bytes.Length); + } - idx = i & 0x0F; - result.WriteByte ((byte) _hexChars[idx]); + /// + /// The UrlEncode + /// + /// The bytes + /// The + public static string UrlEncode(byte[] bytes) + { + int len; + return bytes == null + ? null + : (len = bytes.Length) == 0 + ? String.Empty + : Encoding.ASCII.GetString(InternalUrlEncodeToBytes(bytes, 0, len)); } - return; - } + /// + /// The UrlEncode + /// + /// The bytes + /// The offset + /// The count + /// The + public static string UrlEncode(byte[] bytes, int offset, int count) + { + var encoded = UrlEncodeToBytes(bytes, offset, count); + return encoded == null + ? null + : encoded.Length == 0 + ? String.Empty + : Encoding.ASCII.GetString(encoded); + } - if (c == ' ') { - result.WriteByte ((byte) '%'); - result.WriteByte ((byte) '2'); - result.WriteByte ((byte) '0'); + /// + /// The UrlEncode + /// + /// The s + /// The + public static string UrlEncode(string s) + { + return UrlEncode(s, Encoding.UTF8); + } - return; - } + /// + /// The UrlEncode + /// + /// The s + /// The encoding + /// The + public static string UrlEncode(string s, Encoding encoding) + { + int len; + if (s == null || (len = s.Length) == 0) + return s; + + var needEncode = false; + foreach (var c in s) + { + if ((c < '0') || (c < 'A' && c > '9') || (c > 'Z' && c < 'a') || (c > 'z')) + { + if (notEncoded(c)) + continue; + + needEncode = true; + break; + } + } - result.WriteByte ((byte) c); - } + if (!needEncode) + return s; - private static void writeCharBytes (char c, IList buffer, Encoding encoding) - { - if (c > 255) { - foreach (var b in encoding.GetBytes (new[] { c })) - buffer.Add (b); + if (encoding == null) + encoding = Encoding.UTF8; - return; - } + // Avoided GetByteCount call. + var bytes = new byte[encoding.GetMaxByteCount(len)]; + var realLen = encoding.GetBytes(s, 0, len, bytes, 0); - buffer.Add ((byte) c); - } + return Encoding.ASCII.GetString(InternalUrlEncodeToBytes(bytes, 0, realLen)); + } - #endregion + /// + /// The UrlEncodeToBytes + /// + /// The bytes + /// The + public static byte[] UrlEncodeToBytes(byte[] bytes) + { + int len; + return bytes != null && (len = bytes.Length) > 0 + ? InternalUrlEncodeToBytes(bytes, 0, len) + : bytes; + } - #region Internal Methods + /// + /// The UrlEncodeToBytes + /// + /// The bytes + /// The offset + /// The count + /// The + public static byte[] UrlEncodeToBytes(byte[] bytes, int offset, int count) + { + int len; + if (bytes == null || (len = bytes.Length) == 0) + return bytes; + + if (count == 0) + return new byte[0]; + + if (offset < 0 || offset >= len) + throw new ArgumentOutOfRangeException("offset"); + + if (count < 0 || count > len - offset) + throw new ArgumentOutOfRangeException("count"); + + return InternalUrlEncodeToBytes(bytes, offset, count); + } - internal static Uri CreateRequestUrl ( - string requestUri, string host, bool websocketRequest, bool secure) - { - if (requestUri == null || requestUri.Length == 0 || host == null || host.Length == 0) - return null; - - string schm = null; - string path = null; - if (requestUri.StartsWith ("/")) { - path = requestUri; - } - else if (requestUri.MaybeUri ()) { - Uri uri; - var valid = Uri.TryCreate (requestUri, UriKind.Absolute, out uri) && - (((schm = uri.Scheme).StartsWith ("http") && !websocketRequest) || - (schm.StartsWith ("ws") && websocketRequest)); - - if (!valid) - return null; - - host = uri.Authority; - path = uri.PathAndQuery; - } - else if (requestUri == "*") { - } - else { - // As authority form - host = requestUri; - } - - if (schm == null) - schm = (websocketRequest ? "ws" : "http") + (secure ? "s" : String.Empty); - - var colon = host.IndexOf (':'); - if (colon == -1) - host = String.Format ("{0}:{1}", host, schm == "http" || schm == "ws" ? 80 : 443); - - var url = String.Format ("{0}://{1}{2}", schm, host, path); - - Uri res; - if (!Uri.TryCreate (url, UriKind.Absolute, out res)) - return null; - - return res; - } + /// + /// The UrlEncodeToBytes + /// + /// The s + /// The + public static byte[] UrlEncodeToBytes(string s) + { + return UrlEncodeToBytes(s, Encoding.UTF8); + } - internal static IPrincipal CreateUser ( - string response, - AuthenticationSchemes scheme, - string realm, - string method, - Func credentialsFinder - ) - { - if (response == null || response.Length == 0) - return null; + /// + /// The UrlEncodeToBytes + /// + /// The s + /// The encoding + /// The + public static byte[] UrlEncodeToBytes(string s, Encoding encoding) + { + if (s == null) + return null; + + if (s.Length == 0) + return new byte[0]; + + var bytes = (encoding ?? Encoding.UTF8).GetBytes(s); + return InternalUrlEncodeToBytes(bytes, 0, bytes.Length); + } - if (credentialsFinder == null) - return null; + /// + /// The UrlEncodeUnicode + /// + /// The s + /// The + public static string UrlEncodeUnicode(string s) + { + return s != null && s.Length > 0 + ? Encoding.ASCII.GetString(InternalUrlEncodeUnicodeToBytes(s)) + : s; + } - if (!(scheme == AuthenticationSchemes.Basic || scheme == AuthenticationSchemes.Digest)) - return null; + /// + /// The UrlEncodeUnicodeToBytes + /// + /// The s + /// The + public static byte[] UrlEncodeUnicodeToBytes(string s) + { + return s == null + ? null + : s.Length == 0 + ? new byte[0] + : InternalUrlEncodeUnicodeToBytes(s); + } - if (scheme == AuthenticationSchemes.Digest) { - if (realm == null || realm.Length == 0) - return null; + /// + /// The UrlPathEncode + /// + /// The s + /// The + public static string UrlPathEncode(string s) + { + if (s == null || s.Length == 0) + return s; + + using (var res = new MemoryStream()) + { + foreach (var c in s) + urlPathEncode(c, res); + + res.Close(); + return Encoding.ASCII.GetString(res.ToArray()); + } + } - if (method == null || method.Length == 0) - return null; - } + /// + /// The CreateRequestUrl + /// + /// The requestUri + /// The host + /// The websocketRequest + /// The secure + /// The + internal static Uri CreateRequestUrl( + string requestUri, string host, bool websocketRequest, bool secure) + { + if (requestUri == null || requestUri.Length == 0 || host == null || host.Length == 0) + return null; + + string schm = null; + string path = null; + if (requestUri.StartsWith("/")) + { + path = requestUri; + } + else if (requestUri.MaybeUri()) + { + Uri uri; + var valid = Uri.TryCreate(requestUri, UriKind.Absolute, out uri) && + (((schm = uri.Scheme).StartsWith("http") && !websocketRequest) || + (schm.StartsWith("ws") && websocketRequest)); + + if (!valid) + return null; + + host = uri.Authority; + path = uri.PathAndQuery; + } + else if (requestUri == "*") + { + } + else + { + // As authority form + host = requestUri; + } - if (!response.StartsWith (scheme.ToString (), StringComparison.OrdinalIgnoreCase)) - return null; + if (schm == null) + schm = (websocketRequest ? "ws" : "http") + (secure ? "s" : String.Empty); - var res = AuthenticationResponse.Parse (response); - if (res == null) - return null; + var colon = host.IndexOf(':'); + if (colon == -1) + host = String.Format("{0}:{1}", host, schm == "http" || schm == "ws" ? 80 : 443); - var id = res.ToIdentity (); - if (id == null) - return null; + var url = String.Format("{0}://{1}{2}", schm, host, path); - NetworkCredential cred = null; - try { - cred = credentialsFinder (id); - } - catch { - } + Uri res; + if (!Uri.TryCreate(url, UriKind.Absolute, out res)) + return null; - if (cred == null) - return null; + return res; + } - if (scheme == AuthenticationSchemes.Basic - && ((HttpBasicIdentity) id).Password != cred.Password - ) { - return null; - } + /// + /// The CreateUser + /// + /// The response + /// The scheme + /// The realm + /// The method + /// The credentialsFinder + /// The + internal static IPrincipal CreateUser( + string response, + AuthenticationSchemes scheme, + string realm, + string method, + Func credentialsFinder + ) + { + if (response == null || response.Length == 0) + return null; - if (scheme == AuthenticationSchemes.Digest - && !((HttpDigestIdentity) id).IsValid (cred.Password, realm, method, null) - ) { - return null; - } + if (credentialsFinder == null) + return null; - return new GenericPrincipal (id, cred.Roles); - } + if (!(scheme == AuthenticationSchemes.Basic || scheme == AuthenticationSchemes.Digest)) + return null; - internal static Encoding GetEncoding (string contentType) - { - var name = "charset="; - var comparison = StringComparison.OrdinalIgnoreCase; + if (scheme == AuthenticationSchemes.Digest) + { + if (realm == null || realm.Length == 0) + return null; - foreach (var elm in contentType.SplitHeaderValue (';')) { - var part = elm.Trim (); - if (part.IndexOf (name, comparison) != 0) - continue; + if (method == null || method.Length == 0) + return null; + } - var val = part.GetValue ('=', true); - if (val == null || val.Length == 0) - return null; + if (!response.StartsWith(scheme.ToString(), StringComparison.OrdinalIgnoreCase)) + return null; - return Encoding.GetEncoding (val); - } + var res = AuthenticationResponse.Parse(response); + if (res == null) + return null; - return null; - } + var id = res.ToIdentity(); + if (id == null) + return null; - internal static NameValueCollection InternalParseQueryString (string query, Encoding encoding) - { - int len; - if (query == null || (len = query.Length) == 0 || (len == 1 && query[0] == '?')) - return new NameValueCollection (1); - - if (query[0] == '?') - query = query.Substring (1); - - var res = new QueryStringCollection (); - var components = query.Split ('&'); - foreach (var component in components) { - var i = component.IndexOf ('='); - if (i > -1) { - var name = UrlDecode (component.Substring (0, i), encoding); - var val = component.Length > i + 1 - ? UrlDecode (component.Substring (i + 1), encoding) - : String.Empty; - - res.Add (name, val); - } - else { - res.Add (null, UrlDecode (component, encoding)); - } - } + NetworkCredential cred = null; + try + { + cred = credentialsFinder(id); + } + catch + { + } - return res; - } + if (cred == null) + return null; - internal static string InternalUrlDecode ( - byte[] bytes, int offset, int count, Encoding encoding) - { - var output = new StringBuilder (); - using (var acc = new MemoryStream ()) { - var end = count + offset; - for (var i = offset; i < end; i++) { - if (bytes[i] == '%' && i + 2 < count && bytes[i + 1] != '%') { - int xchar; - if (bytes[i + 1] == (byte) 'u' && i + 5 < end) { - if (acc.Length > 0) { - output.Append (getChars (acc, encoding)); - acc.SetLength (0); - } - - xchar = getChar (bytes, i + 2, 4); - if (xchar != -1) { - output.Append ((char) xchar); - i += 5; - - continue; - } + if (scheme == AuthenticationSchemes.Basic + && ((HttpBasicIdentity)id).Password != cred.Password + ) + { + return null; } - else if ((xchar = getChar (bytes, i + 1, 2)) != -1) { - acc.WriteByte ((byte) xchar); - i += 2; - continue; + if (scheme == AuthenticationSchemes.Digest + && !((HttpDigestIdentity)id).IsValid(cred.Password, realm, method, null) + ) + { + return null; } - } - if (acc.Length > 0) { - output.Append (getChars (acc, encoding)); - acc.SetLength (0); - } + return new GenericPrincipal(id, cred.Roles); + } - if (bytes[i] == '+') { - output.Append (' '); - continue; - } + /// + /// The GetEncoding + /// + /// The contentType + /// The + internal static Encoding GetEncoding(string contentType) + { + var name = "charset="; + var comparison = StringComparison.OrdinalIgnoreCase; + + foreach (var elm in contentType.SplitHeaderValue(';')) + { + var part = elm.Trim(); + if (part.IndexOf(name, comparison) != 0) + continue; + + var val = part.GetValue('=', true); + if (val == null || val.Length == 0) + return null; + + return Encoding.GetEncoding(val); + } - output.Append ((char) bytes[i]); + return null; } - if (acc.Length > 0) - output.Append (getChars (acc, encoding)); - } + /// + /// The InternalParseQueryString + /// + /// The query + /// The encoding + /// The + internal static NameValueCollection InternalParseQueryString(string query, Encoding encoding) + { + int len; + if (query == null || (len = query.Length) == 0 || (len == 1 && query[0] == '?')) + return new NameValueCollection(1); + + if (query[0] == '?') + query = query.Substring(1); + + var res = new QueryStringCollection(); + var components = query.Split('&'); + foreach (var component in components) + { + var i = component.IndexOf('='); + if (i > -1) + { + var name = UrlDecode(component.Substring(0, i), encoding); + var val = component.Length > i + 1 + ? UrlDecode(component.Substring(i + 1), encoding) + : String.Empty; + + res.Add(name, val); + } + else + { + res.Add(null, UrlDecode(component, encoding)); + } + } - return output.ToString (); - } + return res; + } - internal static byte[] InternalUrlDecodeToBytes (byte[] bytes, int offset, int count) - { - using (var res = new MemoryStream ()) { - var end = offset + count; - for (var i = offset; i < end; i++) { - var c = (char) bytes[i]; - if (c == '+') { - c = ' '; - } - else if (c == '%' && i < end - 2) { - var xchar = getChar (bytes, i + 1, 2); - if (xchar != -1) { - c = (char) xchar; - i += 2; + /// + /// The InternalUrlDecode + /// + /// The bytes + /// The offset + /// The count + /// The encoding + /// The + internal static string InternalUrlDecode( + byte[] bytes, int offset, int count, Encoding encoding) + { + var output = new StringBuilder(); + using (var acc = new MemoryStream()) + { + var end = count + offset; + for (var i = offset; i < end; i++) + { + if (bytes[i] == '%' && i + 2 < count && bytes[i + 1] != '%') + { + int xchar; + if (bytes[i + 1] == (byte)'u' && i + 5 < end) + { + if (acc.Length > 0) + { + output.Append(getChars(acc, encoding)); + acc.SetLength(0); + } + + xchar = getChar(bytes, i + 2, 4); + if (xchar != -1) + { + output.Append((char)xchar); + i += 5; + + continue; + } + } + else if ((xchar = getChar(bytes, i + 1, 2)) != -1) + { + acc.WriteByte((byte)xchar); + i += 2; + + continue; + } + } + + if (acc.Length > 0) + { + output.Append(getChars(acc, encoding)); + acc.SetLength(0); + } + + if (bytes[i] == '+') + { + output.Append(' '); + continue; + } + + output.Append((char)bytes[i]); + } + + if (acc.Length > 0) + output.Append(getChars(acc, encoding)); } - } - res.WriteByte ((byte) c); + return output.ToString(); } - res.Close (); - return res.ToArray (); - } - } - - internal static byte[] InternalUrlEncodeToBytes (byte[] bytes, int offset, int count) - { - using (var res = new MemoryStream ()) { - var end = offset + count; - for (var i = offset; i < end; i++) - urlEncode ((char) bytes[i], res, false); - - res.Close (); - return res.ToArray (); - } - } + /// + /// The InternalUrlDecodeToBytes + /// + /// The bytes + /// The offset + /// The count + /// The + internal static byte[] InternalUrlDecodeToBytes(byte[] bytes, int offset, int count) + { + using (var res = new MemoryStream()) + { + var end = offset + count; + for (var i = offset; i < end; i++) + { + var c = (char)bytes[i]; + if (c == '+') + { + c = ' '; + } + else if (c == '%' && i < end - 2) + { + var xchar = getChar(bytes, i + 1, 2); + if (xchar != -1) + { + c = (char)xchar; + i += 2; + } + } + + res.WriteByte((byte)c); + } + + res.Close(); + return res.ToArray(); + } + } - internal static byte[] InternalUrlEncodeUnicodeToBytes (string s) - { - using (var res = new MemoryStream ()) { - foreach (var c in s) - urlEncode (c, res, true); + /// + /// The InternalUrlEncodeToBytes + /// + /// The bytes + /// The offset + /// The count + /// The + internal static byte[] InternalUrlEncodeToBytes(byte[] bytes, int offset, int count) + { + using (var res = new MemoryStream()) + { + var end = offset + count; + for (var i = offset; i < end; i++) + urlEncode((char)bytes[i], res, false); + + res.Close(); + return res.ToArray(); + } + } - res.Close (); - return res.ToArray (); - } - } + /// + /// The InternalUrlEncodeUnicodeToBytes + /// + /// The s + /// The + internal static byte[] InternalUrlEncodeUnicodeToBytes(string s) + { + using (var res = new MemoryStream()) + { + foreach (var c in s) + urlEncode(c, res, true); + + res.Close(); + return res.ToArray(); + } + } - internal static bool TryGetEncoding ( + /// + /// The TryGetEncoding + /// + /// The contentType + /// The result + /// The + internal static bool TryGetEncoding( string contentType, out Encoding result ) - { - result = null; - - try { - result = GetEncoding (contentType); - } - catch { - return false; - } - - return result != null; - } - - #endregion - - #region Public Methods + { + result = null; - public static string HtmlAttributeEncode (string s) - { - if (s == null || s.Length == 0 || !s.Contains ('&', '"', '<', '>')) - return s; - - var output = new StringBuilder (); - foreach (var c in s) - output.Append ( - c == '&' - ? "&" - : c == '"' - ? """ - : c == '<' - ? "<" - : c == '>' - ? ">" - : c.ToString ()); - - return output.ToString (); - } - - public static void HtmlAttributeEncode (string s, TextWriter output) - { - if (output == null) - throw new ArgumentNullException ("output"); - - output.Write (HtmlAttributeEncode (s)); - } + try + { + result = GetEncoding(contentType); + } + catch + { + return false; + } - /// - /// Decodes an HTML-encoded and returns the decoded . - /// - /// - /// A that represents the decoded string. - /// - /// - /// A to decode. - /// - public static string HtmlDecode (string s) - { - if (s == null || s.Length == 0 || !s.Contains ('&')) - return s; - - var entity = new StringBuilder (); - var output = new StringBuilder (); - - // 0 -> nothing, - // 1 -> right after '&' - // 2 -> between '&' and ';' but no '#' - // 3 -> '#' found after '&' and getting numbers - var state = 0; - - var number = 0; - var haveTrailingDigits = false; - foreach (var c in s) { - if (state == 0) { - if (c == '&') { - entity.Append (c); - state = 1; - } - else { - output.Append (c); - } - - continue; + return result != null; } - if (c == '&') { - state = 1; - if (haveTrailingDigits) { - entity.Append (number.ToString (CultureInfo.InvariantCulture)); - haveTrailingDigits = false; - } - - output.Append (entity.ToString ()); - entity.Length = 0; - entity.Append ('&'); + /// + /// The getChar + /// + /// The bytes + /// The offset + /// The length + /// The + private static int getChar(byte[] bytes, int offset, int length) + { + var val = 0; + var end = length + offset; + for (var i = offset; i < end; i++) + { + var current = getInt(bytes[i]); + if (current == -1) + return -1; + + val = (val << 4) + current; + } - continue; + return val; } - if (state == 1) { - if (c == ';') { - state = 0; - output.Append (entity.ToString ()); - output.Append (c); - entity.Length = 0; - } - else { - number = 0; - if (c != '#') - state = 2; - else - state = 3; + /// + /// The getChar + /// + /// The s + /// The offset + /// The length + /// The + private static int getChar(string s, int offset, int length) + { + var val = 0; + var end = length + offset; + for (var i = offset; i < end; i++) + { + var c = s[i]; + if (c > 127) + return -1; + + var current = getInt((byte)c); + if (current == -1) + return -1; + + val = (val << 4) + current; + } - entity.Append (c); - } + return val; } - else if (state == 2) { - entity.Append (c); - if (c == ';') { - var key = entity.ToString (); - var entities = getEntities (); - if (key.Length > 1 && entities.ContainsKey (key.Substring (1, key.Length - 2))) - key = entities[key.Substring (1, key.Length - 2)].ToString (); - - output.Append (key); - state = 0; - entity.Length = 0; - } + + /// + /// The getChars + /// + /// The buffer + /// The encoding + /// The + private static char[] getChars(MemoryStream buffer, Encoding encoding) + { + return encoding.GetChars(buffer.GetBuffer(), 0, (int)buffer.Length); } - else if (state == 3) { - if (c == ';') { - if (number > 65535) { - output.Append ("&#"); - output.Append (number.ToString (CultureInfo.InvariantCulture)); - output.Append (";"); - } - else { - output.Append ((char) number); - } - state = 0; - entity.Length = 0; - haveTrailingDigits = false; - } - else if (Char.IsDigit (c)) { - number = number * 10 + ((int) c - '0'); - haveTrailingDigits = true; - } - else { - state = 2; - if (haveTrailingDigits) { - entity.Append (number.ToString (CultureInfo.InvariantCulture)); - haveTrailingDigits = false; + /// + /// The getEntities + /// + /// The + private static Dictionary getEntities() + { + lock (_sync) + { + if (_entities == null) + initEntities(); + + return _entities; } - - entity.Append (c); - } } - } - if (entity.Length > 0) - output.Append (entity.ToString ()); - else if (haveTrailingDigits) - output.Append (number.ToString (CultureInfo.InvariantCulture)); - - return output.ToString (); - } - - /// - /// Decodes an HTML-encoded and sends the decoded - /// to the specified . - /// - /// - /// A to decode. - /// - /// - /// A that receives the decoded string. - /// - public static void HtmlDecode (string s, TextWriter output) - { - if (output == null) - throw new ArgumentNullException ("output"); - - output.Write (HtmlDecode (s)); - } - - /// - /// HTML-encodes a and returns the encoded . - /// - /// - /// A that represents the encoded string. - /// - /// - /// A to encode. - /// - public static string HtmlEncode (string s) - { - if (s == null || s.Length == 0) - return s; - - var needEncode = false; - foreach (var c in s) { - if (c == '&' || c == '"' || c == '<' || c == '>' || c > 159) { - needEncode = true; - break; + /// + /// The getInt + /// + /// The b + /// The + private static int getInt(byte b) + { + var c = (char)b; + return c >= '0' && c <= '9' + ? c - '0' + : c >= 'a' && c <= 'f' + ? c - 'a' + 10 + : c >= 'A' && c <= 'F' + ? c - 'A' + 10 + : -1; } - } - if (!needEncode) - return s; - - var output = new StringBuilder (); - foreach (var c in s) { - if (c == '&') { - output.Append ("&"); - } - else if (c == '"') { - output.Append ("""); - } - else if (c == '<') { - output.Append ("<"); - } - else if (c == '>') { - output.Append (">"); + /// + /// The initEntities + /// + private static void initEntities() + { + // Build the dictionary of HTML entity references. + // This list comes from the HTML 4.01 W3C recommendation. + _entities = new Dictionary(); + _entities.Add("nbsp", '\u00A0'); + _entities.Add("iexcl", '\u00A1'); + _entities.Add("cent", '\u00A2'); + _entities.Add("pound", '\u00A3'); + _entities.Add("curren", '\u00A4'); + _entities.Add("yen", '\u00A5'); + _entities.Add("brvbar", '\u00A6'); + _entities.Add("sect", '\u00A7'); + _entities.Add("uml", '\u00A8'); + _entities.Add("copy", '\u00A9'); + _entities.Add("ordf", '\u00AA'); + _entities.Add("laquo", '\u00AB'); + _entities.Add("not", '\u00AC'); + _entities.Add("shy", '\u00AD'); + _entities.Add("reg", '\u00AE'); + _entities.Add("macr", '\u00AF'); + _entities.Add("deg", '\u00B0'); + _entities.Add("plusmn", '\u00B1'); + _entities.Add("sup2", '\u00B2'); + _entities.Add("sup3", '\u00B3'); + _entities.Add("acute", '\u00B4'); + _entities.Add("micro", '\u00B5'); + _entities.Add("para", '\u00B6'); + _entities.Add("middot", '\u00B7'); + _entities.Add("cedil", '\u00B8'); + _entities.Add("sup1", '\u00B9'); + _entities.Add("ordm", '\u00BA'); + _entities.Add("raquo", '\u00BB'); + _entities.Add("frac14", '\u00BC'); + _entities.Add("frac12", '\u00BD'); + _entities.Add("frac34", '\u00BE'); + _entities.Add("iquest", '\u00BF'); + _entities.Add("Agrave", '\u00C0'); + _entities.Add("Aacute", '\u00C1'); + _entities.Add("Acirc", '\u00C2'); + _entities.Add("Atilde", '\u00C3'); + _entities.Add("Auml", '\u00C4'); + _entities.Add("Aring", '\u00C5'); + _entities.Add("AElig", '\u00C6'); + _entities.Add("Ccedil", '\u00C7'); + _entities.Add("Egrave", '\u00C8'); + _entities.Add("Eacute", '\u00C9'); + _entities.Add("Ecirc", '\u00CA'); + _entities.Add("Euml", '\u00CB'); + _entities.Add("Igrave", '\u00CC'); + _entities.Add("Iacute", '\u00CD'); + _entities.Add("Icirc", '\u00CE'); + _entities.Add("Iuml", '\u00CF'); + _entities.Add("ETH", '\u00D0'); + _entities.Add("Ntilde", '\u00D1'); + _entities.Add("Ograve", '\u00D2'); + _entities.Add("Oacute", '\u00D3'); + _entities.Add("Ocirc", '\u00D4'); + _entities.Add("Otilde", '\u00D5'); + _entities.Add("Ouml", '\u00D6'); + _entities.Add("times", '\u00D7'); + _entities.Add("Oslash", '\u00D8'); + _entities.Add("Ugrave", '\u00D9'); + _entities.Add("Uacute", '\u00DA'); + _entities.Add("Ucirc", '\u00DB'); + _entities.Add("Uuml", '\u00DC'); + _entities.Add("Yacute", '\u00DD'); + _entities.Add("THORN", '\u00DE'); + _entities.Add("szlig", '\u00DF'); + _entities.Add("agrave", '\u00E0'); + _entities.Add("aacute", '\u00E1'); + _entities.Add("acirc", '\u00E2'); + _entities.Add("atilde", '\u00E3'); + _entities.Add("auml", '\u00E4'); + _entities.Add("aring", '\u00E5'); + _entities.Add("aelig", '\u00E6'); + _entities.Add("ccedil", '\u00E7'); + _entities.Add("egrave", '\u00E8'); + _entities.Add("eacute", '\u00E9'); + _entities.Add("ecirc", '\u00EA'); + _entities.Add("euml", '\u00EB'); + _entities.Add("igrave", '\u00EC'); + _entities.Add("iacute", '\u00ED'); + _entities.Add("icirc", '\u00EE'); + _entities.Add("iuml", '\u00EF'); + _entities.Add("eth", '\u00F0'); + _entities.Add("ntilde", '\u00F1'); + _entities.Add("ograve", '\u00F2'); + _entities.Add("oacute", '\u00F3'); + _entities.Add("ocirc", '\u00F4'); + _entities.Add("otilde", '\u00F5'); + _entities.Add("ouml", '\u00F6'); + _entities.Add("divide", '\u00F7'); + _entities.Add("oslash", '\u00F8'); + _entities.Add("ugrave", '\u00F9'); + _entities.Add("uacute", '\u00FA'); + _entities.Add("ucirc", '\u00FB'); + _entities.Add("uuml", '\u00FC'); + _entities.Add("yacute", '\u00FD'); + _entities.Add("thorn", '\u00FE'); + _entities.Add("yuml", '\u00FF'); + _entities.Add("fnof", '\u0192'); + _entities.Add("Alpha", '\u0391'); + _entities.Add("Beta", '\u0392'); + _entities.Add("Gamma", '\u0393'); + _entities.Add("Delta", '\u0394'); + _entities.Add("Epsilon", '\u0395'); + _entities.Add("Zeta", '\u0396'); + _entities.Add("Eta", '\u0397'); + _entities.Add("Theta", '\u0398'); + _entities.Add("Iota", '\u0399'); + _entities.Add("Kappa", '\u039A'); + _entities.Add("Lambda", '\u039B'); + _entities.Add("Mu", '\u039C'); + _entities.Add("Nu", '\u039D'); + _entities.Add("Xi", '\u039E'); + _entities.Add("Omicron", '\u039F'); + _entities.Add("Pi", '\u03A0'); + _entities.Add("Rho", '\u03A1'); + _entities.Add("Sigma", '\u03A3'); + _entities.Add("Tau", '\u03A4'); + _entities.Add("Upsilon", '\u03A5'); + _entities.Add("Phi", '\u03A6'); + _entities.Add("Chi", '\u03A7'); + _entities.Add("Psi", '\u03A8'); + _entities.Add("Omega", '\u03A9'); + _entities.Add("alpha", '\u03B1'); + _entities.Add("beta", '\u03B2'); + _entities.Add("gamma", '\u03B3'); + _entities.Add("delta", '\u03B4'); + _entities.Add("epsilon", '\u03B5'); + _entities.Add("zeta", '\u03B6'); + _entities.Add("eta", '\u03B7'); + _entities.Add("theta", '\u03B8'); + _entities.Add("iota", '\u03B9'); + _entities.Add("kappa", '\u03BA'); + _entities.Add("lambda", '\u03BB'); + _entities.Add("mu", '\u03BC'); + _entities.Add("nu", '\u03BD'); + _entities.Add("xi", '\u03BE'); + _entities.Add("omicron", '\u03BF'); + _entities.Add("pi", '\u03C0'); + _entities.Add("rho", '\u03C1'); + _entities.Add("sigmaf", '\u03C2'); + _entities.Add("sigma", '\u03C3'); + _entities.Add("tau", '\u03C4'); + _entities.Add("upsilon", '\u03C5'); + _entities.Add("phi", '\u03C6'); + _entities.Add("chi", '\u03C7'); + _entities.Add("psi", '\u03C8'); + _entities.Add("omega", '\u03C9'); + _entities.Add("thetasym", '\u03D1'); + _entities.Add("upsih", '\u03D2'); + _entities.Add("piv", '\u03D6'); + _entities.Add("bull", '\u2022'); + _entities.Add("hellip", '\u2026'); + _entities.Add("prime", '\u2032'); + _entities.Add("Prime", '\u2033'); + _entities.Add("oline", '\u203E'); + _entities.Add("frasl", '\u2044'); + _entities.Add("weierp", '\u2118'); + _entities.Add("image", '\u2111'); + _entities.Add("real", '\u211C'); + _entities.Add("trade", '\u2122'); + _entities.Add("alefsym", '\u2135'); + _entities.Add("larr", '\u2190'); + _entities.Add("uarr", '\u2191'); + _entities.Add("rarr", '\u2192'); + _entities.Add("darr", '\u2193'); + _entities.Add("harr", '\u2194'); + _entities.Add("crarr", '\u21B5'); + _entities.Add("lArr", '\u21D0'); + _entities.Add("uArr", '\u21D1'); + _entities.Add("rArr", '\u21D2'); + _entities.Add("dArr", '\u21D3'); + _entities.Add("hArr", '\u21D4'); + _entities.Add("forall", '\u2200'); + _entities.Add("part", '\u2202'); + _entities.Add("exist", '\u2203'); + _entities.Add("empty", '\u2205'); + _entities.Add("nabla", '\u2207'); + _entities.Add("isin", '\u2208'); + _entities.Add("notin", '\u2209'); + _entities.Add("ni", '\u220B'); + _entities.Add("prod", '\u220F'); + _entities.Add("sum", '\u2211'); + _entities.Add("minus", '\u2212'); + _entities.Add("lowast", '\u2217'); + _entities.Add("radic", '\u221A'); + _entities.Add("prop", '\u221D'); + _entities.Add("infin", '\u221E'); + _entities.Add("ang", '\u2220'); + _entities.Add("and", '\u2227'); + _entities.Add("or", '\u2228'); + _entities.Add("cap", '\u2229'); + _entities.Add("cup", '\u222A'); + _entities.Add("int", '\u222B'); + _entities.Add("there4", '\u2234'); + _entities.Add("sim", '\u223C'); + _entities.Add("cong", '\u2245'); + _entities.Add("asymp", '\u2248'); + _entities.Add("ne", '\u2260'); + _entities.Add("equiv", '\u2261'); + _entities.Add("le", '\u2264'); + _entities.Add("ge", '\u2265'); + _entities.Add("sub", '\u2282'); + _entities.Add("sup", '\u2283'); + _entities.Add("nsub", '\u2284'); + _entities.Add("sube", '\u2286'); + _entities.Add("supe", '\u2287'); + _entities.Add("oplus", '\u2295'); + _entities.Add("otimes", '\u2297'); + _entities.Add("perp", '\u22A5'); + _entities.Add("sdot", '\u22C5'); + _entities.Add("lceil", '\u2308'); + _entities.Add("rceil", '\u2309'); + _entities.Add("lfloor", '\u230A'); + _entities.Add("rfloor", '\u230B'); + _entities.Add("lang", '\u2329'); + _entities.Add("rang", '\u232A'); + _entities.Add("loz", '\u25CA'); + _entities.Add("spades", '\u2660'); + _entities.Add("clubs", '\u2663'); + _entities.Add("hearts", '\u2665'); + _entities.Add("diams", '\u2666'); + _entities.Add("quot", '\u0022'); + _entities.Add("amp", '\u0026'); + _entities.Add("lt", '\u003C'); + _entities.Add("gt", '\u003E'); + _entities.Add("OElig", '\u0152'); + _entities.Add("oelig", '\u0153'); + _entities.Add("Scaron", '\u0160'); + _entities.Add("scaron", '\u0161'); + _entities.Add("Yuml", '\u0178'); + _entities.Add("circ", '\u02C6'); + _entities.Add("tilde", '\u02DC'); + _entities.Add("ensp", '\u2002'); + _entities.Add("emsp", '\u2003'); + _entities.Add("thinsp", '\u2009'); + _entities.Add("zwnj", '\u200C'); + _entities.Add("zwj", '\u200D'); + _entities.Add("lrm", '\u200E'); + _entities.Add("rlm", '\u200F'); + _entities.Add("ndash", '\u2013'); + _entities.Add("mdash", '\u2014'); + _entities.Add("lsquo", '\u2018'); + _entities.Add("rsquo", '\u2019'); + _entities.Add("sbquo", '\u201A'); + _entities.Add("ldquo", '\u201C'); + _entities.Add("rdquo", '\u201D'); + _entities.Add("bdquo", '\u201E'); + _entities.Add("dagger", '\u2020'); + _entities.Add("Dagger", '\u2021'); + _entities.Add("permil", '\u2030'); + _entities.Add("lsaquo", '\u2039'); + _entities.Add("rsaquo", '\u203A'); + _entities.Add("euro", '\u20AC'); } - else if (c > 159) { - // MS starts encoding with &# from 160 and stops at 255. - // We don't do that. One reason is the 65308/65310 unicode - // characters that look like '<' and '>'. - output.Append ("&#"); - output.Append (((int) c).ToString (CultureInfo.InvariantCulture)); - output.Append (";"); - } - else { - output.Append (c); - } - } - return output.ToString (); - } - - /// - /// HTML-encodes a and sends the encoded - /// to the specified . - /// - /// - /// A to encode. - /// - /// - /// A that receives the encoded string. - /// - public static void HtmlEncode (string s, TextWriter output) - { - if (output == null) - throw new ArgumentNullException ("output"); - - output.Write (HtmlEncode (s)); - } - - public static NameValueCollection ParseQueryString (string query) - { - return ParseQueryString (query, Encoding.UTF8); - } - - public static NameValueCollection ParseQueryString (string query, Encoding encoding) - { - if (query == null) - throw new ArgumentNullException ("query"); + /// + /// The notEncoded + /// + /// The c + /// The + private static bool notEncoded(char c) + { + return c == '!' || + c == '\'' || + c == '(' || + c == ')' || + c == '*' || + c == '-' || + c == '.' || + c == '_'; + } - return InternalParseQueryString (query, encoding ?? Encoding.UTF8); - } + /// + /// The urlEncode + /// + /// The c + /// The result + /// The unicode + private static void urlEncode(char c, Stream result, bool unicode) + { + if (c > 255) + { + // FIXME: What happens when there is an internal error? + //if (!unicode) + // throw new ArgumentOutOfRangeException ("c", c, "Greater than 255."); + + result.WriteByte((byte)'%'); + result.WriteByte((byte)'u'); + + var i = (int)c; + var idx = i >> 12; + result.WriteByte((byte)_hexChars[idx]); + + idx = (i >> 8) & 0x0F; + result.WriteByte((byte)_hexChars[idx]); + + idx = (i >> 4) & 0x0F; + result.WriteByte((byte)_hexChars[idx]); + + idx = i & 0x0F; + result.WriteByte((byte)_hexChars[idx]); + + return; + } - public static string UrlDecode (string s) - { - return UrlDecode (s, Encoding.UTF8); - } + if (c > ' ' && notEncoded(c)) + { + result.WriteByte((byte)c); + return; + } - public static string UrlDecode (string s, Encoding encoding) - { - if (s == null || s.Length == 0 || !s.Contains ('%', '+')) - return s; - - if (encoding == null) - encoding = Encoding.UTF8; - - var buff = new List (); - var len = s.Length; - for (var i = 0; i < len; i++) { - var c = s[i]; - if (c == '%' && i + 2 < len && s[i + 1] != '%') { - int xchar; - if (s[i + 1] == 'u' && i + 5 < len) { - // Unicode hex sequence. - xchar = getChar (s, i + 2, 4); - if (xchar != -1) { - writeCharBytes ((char) xchar, buff, encoding); - i += 5; + if (c == ' ') + { + result.WriteByte((byte)'+'); + return; } - else { - writeCharBytes ('%', buff, encoding); + + if ((c < '0') || + (c < 'A' && c > '9') || + (c > 'Z' && c < 'a') || + (c > 'z')) + { + if (unicode && c > 127) + { + result.WriteByte((byte)'%'); + result.WriteByte((byte)'u'); + result.WriteByte((byte)'0'); + result.WriteByte((byte)'0'); + } + else + { + result.WriteByte((byte)'%'); + } + + var i = (int)c; + var idx = i >> 4; + result.WriteByte((byte)_hexChars[idx]); + + idx = i & 0x0F; + result.WriteByte((byte)_hexChars[idx]); + + return; } - } - else if ((xchar = getChar (s, i + 1, 2)) != -1) { - writeCharBytes ((char) xchar, buff, encoding); - i += 2; - } - else { - writeCharBytes ('%', buff, encoding); - } - - continue; - } - if (c == '+') { - writeCharBytes (' ', buff, encoding); - continue; + result.WriteByte((byte)c); } - writeCharBytes (c, buff, encoding); - } - - return encoding.GetString (buff.ToArray ()); - } - - public static string UrlDecode (byte[] bytes, Encoding encoding) - { - int len; - return bytes == null - ? null - : (len = bytes.Length) == 0 - ? String.Empty - : InternalUrlDecode (bytes, 0, len, encoding ?? Encoding.UTF8); - } - - public static string UrlDecode (byte[] bytes, int offset, int count, Encoding encoding) - { - if (bytes == null) - return null; - - var len = bytes.Length; - if (len == 0 || count == 0) - return String.Empty; - - if (offset < 0 || offset >= len) - throw new ArgumentOutOfRangeException ("offset"); - - if (count < 0 || count > len - offset) - throw new ArgumentOutOfRangeException ("count"); - - return InternalUrlDecode (bytes, offset, count, encoding ?? Encoding.UTF8); - } - - public static byte[] UrlDecodeToBytes (byte[] bytes) - { - int len; - return bytes != null && (len = bytes.Length) > 0 - ? InternalUrlDecodeToBytes (bytes, 0, len) - : bytes; - } - - public static byte[] UrlDecodeToBytes (string s) - { - return UrlDecodeToBytes (s, Encoding.UTF8); - } - - public static byte[] UrlDecodeToBytes (string s, Encoding encoding) - { - if (s == null) - return null; - - if (s.Length == 0) - return new byte[0]; - - var bytes = (encoding ?? Encoding.UTF8).GetBytes (s); - return InternalUrlDecodeToBytes (bytes, 0, bytes.Length); - } - - public static byte[] UrlDecodeToBytes (byte[] bytes, int offset, int count) - { - int len; - if (bytes == null || (len = bytes.Length) == 0) - return bytes; - - if (count == 0) - return new byte[0]; - - if (offset < 0 || offset >= len) - throw new ArgumentOutOfRangeException ("offset"); - - if (count < 0 || count > len - offset ) - throw new ArgumentOutOfRangeException ("count"); + /// + /// The urlPathEncode + /// + /// The c + /// The result + private static void urlPathEncode(char c, Stream result) + { + if (c < 33 || c > 126) + { + var bytes = Encoding.UTF8.GetBytes(c.ToString()); + foreach (var b in bytes) + { + result.WriteByte((byte)'%'); + + var i = (int)b; + var idx = i >> 4; + result.WriteByte((byte)_hexChars[idx]); + + idx = i & 0x0F; + result.WriteByte((byte)_hexChars[idx]); + } + + return; + } - return InternalUrlDecodeToBytes (bytes, offset, count); - } + if (c == ' ') + { + result.WriteByte((byte)'%'); + result.WriteByte((byte)'2'); + result.WriteByte((byte)'0'); - public static string UrlEncode (byte[] bytes) - { - int len; - return bytes == null - ? null - : (len = bytes.Length) == 0 - ? String.Empty - : Encoding.ASCII.GetString (InternalUrlEncodeToBytes (bytes, 0, len)); - } + return; + } - public static string UrlEncode (string s) - { - return UrlEncode (s, Encoding.UTF8); - } - - public static string UrlEncode (string s, Encoding encoding) - { - int len; - if (s == null || (len = s.Length) == 0) - return s; - - var needEncode = false; - foreach (var c in s) { - if ((c < '0') || (c < 'A' && c > '9') || (c > 'Z' && c < 'a') || (c > 'z')) { - if (notEncoded (c)) - continue; - - needEncode = true; - break; + result.WriteByte((byte)c); } - } - - if (!needEncode) - return s; - - if (encoding == null) - encoding = Encoding.UTF8; - // Avoided GetByteCount call. - var bytes = new byte[encoding.GetMaxByteCount (len)]; - var realLen = encoding.GetBytes (s, 0, len, bytes, 0); - - return Encoding.ASCII.GetString (InternalUrlEncodeToBytes (bytes, 0, realLen)); - } - - public static string UrlEncode (byte[] bytes, int offset, int count) - { - var encoded = UrlEncodeToBytes (bytes, offset, count); - return encoded == null - ? null - : encoded.Length == 0 - ? String.Empty - : Encoding.ASCII.GetString (encoded); - } - - public static byte[] UrlEncodeToBytes (byte[] bytes) - { - int len; - return bytes != null && (len = bytes.Length) > 0 - ? InternalUrlEncodeToBytes (bytes, 0, len) - : bytes; - } - - public static byte[] UrlEncodeToBytes (string s) - { - return UrlEncodeToBytes (s, Encoding.UTF8); - } - - public static byte[] UrlEncodeToBytes (string s, Encoding encoding) - { - if (s == null) - return null; - - if (s.Length == 0) - return new byte[0]; - - var bytes = (encoding ?? Encoding.UTF8).GetBytes (s); - return InternalUrlEncodeToBytes (bytes, 0, bytes.Length); - } - - public static byte[] UrlEncodeToBytes (byte[] bytes, int offset, int count) - { - int len; - if (bytes == null || (len = bytes.Length) == 0) - return bytes; - - if (count == 0) - return new byte[0]; - - if (offset < 0 || offset >= len) - throw new ArgumentOutOfRangeException ("offset"); - - if (count < 0 || count > len - offset) - throw new ArgumentOutOfRangeException ("count"); - - return InternalUrlEncodeToBytes (bytes, offset, count); - } - - public static string UrlEncodeUnicode (string s) - { - return s != null && s.Length > 0 - ? Encoding.ASCII.GetString (InternalUrlEncodeUnicodeToBytes (s)) - : s; - } - - public static byte[] UrlEncodeUnicodeToBytes (string s) - { - return s == null - ? null - : s.Length == 0 - ? new byte[0] - : InternalUrlEncodeUnicodeToBytes (s); - } - - public static string UrlPathEncode (string s) - { - if (s == null || s.Length == 0) - return s; + /// + /// The writeCharBytes + /// + /// The c + /// The buffer + /// The encoding + private static void writeCharBytes(char c, IList buffer, Encoding encoding) + { + if (c > 255) + { + foreach (var b in encoding.GetBytes(new[] { c })) + buffer.Add(b); + + return; + } - using (var res = new MemoryStream ()) { - foreach (var c in s) - urlPathEncode (c, res); + buffer.Add((byte)c); + } - res.Close (); - return Encoding.ASCII.GetString (res.ToArray ()); - } + #endregion 方法 } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/HttpVersion.cs b/src/WebSocket/WebSocketCore/Net/HttpVersion.cs index 224893456..ad8039d21 100644 --- a/src/WebSocket/WebSocketCore/Net/HttpVersion.cs +++ b/src/WebSocket/WebSocketCore/Net/HttpVersion.cs @@ -1,4 +1,3 @@ -#region License /* * HttpVersion.cs * @@ -27,47 +26,44 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Lawrence Pit */ -#endregion using System; namespace WebSocketCore.Net { - /// - /// Provides the HTTP version numbers. - /// - public class HttpVersion - { - #region Public Fields - /// - /// Provides a instance for the HTTP/1.0. + /// Provides the HTTP version numbers. /// - public static readonly Version Version10 = new Version (1, 0); + public class HttpVersion + { + #region ֶ - /// - /// Provides a instance for the HTTP/1.1. - /// - public static readonly Version Version11 = new Version (1, 1); + /// + /// Provides a instance for the HTTP/1.0. + /// + public static readonly Version Version10 = new Version(1, 0); - #endregion + /// + /// Provides a instance for the HTTP/1.1. + /// + public static readonly Version Version11 = new Version(1, 1); - #region Public Constructors + #endregion ֶ - /// - /// Initializes a new instance of the class. - /// - public HttpVersion () - { - } + #region 캯 - #endregion - } -} + /// + /// Initializes a new instance of the class. + /// + public HttpVersion() + { + } + + #endregion 캯 + } +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/InputChunkState.cs b/src/WebSocket/WebSocketCore/Net/InputChunkState.cs index 72aa533ee..bc1cb6d40 100644 --- a/src/WebSocket/WebSocketCore/Net/InputChunkState.cs +++ b/src/WebSocket/WebSocketCore/Net/InputChunkState.cs @@ -1,4 +1,3 @@ -#region License /* * InputChunkState.cs * @@ -28,25 +27,48 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Gonzalo Paniagua Javier */ -#endregion using System; namespace WebSocketCore.Net { - internal enum InputChunkState - { - None, - Data, - DataEnded, - Trailer, - End - } -} + #region ö + + /// + /// Defines the InputChunkState + /// + internal enum InputChunkState + { + /// + /// Defines the None + /// + None, + + /// + /// Defines the Data + /// + Data, + + /// + /// Defines the DataEnded + /// + DataEnded, + + /// + /// Defines the Trailer + /// + Trailer, + + /// + /// Defines the End + /// + End + } + + #endregion ö +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/InputState.cs b/src/WebSocket/WebSocketCore/Net/InputState.cs index d4f419479..9c98842e7 100644 --- a/src/WebSocket/WebSocketCore/Net/InputState.cs +++ b/src/WebSocket/WebSocketCore/Net/InputState.cs @@ -1,4 +1,3 @@ -#region License /* * InputState.cs * @@ -28,22 +27,33 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Gonzalo Paniagua Javier */ -#endregion using System; namespace WebSocketCore.Net { - internal enum InputState - { - RequestLine, - Headers - } -} + #region ö + + /// + /// Defines the InputState + /// + internal enum InputState + { + /// + /// Defines the RequestLine + /// + RequestLine, + + /// + /// Defines the Headers + /// + Headers + } + + #endregion ö +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/LineState.cs b/src/WebSocket/WebSocketCore/Net/LineState.cs index 10d9d9342..2a9d64414 100644 --- a/src/WebSocket/WebSocketCore/Net/LineState.cs +++ b/src/WebSocket/WebSocketCore/Net/LineState.cs @@ -1,4 +1,3 @@ -#region License /* * LineState.cs * @@ -28,23 +27,38 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Gonzalo Paniagua Javier */ -#endregion using System; namespace WebSocketCore.Net { - internal enum LineState - { - None, - Cr, - Lf - } -} + #region ö + + /// + /// Defines the LineState + /// + internal enum LineState + { + /// + /// Defines the None + /// + None, + + /// + /// Defines the Cr + /// + Cr, + + /// + /// Defines the Lf + /// + Lf + } + + #endregion ö +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/NetworkCredential.cs b/src/WebSocket/WebSocketCore/Net/NetworkCredential.cs index 9f2a8d890..aade38376 100644 --- a/src/WebSocket/WebSocketCore/Net/NetworkCredential.cs +++ b/src/WebSocket/WebSocketCore/Net/NetworkCredential.cs @@ -1,4 +1,3 @@ -#region License /* * NetworkCredential.cs * @@ -24,186 +23,160 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; namespace WebSocketCore.Net { - /// - /// Provides the credentials for the password-based authentication. - /// - public class NetworkCredential - { - #region Private Fields - - private string _domain; - private static readonly string[] _noRoles; - private string _password; - private string[] _roles; - private string _username; - - #endregion - - #region Static Constructor - - static NetworkCredential () - { - _noRoles = new string[0]; - } - - #endregion - - #region Public Constructors - /// - /// Initializes a new instance of the class with - /// the specified and . + /// Provides the credentials for the password-based authentication. /// - /// - /// A that represents the username associated with - /// the credentials. - /// - /// - /// A that represents the password for the username - /// associated with the credentials. - /// - /// - /// is . - /// - /// - /// is empty. - /// - public NetworkCredential (string username, string password) - : this (username, password, null, null) + public class NetworkCredential { - } - - /// - /// Initializes a new instance of the class with - /// the specified , , - /// and . - /// - /// - /// A that represents the username associated with - /// the credentials. - /// - /// - /// A that represents the password for the username - /// associated with the credentials. - /// - /// - /// A that represents the domain associated with - /// the credentials. - /// - /// - /// An array of that represents the roles - /// associated with the credentials if any. - /// - /// - /// is . - /// - /// - /// is empty. - /// - public NetworkCredential ( + #region ֶ + + /// + /// Defines the _noRoles + /// + private static readonly string[] _noRoles; + + /// + /// Defines the _domain + /// + private string _domain; + + /// + /// Defines the _password + /// + private string _password; + + /// + /// Defines the _roles + /// + private string[] _roles; + + /// + /// Defines the _username + /// + private string _username; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The username + /// The password + /// The domain + /// The roles + public NetworkCredential( string username, string password, string domain, params string[] roles ) - { - if (username == null) - throw new ArgumentNullException ("username"); - - if (username.Length == 0) - throw new ArgumentException ("An empty string.", "username"); - - _username = username; - _password = password; - _domain = domain; - _roles = roles; + { + if (username == null) + throw new ArgumentNullException("username"); + + if (username.Length == 0) + throw new ArgumentException("An empty string.", "username"); + + _username = username; + _password = password; + _domain = domain; + _roles = roles; + } + + /// + /// Initializes a new instance of the class. + /// + /// The username + /// The password + public NetworkCredential(string username, string password) + : this(username, password, null, null) + { + } + + /// + /// Initializes static members of the class. + /// + static NetworkCredential() + { + _noRoles = new string[0]; + } + + #endregion 캯 + + #region + + /// + /// Gets or sets the Domain + /// Gets the domain associated with the credentials. + /// + public string Domain + { + get + { + return _domain ?? String.Empty; + } + + internal set + { + _domain = value; + } + } + + /// + /// Gets or sets the Password + /// Gets the password for the username associated with the credentials. + /// + public string Password + { + get + { + return _password ?? String.Empty; + } + + internal set + { + _password = value; + } + } + + /// + /// Gets or sets the Roles + /// Gets the roles associated with the credentials. + /// + public string[] Roles + { + get + { + return _roles ?? _noRoles; + } + + internal set + { + _roles = value; + } + } + + /// + /// Gets or sets the Username + /// Gets the username associated with the credentials. + /// + public string Username + { + get + { + return _username; + } + + internal set + { + _username = value; + } + } + + #endregion } - - #endregion - - #region Public Properties - - /// - /// Gets the domain associated with the credentials. - /// - /// - /// This property returns an empty string if the domain was - /// initialized with . - /// - /// - /// A that represents the domain name - /// to which the username belongs. - /// - public string Domain { - get { - return _domain ?? String.Empty; - } - - internal set { - _domain = value; - } - } - - /// - /// Gets the password for the username associated with the credentials. - /// - /// - /// This property returns an empty string if the password was - /// initialized with . - /// - /// - /// A that represents the password. - /// - public string Password { - get { - return _password ?? String.Empty; - } - - internal set { - _password = value; - } - } - - /// - /// Gets the roles associated with the credentials. - /// - /// - /// This property returns an empty array if the roles were - /// initialized with . - /// - /// - /// An array of that represents the role names - /// to which the username belongs. - /// - public string[] Roles { - get { - return _roles ?? _noRoles; - } - - internal set { - _roles = value; - } - } - - /// - /// Gets the username associated with the credentials. - /// - /// - /// A that represents the username. - /// - public string Username { - get { - return _username; - } - - internal set { - _username = value; - } - } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/QueryStringCollection.cs b/src/WebSocket/WebSocketCore/Net/QueryStringCollection.cs index 7d9214898..103210877 100644 --- a/src/WebSocket/WebSocketCore/Net/QueryStringCollection.cs +++ b/src/WebSocket/WebSocketCore/Net/QueryStringCollection.cs @@ -1,4 +1,3 @@ -#region License /* * QueryStringCollection.cs * @@ -28,9 +27,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Patrik Torstensson @@ -38,7 +35,6 @@ * - Tim Coleman * - Gonzalo Paniagua Javier */ -#endregion using System; using System.Collections.Specialized; @@ -46,23 +42,34 @@ namespace WebSocketCore.Net { - internal sealed class QueryStringCollection : NameValueCollection - { - public override string ToString () + /// + /// Defines the + /// + internal sealed class QueryStringCollection : NameValueCollection { - var cnt = Count; - if (cnt == 0) - return String.Empty; + #region 方法 - var output = new StringBuilder (); - var keys = AllKeys; - foreach (var key in keys) - output.AppendFormat ("{0}={1}&", key, this [key]); + /// + /// The ToString + /// + /// The + public override string ToString() + { + var cnt = Count; + if (cnt == 0) + return String.Empty; - if (output.Length > 0) - output.Length--; + var output = new StringBuilder(); + var keys = AllKeys; + foreach (var key in keys) + output.AppendFormat("{0}={1}&", key, this[key]); - return output.ToString (); + if (output.Length > 0) + output.Length--; + + return output.ToString(); + } + + #endregion 方法 } - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/ReadBufferState.cs b/src/WebSocket/WebSocketCore/Net/ReadBufferState.cs index d894e4a5c..4dce93d08 100644 --- a/src/WebSocket/WebSocketCore/Net/ReadBufferState.cs +++ b/src/WebSocket/WebSocketCore/Net/ReadBufferState.cs @@ -1,4 +1,3 @@ -#region License /* * ReadBufferState.cs * @@ -28,97 +27,153 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Gonzalo Paniagua Javier */ -#endregion using System; namespace WebSocketCore.Net { - internal class ReadBufferState - { - #region Private Fields - - private HttpStreamAsyncResult _asyncResult; - private byte[] _buffer; - private int _count; - private int _initialCount; - private int _offset; - - #endregion - - #region Public Constructors - - public ReadBufferState ( - byte[] buffer, int offset, int count, HttpStreamAsyncResult asyncResult) + /// + /// Defines the + /// + internal class ReadBufferState { - _buffer = buffer; - _offset = offset; - _count = count; - _initialCount = count; - _asyncResult = asyncResult; - } - - #endregion - - #region Public Properties - - public HttpStreamAsyncResult AsyncResult { - get { - return _asyncResult; - } - - set { - _asyncResult = value; - } - } - - public byte[] Buffer { - get { - return _buffer; - } - - set { - _buffer = value; - } - } - - public int Count { - get { - return _count; - } - - set { - _count = value; - } - } - - public int InitialCount { - get { - return _initialCount; - } - - set { - _initialCount = value; - } - } - - public int Offset { - get { - return _offset; - } - - set { - _offset = value; - } + #region ֶ + + /// + /// Defines the _asyncResult + /// + private HttpStreamAsyncResult _asyncResult; + + /// + /// Defines the _buffer + /// + private byte[] _buffer; + + /// + /// Defines the _count + /// + private int _count; + + /// + /// Defines the _initialCount + /// + private int _initialCount; + + /// + /// Defines the _offset + /// + private int _offset; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The buffer + /// The offset + /// The count + /// The asyncResult + public ReadBufferState( + byte[] buffer, int offset, int count, HttpStreamAsyncResult asyncResult) + { + _buffer = buffer; + _offset = offset; + _count = count; + _initialCount = count; + _asyncResult = asyncResult; + } + + #endregion 캯 + + #region + + /// + /// Gets or sets the AsyncResult + /// + public HttpStreamAsyncResult AsyncResult + { + get + { + return _asyncResult; + } + + set + { + _asyncResult = value; + } + } + + /// + /// Gets or sets the Buffer + /// + public byte[] Buffer + { + get + { + return _buffer; + } + + set + { + _buffer = value; + } + } + + /// + /// Gets or sets the Count + /// + public int Count + { + get + { + return _count; + } + + set + { + _count = value; + } + } + + /// + /// Gets or sets the InitialCount + /// + public int InitialCount + { + get + { + return _initialCount; + } + + set + { + _initialCount = value; + } + } + + /// + /// Gets or sets the Offset + /// + public int Offset + { + get + { + return _offset; + } + + set + { + _offset = value; + } + } + + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/RequestStream.cs b/src/WebSocket/WebSocketCore/Net/RequestStream.cs index 1817a38d9..c8e242fe0 100644 --- a/src/WebSocket/WebSocketCore/Net/RequestStream.cs +++ b/src/WebSocket/WebSocketCore/Net/RequestStream.cs @@ -1,4 +1,3 @@ -#region License /* * RequestStream.cs * @@ -28,240 +27,365 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Gonzalo Paniagua Javier */ -#endregion using System; using System.IO; namespace WebSocketCore.Net { - internal class RequestStream : Stream - { - #region Private Fields - - private long _bodyLeft; - private byte[] _buffer; - private int _count; - private bool _disposed; - private int _offset; - private Stream _stream; - - #endregion - - #region Internal Constructors - - internal RequestStream (Stream stream, byte[] buffer, int offset, int count) - : this (stream, buffer, offset, count, -1) + /// + /// Defines the + /// + internal class RequestStream : Stream { - } - - internal RequestStream ( + #region ֶ + + /// + /// Defines the _bodyLeft + /// + private long _bodyLeft; + + /// + /// Defines the _buffer + /// + private byte[] _buffer; + + /// + /// Defines the _count + /// + private int _count; + + /// + /// Defines the _disposed + /// + private bool _disposed; + + /// + /// Defines the _offset + /// + private int _offset; + + /// + /// Defines the _stream + /// + private Stream _stream; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The stream + /// The buffer + /// The offset + /// The count + /// The contentLength + internal RequestStream( Stream stream, byte[] buffer, int offset, int count, long contentLength) - { - _stream = stream; - _buffer = buffer; - _offset = offset; - _count = count; - _bodyLeft = contentLength; - } - - #endregion - - #region Public Properties - - public override bool CanRead { - get { - return true; - } - } - - public override bool CanSeek { - get { - return false; - } - } - - public override bool CanWrite { - get { - return false; - } - } - - public override long Length { - get { - throw new NotSupportedException (); - } - } - - public override long Position { - get { - throw new NotSupportedException (); - } - - set { - throw new NotSupportedException (); - } - } - - #endregion - - #region Private Methods - - // Returns 0 if we can keep reading from the base stream, - // > 0 if we read something from the buffer, - // -1 if we had a content length set and we finished reading that many bytes. - private int fillFromBuffer (byte[] buffer, int offset, int count) - { - if (buffer == null) - throw new ArgumentNullException ("buffer"); - - if (offset < 0) - throw new ArgumentOutOfRangeException ("offset", "A negative value."); - - if (count < 0) - throw new ArgumentOutOfRangeException ("count", "A negative value."); - - var len = buffer.Length; - if (offset + count > len) - throw new ArgumentException ( - "The sum of 'offset' and 'count' is greater than 'buffer' length."); - - if (_bodyLeft == 0) - return -1; - - if (_count == 0 || count == 0) - return 0; - - if (count > _count) - count = _count; - - if (_bodyLeft > 0 && count > _bodyLeft) - count = (int) _bodyLeft; - - Buffer.BlockCopy (_buffer, _offset, buffer, offset, count); - _offset += count; - _count -= count; - if (_bodyLeft > 0) - _bodyLeft -= count; - - return count; - } - - #endregion - - #region Public Methods - - public override IAsyncResult BeginRead ( + { + _stream = stream; + _buffer = buffer; + _offset = offset; + _count = count; + _bodyLeft = contentLength; + } + + /// + /// Initializes a new instance of the class. + /// + /// The stream + /// The buffer + /// The offset + /// The count + internal RequestStream(Stream stream, byte[] buffer, int offset, int count) + : this(stream, buffer, offset, count, -1) + { + } + + #endregion 캯 + + #region + + /// + /// Gets a value indicating whether CanRead + /// + public override bool CanRead + { + get + { + return true; + } + } + + /// + /// Gets a value indicating whether CanSeek + /// + public override bool CanSeek + { + get + { + return false; + } + } + + /// + /// Gets a value indicating whether CanWrite + /// + public override bool CanWrite + { + get + { + return false; + } + } + + /// + /// Gets the Length + /// + public override long Length + { + get + { + throw new NotSupportedException(); + } + } + + /// + /// Gets or sets the Position + /// + public override long Position + { + get + { + throw new NotSupportedException(); + } + + set + { + throw new NotSupportedException(); + } + } + + #endregion + + #region + + /// + /// The BeginRead + /// + /// The buffer + /// The offset + /// The count + /// The callback + /// The state + /// The + public override IAsyncResult BeginRead( byte[] buffer, int offset, int count, AsyncCallback callback, object state) - { - if (_disposed) - throw new ObjectDisposedException (GetType ().ToString ()); - - var nread = fillFromBuffer (buffer, offset, count); - if (nread > 0 || nread == -1) { - var ares = new HttpStreamAsyncResult (callback, state); - ares.Buffer = buffer; - ares.Offset = offset; - ares.Count = count; - ares.SyncRead = nread > 0 ? nread : 0; - ares.Complete (); - - return ares; - } - - // Avoid reading past the end of the request to allow for HTTP pipelining. - if (_bodyLeft >= 0 && count > _bodyLeft) - count = (int) _bodyLeft; - - return _stream.BeginRead (buffer, offset, count, callback, state); - } - - public override IAsyncResult BeginWrite ( + { + if (_disposed) + throw new ObjectDisposedException(GetType().ToString()); + + var nread = fillFromBuffer(buffer, offset, count); + if (nread > 0 || nread == -1) + { + var ares = new HttpStreamAsyncResult(callback, state); + ares.Buffer = buffer; + ares.Offset = offset; + ares.Count = count; + ares.SyncRead = nread > 0 ? nread : 0; + ares.Complete(); + + return ares; + } + + // Avoid reading past the end of the request to allow for HTTP pipelining. + if (_bodyLeft >= 0 && count > _bodyLeft) + count = (int)_bodyLeft; + + return _stream.BeginRead(buffer, offset, count, callback, state); + } + + /// + /// The BeginWrite + /// + /// The buffer + /// The offset + /// The count + /// The callback + /// The state + /// The + public override IAsyncResult BeginWrite( byte[] buffer, int offset, int count, AsyncCallback callback, object state) - { - throw new NotSupportedException (); - } - - public override void Close () - { - _disposed = true; - } - - public override int EndRead (IAsyncResult asyncResult) - { - if (_disposed) - throw new ObjectDisposedException (GetType ().ToString ()); - - if (asyncResult == null) - throw new ArgumentNullException ("asyncResult"); - - if (asyncResult is HttpStreamAsyncResult) { - var ares = (HttpStreamAsyncResult) asyncResult; - if (!ares.IsCompleted) - ares.AsyncWaitHandle.WaitOne (); - - return ares.SyncRead; - } - - // Close on exception? - var nread = _stream.EndRead (asyncResult); - if (nread > 0 && _bodyLeft > 0) - _bodyLeft -= nread; - - return nread; - } - - public override void EndWrite (IAsyncResult asyncResult) - { - throw new NotSupportedException (); - } - - public override void Flush () - { - } - - public override int Read (byte[] buffer, int offset, int count) - { - if (_disposed) - throw new ObjectDisposedException (GetType ().ToString ()); - - // Call the fillFromBuffer method to check for buffer boundaries even when _bodyLeft is 0. - var nread = fillFromBuffer (buffer, offset, count); - if (nread == -1) // No more bytes available (Content-Length). - return 0; - - if (nread > 0) - return nread; - - nread = _stream.Read (buffer, offset, count); - if (nread > 0 && _bodyLeft > 0) - _bodyLeft -= nread; - - return nread; + { + throw new NotSupportedException(); + } + + /// + /// The Close + /// + public override void Close() + { + _disposed = true; + } + + /// + /// The EndRead + /// + /// The asyncResult + /// The + public override int EndRead(IAsyncResult asyncResult) + { + if (_disposed) + throw new ObjectDisposedException(GetType().ToString()); + + if (asyncResult == null) + throw new ArgumentNullException("asyncResult"); + + if (asyncResult is HttpStreamAsyncResult) + { + var ares = (HttpStreamAsyncResult)asyncResult; + if (!ares.IsCompleted) + ares.AsyncWaitHandle.WaitOne(); + + return ares.SyncRead; + } + + // Close on exception? + var nread = _stream.EndRead(asyncResult); + if (nread > 0 && _bodyLeft > 0) + _bodyLeft -= nread; + + return nread; + } + + /// + /// The EndWrite + /// + /// The asyncResult + public override void EndWrite(IAsyncResult asyncResult) + { + throw new NotSupportedException(); + } + + /// + /// The Flush + /// + public override void Flush() + { + } + + /// + /// The Read + /// + /// The buffer + /// The offset + /// The count + /// The + public override int Read(byte[] buffer, int offset, int count) + { + if (_disposed) + throw new ObjectDisposedException(GetType().ToString()); + + // Call the fillFromBuffer method to check for buffer boundaries even when _bodyLeft is 0. + var nread = fillFromBuffer(buffer, offset, count); + if (nread == -1) // No more bytes available (Content-Length). + return 0; + + if (nread > 0) + return nread; + + nread = _stream.Read(buffer, offset, count); + if (nread > 0 && _bodyLeft > 0) + _bodyLeft -= nread; + + return nread; + } + + /// + /// The Seek + /// + /// The offset + /// The origin + /// The + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + /// + /// The SetLength + /// + /// The value + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + /// + /// The Write + /// + /// The buffer + /// The offset + /// The count + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + + // Returns 0 if we can keep reading from the base stream, + // > 0 if we read something from the buffer, + // -1 if we had a content length set and we finished reading that many bytes. + /// + /// The fillFromBuffer + /// + /// The buffer + /// The offset + /// The count + /// The + private int fillFromBuffer(byte[] buffer, int offset, int count) + { + if (buffer == null) + throw new ArgumentNullException("buffer"); + + if (offset < 0) + throw new ArgumentOutOfRangeException("offset", "A negative value."); + + if (count < 0) + throw new ArgumentOutOfRangeException("count", "A negative value."); + + var len = buffer.Length; + if (offset + count > len) + throw new ArgumentException( + "The sum of 'offset' and 'count' is greater than 'buffer' length."); + + if (_bodyLeft == 0) + return -1; + + if (_count == 0 || count == 0) + return 0; + + if (count > _count) + count = _count; + + if (_bodyLeft > 0 && count > _bodyLeft) + count = (int)_bodyLeft; + + Buffer.BlockCopy(_buffer, _offset, buffer, offset, count); + _offset += count; + _count -= count; + if (_bodyLeft > 0) + _bodyLeft -= count; + + return count; + } + + #endregion } - - public override long Seek (long offset, SeekOrigin origin) - { - throw new NotSupportedException (); - } - - public override void SetLength (long value) - { - throw new NotSupportedException (); - } - - public override void Write (byte[] buffer, int offset, int count) - { - throw new NotSupportedException (); - } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/ResponseStream.cs b/src/WebSocket/WebSocketCore/Net/ResponseStream.cs index be3c77c4e..0fe35a0b6 100644 --- a/src/WebSocket/WebSocketCore/Net/ResponseStream.cs +++ b/src/WebSocket/WebSocketCore/Net/ResponseStream.cs @@ -1,4 +1,3 @@ -#region License /* * ResponseStream.cs * @@ -28,14 +27,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Gonzalo Paniagua Javier */ -#endregion using System; using System.IO; @@ -43,283 +39,469 @@ namespace WebSocketCore.Net { - internal class ResponseStream : Stream - { - #region Private Fields - - private MemoryStream _body; - private static readonly byte[] _crlf = new byte[] { 13, 10 }; - private bool _disposed; - private HttpListenerResponse _response; - private bool _sendChunked; - private Stream _stream; - private Action _write; - private Action _writeBody; - private Action _writeChunked; - - #endregion - - #region Internal Constructors - - internal ResponseStream ( - Stream stream, HttpListenerResponse response, bool ignoreWriteExceptions) - { - _stream = stream; - _response = response; - - if (ignoreWriteExceptions) { - _write = writeWithoutThrowingException; - _writeChunked = writeChunkedWithoutThrowingException; - } - else { - _write = stream.Write; - _writeChunked = writeChunked; - } - - _body = new MemoryStream (); - } - - #endregion - - #region Public Properties - - public override bool CanRead { - get { - return false; - } - } - - public override bool CanSeek { - get { - return false; - } - } - - public override bool CanWrite { - get { - return !_disposed; - } - } - - public override long Length { - get { - throw new NotSupportedException (); - } - } - - public override long Position { - get { - throw new NotSupportedException (); - } - - set { - throw new NotSupportedException (); - } - } - - #endregion - - #region Private Methods - - private bool flush (bool closing) + /// + /// Defines the + /// + internal class ResponseStream : Stream { - if (!_response.HeadersSent) { - if (!flushHeaders (closing)) { - if (closing) - _response.CloseConnection = true; - - return false; + #region ֶ + + /// + /// Defines the _crlf + /// + private static readonly byte[] _crlf = new byte[] { 13, 10 }; + + /// + /// Defines the _body + /// + private MemoryStream _body; + + /// + /// Defines the _disposed + /// + private bool _disposed; + + /// + /// Defines the _response + /// + private HttpListenerResponse _response; + + /// + /// Defines the _sendChunked + /// + private bool _sendChunked; + + /// + /// Defines the _stream + /// + private Stream _stream; + + /// + /// Defines the _write + /// + private Action _write; + + /// + /// Defines the _writeBody + /// + private Action _writeBody; + + /// + /// Defines the _writeChunked + /// + private Action _writeChunked; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The stream + /// The response + /// The ignoreWriteExceptions + internal ResponseStream( + Stream stream, HttpListenerResponse response, bool ignoreWriteExceptions) + { + _stream = stream; + _response = response; + + if (ignoreWriteExceptions) + { + _write = writeWithoutThrowingException; + _writeChunked = writeChunkedWithoutThrowingException; + } + else + { + _write = stream.Write; + _writeChunked = writeChunked; + } + + _body = new MemoryStream(); } - _sendChunked = _response.SendChunked; - _writeBody = _sendChunked ? _writeChunked : _write; - } - - flushBody (closing); - if (closing && _sendChunked) { - var last = getChunkSizeBytes (0, true); - _write (last, 0, last.Length); - } + #endregion 캯 - return true; - } + #region - private void flushBody (bool closing) - { - using (_body) { - var len = _body.Length; - if (len > Int32.MaxValue) { - _body.Position = 0; - var buffLen = 1024; - var buff = new byte[buffLen]; - var nread = 0; - while ((nread = _body.Read (buff, 0, buffLen)) > 0) - _writeBody (buff, 0, nread); - } - else if (len > 0) { - _writeBody (_body.GetBuffer (), 0, (int) len); + /// + /// Gets a value indicating whether CanRead + /// + public override bool CanRead + { + get + { + return false; + } } - } - _body = !closing ? new MemoryStream () : null; - } + /// + /// Gets a value indicating whether CanSeek + /// + public override bool CanSeek + { + get + { + return false; + } + } - private bool flushHeaders (bool closing) - { - using (var buff = new MemoryStream ()) { - var headers = _response.WriteHeadersTo (buff); - var start = buff.Position; - var len = buff.Length - start; - if (len > 32768) - return false; - - if (!_response.SendChunked && _response.ContentLength64 != _body.Length) - return false; - - _write (buff.GetBuffer (), (int) start, (int) len); - _response.CloseConnection = headers["Connection"] == "close"; - _response.HeadersSent = true; - } - - return true; - } + /// + /// Gets a value indicating whether CanWrite + /// + public override bool CanWrite + { + get + { + return !_disposed; + } + } - private static byte[] getChunkSizeBytes (int size, bool final) - { - return Encoding.ASCII.GetBytes (String.Format ("{0:x}\r\n{1}", size, final ? "\r\n" : "")); - } + /// + /// Gets the Length + /// + public override long Length + { + get + { + throw new NotSupportedException(); + } + } - private void writeChunked (byte[] buffer, int offset, int count) - { - var size = getChunkSizeBytes (count, false); - _stream.Write (size, 0, size.Length); - _stream.Write (buffer, offset, count); - _stream.Write (_crlf, 0, 2); - } + /// + /// Gets or sets the Position + /// + public override long Position + { + get + { + throw new NotSupportedException(); + } + + set + { + throw new NotSupportedException(); + } + } - private void writeChunkedWithoutThrowingException (byte[] buffer, int offset, int count) - { - try { - writeChunked (buffer, offset, count); - } - catch { - } - } + #endregion - private void writeWithoutThrowingException (byte[] buffer, int offset, int count) - { - try { - _stream.Write (buffer, offset, count); - } - catch { - } - } + #region - #endregion + /// + /// The BeginRead + /// + /// The buffer + /// The offset + /// The count + /// The callback + /// The state + /// The + public override IAsyncResult BeginRead( + byte[] buffer, int offset, int count, AsyncCallback callback, object state) + { + throw new NotSupportedException(); + } - #region Internal Methods + /// + /// The BeginWrite + /// + /// The buffer + /// The offset + /// The count + /// The callback + /// The state + /// The + public override IAsyncResult BeginWrite( + byte[] buffer, int offset, int count, AsyncCallback callback, object state) + { + if (_disposed) + throw new ObjectDisposedException(GetType().ToString()); - internal void Close (bool force) - { - if (_disposed) - return; - - _disposed = true; - if (!force && flush (true)) { - _response.Close (); - } - else { - if (_sendChunked) { - var last = getChunkSizeBytes (0, true); - _write (last, 0, last.Length); + return _body.BeginWrite(buffer, offset, count, callback, state); } - _body.Dispose (); - _body = null; + /// + /// The Close + /// + public override void Close() + { + Close(false); + } - _response.Abort (); - } + /// + /// The EndRead + /// + /// The asyncResult + /// The + public override int EndRead(IAsyncResult asyncResult) + { + throw new NotSupportedException(); + } - _response = null; - _stream = null; - } + /// + /// The EndWrite + /// + /// The asyncResult + public override void EndWrite(IAsyncResult asyncResult) + { + if (_disposed) + throw new ObjectDisposedException(GetType().ToString()); - internal void InternalWrite (byte[] buffer, int offset, int count) - { - _write (buffer, offset, count); - } + _body.EndWrite(asyncResult); + } - #endregion + /// + /// The Flush + /// + public override void Flush() + { + if (!_disposed && (_sendChunked || _response.SendChunked)) + flush(false); + } - #region Public Methods + /// + /// The Read + /// + /// The buffer + /// The offset + /// The count + /// The + public override int Read(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } - public override IAsyncResult BeginRead ( - byte[] buffer, int offset, int count, AsyncCallback callback, object state) - { - throw new NotSupportedException (); - } + /// + /// The Seek + /// + /// The offset + /// The origin + /// The + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } - public override IAsyncResult BeginWrite ( - byte[] buffer, int offset, int count, AsyncCallback callback, object state) - { - if (_disposed) - throw new ObjectDisposedException (GetType ().ToString ()); + /// + /// The SetLength + /// + /// The value + public override void SetLength(long value) + { + throw new NotSupportedException(); + } - return _body.BeginWrite (buffer, offset, count, callback, state); - } + /// + /// The Write + /// + /// The buffer + /// The offset + /// The count + public override void Write(byte[] buffer, int offset, int count) + { + if (_disposed) + throw new ObjectDisposedException(GetType().ToString()); + + _body.Write(buffer, offset, count); + } - public override void Close () - { - Close (false); - } + /// + /// The Close + /// + /// The force + internal void Close(bool force) + { + if (_disposed) + return; + + _disposed = true; + if (!force && flush(true)) + { + _response.Close(); + } + else + { + if (_sendChunked) + { + var last = getChunkSizeBytes(0, true); + _write(last, 0, last.Length); + } + + _body.Dispose(); + _body = null; + + _response.Abort(); + } + + _response = null; + _stream = null; + } - protected override void Dispose (bool disposing) - { - Close (!disposing); - } + /// + /// The InternalWrite + /// + /// The buffer + /// The offset + /// The count + internal void InternalWrite(byte[] buffer, int offset, int count) + { + _write(buffer, offset, count); + } - public override int EndRead (IAsyncResult asyncResult) - { - throw new NotSupportedException (); - } + /// + /// The Dispose + /// + /// The disposing + protected override void Dispose(bool disposing) + { + Close(!disposing); + } - public override void EndWrite (IAsyncResult asyncResult) - { - if (_disposed) - throw new ObjectDisposedException (GetType ().ToString ()); + /// + /// The getChunkSizeBytes + /// + /// The size + /// The final + /// The + private static byte[] getChunkSizeBytes(int size, bool final) + { + return Encoding.ASCII.GetBytes(String.Format("{0:x}\r\n{1}", size, final ? "\r\n" : "")); + } - _body.EndWrite (asyncResult); - } + /// + /// The flush + /// + /// The closing + /// The + private bool flush(bool closing) + { + if (!_response.HeadersSent) + { + if (!flushHeaders(closing)) + { + if (closing) + _response.CloseConnection = true; + + return false; + } + + _sendChunked = _response.SendChunked; + _writeBody = _sendChunked ? _writeChunked : _write; + } + + flushBody(closing); + if (closing && _sendChunked) + { + var last = getChunkSizeBytes(0, true); + _write(last, 0, last.Length); + } + + return true; + } - public override void Flush () - { - if (!_disposed && (_sendChunked || _response.SendChunked)) - flush (false); - } + /// + /// The flushBody + /// + /// The closing + private void flushBody(bool closing) + { + using (_body) + { + var len = _body.Length; + if (len > Int32.MaxValue) + { + _body.Position = 0; + var buffLen = 1024; + var buff = new byte[buffLen]; + var nread = 0; + while ((nread = _body.Read(buff, 0, buffLen)) > 0) + _writeBody(buff, 0, nread); + } + else if (len > 0) + { + _writeBody(_body.GetBuffer(), 0, (int)len); + } + } + + _body = !closing ? new MemoryStream() : null; + } - public override int Read (byte[] buffer, int offset, int count) - { - throw new NotSupportedException (); - } + /// + /// The flushHeaders + /// + /// The closing + /// The + private bool flushHeaders(bool closing) + { + using (var buff = new MemoryStream()) + { + var headers = _response.WriteHeadersTo(buff); + var start = buff.Position; + var len = buff.Length - start; + if (len > 32768) + return false; + + if (!_response.SendChunked && _response.ContentLength64 != _body.Length) + return false; + + _write(buff.GetBuffer(), (int)start, (int)len); + _response.CloseConnection = headers["Connection"] == "close"; + _response.HeadersSent = true; + } + + return true; + } - public override long Seek (long offset, SeekOrigin origin) - { - throw new NotSupportedException (); - } + /// + /// The writeChunked + /// + /// The buffer + /// The offset + /// The count + private void writeChunked(byte[] buffer, int offset, int count) + { + var size = getChunkSizeBytes(count, false); + _stream.Write(size, 0, size.Length); + _stream.Write(buffer, offset, count); + _stream.Write(_crlf, 0, 2); + } - public override void SetLength (long value) - { - throw new NotSupportedException (); - } + /// + /// The writeChunkedWithoutThrowingException + /// + /// The buffer + /// The offset + /// The count + private void writeChunkedWithoutThrowingException(byte[] buffer, int offset, int count) + { + try + { + writeChunked(buffer, offset, count); + } + catch + { + } + } - public override void Write (byte[] buffer, int offset, int count) - { - if (_disposed) - throw new ObjectDisposedException (GetType ().ToString ()); + /// + /// The writeWithoutThrowingException + /// + /// The buffer + /// The offset + /// The count + private void writeWithoutThrowingException(byte[] buffer, int offset, int count) + { + try + { + _stream.Write(buffer, offset, count); + } + catch + { + } + } - _body.Write (buffer, offset, count); + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/ServerSslConfiguration.cs b/src/WebSocket/WebSocketCore/Net/ServerSslConfiguration.cs index 92d6c987c..f310e8a41 100644 --- a/src/WebSocket/WebSocketCore/Net/ServerSslConfiguration.cs +++ b/src/WebSocket/WebSocketCore/Net/ServerSslConfiguration.cs @@ -1,5 +1,4 @@ -#region License -/* +/* * ServerSslConfiguration.cs * * The MIT License @@ -25,14 +24,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Liryna */ -#endregion using System; using System.Net.Security; @@ -41,205 +37,188 @@ namespace WebSocketCore.Net { - /// - /// Stores the parameters for the used by servers. - /// - public class ServerSslConfiguration - { - #region Private Fields - - private bool _checkCertRevocation; - private bool _clientCertRequired; - private RemoteCertificateValidationCallback _clientCertValidationCallback; - private SslProtocols _enabledSslProtocols; - private X509Certificate2 _serverCert; - - #endregion - - #region Public Constructors - - /// - /// Initializes a new instance of the class. - /// - public ServerSslConfiguration () - { - _enabledSslProtocols = SslProtocols.Default; - } - - /// - /// Initializes a new instance of the class - /// with the specified . - /// - /// - /// A that represents the certificate used to - /// authenticate the server. - /// - public ServerSslConfiguration (X509Certificate2 serverCertificate) - { - _serverCert = serverCertificate; - _enabledSslProtocols = SslProtocols.Default; - } - /// - /// Copies the parameters from the specified to - /// a new instance of the class. + /// Stores the parameters for the used by servers. /// - /// - /// A from which to copy. - /// - /// - /// is . - /// - public ServerSslConfiguration (ServerSslConfiguration configuration) + public class ServerSslConfiguration { - if (configuration == null) - throw new ArgumentNullException ("configuration"); - - _checkCertRevocation = configuration._checkCertRevocation; - _clientCertRequired = configuration._clientCertRequired; - _clientCertValidationCallback = configuration._clientCertValidationCallback; - _enabledSslProtocols = configuration._enabledSslProtocols; - _serverCert = configuration._serverCert; - } - - #endregion - - #region Public Properties - - /// - /// Gets or sets a value indicating whether the certificate revocation - /// list is checked during authentication. - /// - /// - /// - /// true if the certificate revocation list is checked during - /// authentication; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - public bool CheckCertificateRevocation { - get { - return _checkCertRevocation; - } - - set { - _checkCertRevocation = value; - } - } - - /// - /// Gets or sets a value indicating whether the client is asked for - /// a certificate for authentication. - /// - /// - /// - /// true if the client is asked for a certificate for - /// authentication; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - public bool ClientCertificateRequired { - get { - return _clientCertRequired; - } - - set { - _clientCertRequired = value; - } - } - - /// - /// Gets or sets the callback used to validate the certificate - /// supplied by the client. - /// - /// - /// The certificate is valid if the callback returns true. - /// - /// - /// - /// A delegate that - /// invokes the method called for validating the certificate. - /// - /// - /// The default value is a delegate that invokes a method that - /// only returns true. - /// - /// - public RemoteCertificateValidationCallback ClientCertificateValidationCallback { - get { - if (_clientCertValidationCallback == null) - _clientCertValidationCallback = defaultValidateClientCertificate; - - return _clientCertValidationCallback; - } - - set { - _clientCertValidationCallback = value; - } - } - - /// - /// Gets or sets the protocols used for authentication. - /// - /// - /// - /// The enum values that represent - /// the protocols used for authentication. - /// - /// - /// The default value is . - /// - /// - public SslProtocols EnabledSslProtocols { - get { - return _enabledSslProtocols; - } - - set { - _enabledSslProtocols = value; - } - } - - /// - /// Gets or sets the certificate used to authenticate the server. - /// - /// - /// - /// A or - /// if not specified. - /// - /// - /// That instance represents an X.509 certificate. - /// - /// - public X509Certificate2 ServerCertificate { - get { - return _serverCert; - } - - set { - _serverCert = value; - } - } - - #endregion - - #region Private Methods - - private static bool defaultValidateClientCertificate ( + #region 字段 + + /// + /// Defines the _checkCertRevocation + /// + private bool _checkCertRevocation; + + /// + /// Defines the _clientCertRequired + /// + private bool _clientCertRequired; + + /// + /// Defines the _clientCertValidationCallback + /// + private RemoteCertificateValidationCallback _clientCertValidationCallback; + + /// + /// Defines the _enabledSslProtocols + /// + private SslProtocols _enabledSslProtocols; + + /// + /// Defines the _serverCert + /// + private X509Certificate2 _serverCert; + + #endregion 字段 + + #region 构造函数 + + /// + /// Initializes a new instance of the class. + /// + public ServerSslConfiguration() + { + _enabledSslProtocols = SslProtocols.Default; + } + + /// + /// Initializes a new instance of the class. + /// + /// The configuration + public ServerSslConfiguration(ServerSslConfiguration configuration) + { + if (configuration == null) + throw new ArgumentNullException("configuration"); + + _checkCertRevocation = configuration._checkCertRevocation; + _clientCertRequired = configuration._clientCertRequired; + _clientCertValidationCallback = configuration._clientCertValidationCallback; + _enabledSslProtocols = configuration._enabledSslProtocols; + _serverCert = configuration._serverCert; + } + + /// + /// Initializes a new instance of the class. + /// + /// The serverCertificate + public ServerSslConfiguration(X509Certificate2 serverCertificate) + { + _serverCert = serverCertificate; + _enabledSslProtocols = SslProtocols.Default; + } + + #endregion 构造函数 + + #region 属性 + + /// + /// Gets or sets a value indicating whether the certificate revocation + /// list is checked during authentication. + /// + public bool CheckCertificateRevocation + { + get + { + return _checkCertRevocation; + } + + set + { + _checkCertRevocation = value; + } + } + + /// + /// Gets or sets a value indicating whether the client is asked for + /// a certificate for authentication. + /// + public bool ClientCertificateRequired + { + get + { + return _clientCertRequired; + } + + set + { + _clientCertRequired = value; + } + } + + /// + /// Gets or sets the callback used to validate the certificate + /// supplied by the client. + /// + public RemoteCertificateValidationCallback ClientCertificateValidationCallback + { + get + { + if (_clientCertValidationCallback == null) + _clientCertValidationCallback = defaultValidateClientCertificate; + + return _clientCertValidationCallback; + } + + set + { + _clientCertValidationCallback = value; + } + } + + /// + /// Gets or sets the protocols used for authentication. + /// + public SslProtocols EnabledSslProtocols + { + get + { + return _enabledSslProtocols; + } + + set + { + _enabledSslProtocols = value; + } + } + + /// + /// Gets or sets the certificate used to authenticate the server. + /// + public X509Certificate2 ServerCertificate + { + get + { + return _serverCert; + } + + set + { + _serverCert = value; + } + } + + #endregion 属性 + + #region 方法 + + /// + /// The defaultValidateClientCertificate + /// + /// The sender + /// The certificate + /// The chain + /// The sslPolicyErrors + /// The + private static bool defaultValidateClientCertificate( object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors ) - { - return true; - } + { + return true; + } - #endregion - } -} + #endregion 方法 + } +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/WebHeaderCollection.cs b/src/WebSocket/WebSocketCore/Net/WebHeaderCollection.cs index a6df939ea..fd6b984ae 100644 --- a/src/WebSocket/WebSocketCore/Net/WebHeaderCollection.cs +++ b/src/WebSocket/WebSocketCore/Net/WebHeaderCollection.cs @@ -1,4 +1,3 @@ -#region License /* * WebHeaderCollection.cs * @@ -29,16 +28,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Authors /* * Authors: * - Lawrence Pit * - Gonzalo Paniagua Javier * - Miguel de Icaza */ -#endregion using System; using System.Collections; @@ -51,27 +47,21 @@ namespace WebSocketCore.Net { - /// - /// Provides a collection of the HTTP headers associated with a request or response. - /// - [Serializable] - [ComVisible (true)] - public class WebHeaderCollection : NameValueCollection, ISerializable - { - #region Private Fields - - private static readonly Dictionary _headers; - private bool _internallyUsed; - private HttpHeaderType _state; - - #endregion - - #region Static Constructor - - static WebHeaderCollection () + /// + /// Provides a collection of the HTTP headers associated with a request or response. + /// + [Serializable] + [ComVisible(true)] + public class WebHeaderCollection : NameValueCollection, ISerializable { - _headers = - new Dictionary (StringComparer.InvariantCultureIgnoreCase) { + private static readonly Dictionary _headers; + private bool _internallyUsed; + private HttpHeaderType _state; + + static WebHeaderCollection() + { + _headers = + new Dictionary(StringComparer.InvariantCultureIgnoreCase) { { "Accept", new HttpHeaderInfo ( @@ -444,1016 +434,993 @@ static WebHeaderCollection () "WWW-Authenticate", HttpHeaderType.Response | HttpHeaderType.Restricted | HttpHeaderType.MultiValue) } - }; - } - - #endregion - - #region Internal Constructors - - internal WebHeaderCollection (HttpHeaderType state, bool internallyUsed) - { - _state = state; - _internallyUsed = internallyUsed; - } - - #endregion - - #region Protected Constructors - - /// - /// Initializes a new instance of the class from - /// the specified and . - /// - /// - /// A that contains the serialized object data. - /// - /// - /// A that specifies the source for the deserialization. - /// - /// - /// is . - /// - /// - /// An element with the specified name isn't found in . - /// - protected WebHeaderCollection ( - SerializationInfo serializationInfo, StreamingContext streamingContext) - { - if (serializationInfo == null) - throw new ArgumentNullException ("serializationInfo"); - - try { - _internallyUsed = serializationInfo.GetBoolean ("InternallyUsed"); - _state = (HttpHeaderType) serializationInfo.GetInt32 ("State"); - - var cnt = serializationInfo.GetInt32 ("Count"); - for (var i = 0; i < cnt; i++) { - base.Add ( - serializationInfo.GetString (i.ToString ()), - serializationInfo.GetString ((cnt + i).ToString ())); + }; } - } - catch (SerializationException ex) { - throw new ArgumentException (ex.Message, "serializationInfo", ex); - } - } - - #endregion - - #region Public Constructors - - /// - /// Initializes a new instance of the class. - /// - public WebHeaderCollection () - { - } - #endregion - - #region Internal Properties - - internal HttpHeaderType State { - get { - return _state; - } - } - - #endregion - - #region Public Properties - - /// - /// Gets all header names in the collection. - /// - /// - /// An array of that contains all header names in the collection. - /// - public override string[] AllKeys { - get { - return base.AllKeys; - } - } - - /// - /// Gets the number of headers in the collection. - /// - /// - /// An that represents the number of headers in the collection. - /// - public override int Count { - get { - return base.Count; - } - } - - /// - /// Gets or sets the specified request in the collection. - /// - /// - /// A that represents the value of the request . - /// - /// - /// One of the enum values, represents - /// the request header to get or set. - /// - /// - /// - /// is a restricted header. - /// - /// - /// -or- - /// - /// - /// contains invalid characters. - /// - /// - /// - /// The length of is greater than 65,535 characters. - /// - /// - /// The current instance doesn't allow - /// the request . - /// - public string this[HttpRequestHeader header] { - get { - return Get (Convert (header)); - } - - set { - Add (header, value); - } - } - - /// - /// Gets or sets the specified response in the collection. - /// - /// - /// A that represents the value of the response . - /// - /// - /// One of the enum values, represents - /// the response header to get or set. - /// - /// - /// - /// is a restricted header. - /// - /// - /// -or- - /// - /// - /// contains invalid characters. - /// - /// - /// - /// The length of is greater than 65,535 characters. - /// - /// - /// The current instance doesn't allow - /// the response . - /// - public string this[HttpResponseHeader header] { - get { - return Get (Convert (header)); - } - - set { - Add (header, value); - } - } - - /// - /// Gets a collection of header names in the collection. - /// - /// - /// A that contains - /// all header names in the collection. - /// - public override NameObjectCollectionBase.KeysCollection Keys { - get { - return base.Keys; - } - } - - #endregion - - #region Private Methods - - private void add (string name, string value, bool ignoreRestricted) - { - var act = ignoreRestricted - ? (Action ) addWithoutCheckingNameAndRestricted - : addWithoutCheckingName; - - doWithCheckingState (act, checkName (name), value, true); - } + internal WebHeaderCollection(HttpHeaderType state, bool internallyUsed) + { + _state = state; + _internallyUsed = internallyUsed; + } - private void addWithoutCheckingName (string name, string value) - { - doWithoutCheckingName (base.Add, name, value); - } + /// + /// Initializes a new instance of the class from + /// the specified and . + /// + /// + /// A that contains the serialized object data. + /// + /// + /// A that specifies the source for the deserialization. + /// + /// + /// is . + /// + /// + /// An element with the specified name isn't found in . + /// + protected WebHeaderCollection( + SerializationInfo serializationInfo, StreamingContext streamingContext) + { + if (serializationInfo == null) + throw new ArgumentNullException("serializationInfo"); + + try + { + _internallyUsed = serializationInfo.GetBoolean("InternallyUsed"); + _state = (HttpHeaderType)serializationInfo.GetInt32("State"); + + var cnt = serializationInfo.GetInt32("Count"); + for (var i = 0; i < cnt; i++) + { + base.Add( + serializationInfo.GetString(i.ToString()), + serializationInfo.GetString((cnt + i).ToString())); + } + } + catch (SerializationException ex) + { + throw new ArgumentException(ex.Message, "serializationInfo", ex); + } + } - private void addWithoutCheckingNameAndRestricted (string name, string value) - { - base.Add (name, checkValue (value)); - } + /// + /// Initializes a new instance of the class. + /// + public WebHeaderCollection() + { + } - private static int checkColonSeparated (string header) - { - var idx = header.IndexOf (':'); - if (idx == -1) - throw new ArgumentException ("No colon could be found.", "header"); + internal HttpHeaderType State + { + get + { + return _state; + } + } - return idx; - } + /// + /// Gets all header names in the collection. + /// + /// + /// An array of that contains all header names in the collection. + /// + public override string[] AllKeys + { + get + { + return base.AllKeys; + } + } - private static HttpHeaderType checkHeaderType (string name) - { - var info = getHeaderInfo (name); - return info == null - ? HttpHeaderType.Unspecified - : info.IsRequest && !info.IsResponse - ? HttpHeaderType.Request - : !info.IsRequest && info.IsResponse - ? HttpHeaderType.Response - : HttpHeaderType.Unspecified; - } + /// + /// Gets the number of headers in the collection. + /// + /// + /// An that represents the number of headers in the collection. + /// + public override int Count + { + get + { + return base.Count; + } + } - private static string checkName (string name) - { - if (name == null || name.Length == 0) - throw new ArgumentNullException ("name"); + /// + /// Gets or sets the specified request in the collection. + /// + /// + /// A that represents the value of the request . + /// + /// + /// One of the enum values, represents + /// the request header to get or set. + /// + /// + /// + /// is a restricted header. + /// + /// + /// -or- + /// + /// + /// contains invalid characters. + /// + /// + /// + /// The length of is greater than 65,535 characters. + /// + /// + /// The current instance doesn't allow + /// the request . + /// + public string this[HttpRequestHeader header] + { + get + { + return Get(Convert(header)); + } + + set + { + Add(header, value); + } + } - name = name.Trim (); - if (!IsHeaderName (name)) - throw new ArgumentException ("Contains invalid characters.", "name"); + /// + /// Gets or sets the specified response in the collection. + /// + /// + /// A that represents the value of the response . + /// + /// + /// One of the enum values, represents + /// the response header to get or set. + /// + /// + /// + /// is a restricted header. + /// + /// + /// -or- + /// + /// + /// contains invalid characters. + /// + /// + /// + /// The length of is greater than 65,535 characters. + /// + /// + /// The current instance doesn't allow + /// the response . + /// + public string this[HttpResponseHeader header] + { + get + { + return Get(Convert(header)); + } + + set + { + Add(header, value); + } + } - return name; - } + /// + /// Gets a collection of header names in the collection. + /// + /// + /// A that contains + /// all header names in the collection. + /// + public override NameObjectCollectionBase.KeysCollection Keys + { + get + { + return base.Keys; + } + } - private void checkRestricted (string name) - { - if (!_internallyUsed && isRestricted (name, true)) - throw new ArgumentException ("This header must be modified with the appropiate property."); - } + private void add(string name, string value, bool ignoreRestricted) + { + var act = ignoreRestricted + ? (Action)addWithoutCheckingNameAndRestricted + : addWithoutCheckingName; - private void checkState (bool response) - { - if (_state == HttpHeaderType.Unspecified) - return; + doWithCheckingState(act, checkName(name), value, true); + } - if (response && _state == HttpHeaderType.Request) - throw new InvalidOperationException ( - "This collection has already been used to store the request headers."); + private void addWithoutCheckingName(string name, string value) + { + doWithoutCheckingName(base.Add, name, value); + } - if (!response && _state == HttpHeaderType.Response) - throw new InvalidOperationException ( - "This collection has already been used to store the response headers."); - } + private void addWithoutCheckingNameAndRestricted(string name, string value) + { + base.Add(name, checkValue(value)); + } - private static string checkValue (string value) - { - if (value == null || value.Length == 0) - return String.Empty; + private static int checkColonSeparated(string header) + { + var idx = header.IndexOf(':'); + if (idx == -1) + throw new ArgumentException("No colon could be found.", "header"); - value = value.Trim (); - if (value.Length > 65535) - throw new ArgumentOutOfRangeException ("value", "Greater than 65,535 characters."); + return idx; + } - if (!IsHeaderValue (value)) - throw new ArgumentException ("Contains invalid characters.", "value"); + private static HttpHeaderType checkHeaderType(string name) + { + var info = getHeaderInfo(name); + return info == null + ? HttpHeaderType.Unspecified + : info.IsRequest && !info.IsResponse + ? HttpHeaderType.Request + : !info.IsRequest && info.IsResponse + ? HttpHeaderType.Response + : HttpHeaderType.Unspecified; + } - return value; - } + private static string checkName(string name) + { + if (name == null || name.Length == 0) + throw new ArgumentNullException("name"); - private static string convert (string key) - { - HttpHeaderInfo info; - return _headers.TryGetValue (key, out info) ? info.Name : String.Empty; - } + name = name.Trim(); + if (!IsHeaderName(name)) + throw new ArgumentException("Contains invalid characters.", "name"); - private void doWithCheckingState ( - Action action, string name, string value, bool setState) - { - var type = checkHeaderType (name); - if (type == HttpHeaderType.Request) - doWithCheckingState (action, name, value, false, setState); - else if (type == HttpHeaderType.Response) - doWithCheckingState (action, name, value, true, setState); - else - action (name, value); - } + return name; + } - private void doWithCheckingState ( - Action action, string name, string value, bool response, bool setState) - { - checkState (response); - action (name, value); - if (setState && _state == HttpHeaderType.Unspecified) - _state = response ? HttpHeaderType.Response : HttpHeaderType.Request; - } + private void checkRestricted(string name) + { + if (!_internallyUsed && isRestricted(name, true)) + throw new ArgumentException("This header must be modified with the appropiate property."); + } - private void doWithoutCheckingName (Action action, string name, string value) - { - checkRestricted (name); - action (name, checkValue (value)); - } + private void checkState(bool response) + { + if (_state == HttpHeaderType.Unspecified) + return; - private static HttpHeaderInfo getHeaderInfo (string name) - { - foreach (var info in _headers.Values) - if (info.Name.Equals (name, StringComparison.InvariantCultureIgnoreCase)) - return info; + if (response && _state == HttpHeaderType.Request) + throw new InvalidOperationException( + "This collection has already been used to store the request headers."); - return null; - } + if (!response && _state == HttpHeaderType.Response) + throw new InvalidOperationException( + "This collection has already been used to store the response headers."); + } - private static bool isRestricted (string name, bool response) - { - var info = getHeaderInfo (name); - return info != null && info.IsRestricted (response); - } + private static string checkValue(string value) + { + if (value == null || value.Length == 0) + return String.Empty; - private void removeWithoutCheckingName (string name, string unuse) - { - checkRestricted (name); - base.Remove (name); - } + value = value.Trim(); + if (value.Length > 65535) + throw new ArgumentOutOfRangeException("value", "Greater than 65,535 characters."); - private void setWithoutCheckingName (string name, string value) - { - doWithoutCheckingName (base.Set, name, value); - } + if (!IsHeaderValue(value)) + throw new ArgumentException("Contains invalid characters.", "value"); - #endregion + return value; + } - #region Internal Methods + private static string convert(string key) + { + HttpHeaderInfo info; + return _headers.TryGetValue(key, out info) ? info.Name : String.Empty; + } - internal static string Convert (HttpRequestHeader header) - { - return convert (header.ToString ()); - } + private void doWithCheckingState( + Action action, string name, string value, bool setState) + { + var type = checkHeaderType(name); + if (type == HttpHeaderType.Request) + doWithCheckingState(action, name, value, false, setState); + else if (type == HttpHeaderType.Response) + doWithCheckingState(action, name, value, true, setState); + else + action(name, value); + } - internal static string Convert (HttpResponseHeader header) - { - return convert (header.ToString ()); - } + private void doWithCheckingState( + Action action, string name, string value, bool response, bool setState) + { + checkState(response); + action(name, value); + if (setState && _state == HttpHeaderType.Unspecified) + _state = response ? HttpHeaderType.Response : HttpHeaderType.Request; + } - internal void InternalRemove (string name) - { - base.Remove (name); - } + private void doWithoutCheckingName(Action action, string name, string value) + { + checkRestricted(name); + action(name, checkValue(value)); + } - internal void InternalSet (string header, bool response) - { - var pos = checkColonSeparated (header); - InternalSet (header.Substring (0, pos), header.Substring (pos + 1), response); - } + private static HttpHeaderInfo getHeaderInfo(string name) + { + foreach (var info in _headers.Values) + if (info.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) + return info; - internal void InternalSet (string name, string value, bool response) - { - value = checkValue (value); - if (IsMultiValue (name, response)) - base.Add (name, value); - else - base.Set (name, value); - } + return null; + } - internal static bool IsHeaderName (string name) - { - return name != null && name.Length > 0 && name.IsToken (); - } + private static bool isRestricted(string name, bool response) + { + var info = getHeaderInfo(name); + return info != null && info.IsRestricted(response); + } - internal static bool IsHeaderValue (string value) - { - return value.IsText (); - } + private void removeWithoutCheckingName(string name, string unuse) + { + checkRestricted(name); + base.Remove(name); + } - internal static bool IsMultiValue (string headerName, bool response) - { - if (headerName == null || headerName.Length == 0) - return false; + private void setWithoutCheckingName(string name, string value) + { + doWithoutCheckingName(base.Set, name, value); + } - var info = getHeaderInfo (headerName); - return info != null && info.IsMultiValue (response); - } + internal static string Convert(HttpRequestHeader header) + { + return convert(header.ToString()); + } - internal string ToStringMultiValue (bool response) - { - var buff = new StringBuilder (); - Count.Times ( - i => { - var key = GetKey (i); - if (IsMultiValue (key, response)) - foreach (var val in GetValues (i)) - buff.AppendFormat ("{0}: {1}\r\n", key, val); - else - buff.AppendFormat ("{0}: {1}\r\n", key, Get (i)); - }); - - return buff.Append ("\r\n").ToString (); - } + internal static string Convert(HttpResponseHeader header) + { + return convert(header.ToString()); + } - #endregion + internal void InternalRemove(string name) + { + base.Remove(name); + } - #region Protected Methods + internal void InternalSet(string header, bool response) + { + var pos = checkColonSeparated(header); + InternalSet(header.Substring(0, pos), header.Substring(pos + 1), response); + } - /// - /// Adds a header to the collection without checking if the header is on - /// the restricted header list. - /// - /// - /// A that represents the name of the header to add. - /// - /// - /// A that represents the value of the header to add. - /// - /// - /// is or empty. - /// - /// - /// or contains invalid characters. - /// - /// - /// The length of is greater than 65,535 characters. - /// - /// - /// The current instance doesn't allow - /// the . - /// - protected void AddWithoutValidate (string headerName, string headerValue) - { - add (headerName, headerValue, true); - } + internal void InternalSet(string name, string value, bool response) + { + value = checkValue(value); + if (IsMultiValue(name, response)) + base.Add(name, value); + else + base.Set(name, value); + } - #endregion + internal static bool IsHeaderName(string name) + { + return name != null && name.Length > 0 && name.IsToken(); + } - #region Public Methods + internal static bool IsHeaderValue(string value) + { + return value.IsText(); + } - /// - /// Adds the specified to the collection. - /// - /// - /// A that represents the header with the name and value separated by - /// a colon (':'). - /// - /// - /// is , empty, or the name part of - /// is empty. - /// - /// - /// - /// doesn't contain a colon. - /// - /// - /// -or- - /// - /// - /// is a restricted header. - /// - /// - /// -or- - /// - /// - /// The name or value part of contains invalid characters. - /// - /// - /// - /// The length of the value part of is greater than 65,535 characters. - /// - /// - /// The current instance doesn't allow - /// the . - /// - public void Add (string header) - { - if (header == null || header.Length == 0) - throw new ArgumentNullException ("header"); + internal static bool IsMultiValue(string headerName, bool response) + { + if (headerName == null || headerName.Length == 0) + return false; - var pos = checkColonSeparated (header); - add (header.Substring (0, pos), header.Substring (pos + 1), false); - } + var info = getHeaderInfo(headerName); + return info != null && info.IsMultiValue(response); + } - /// - /// Adds the specified request with - /// the specified to the collection. - /// - /// - /// One of the enum values, represents - /// the request header to add. - /// - /// - /// A that represents the value of the header to add. - /// - /// - /// - /// is a restricted header. - /// - /// - /// -or- - /// - /// - /// contains invalid characters. - /// - /// - /// - /// The length of is greater than 65,535 characters. - /// - /// - /// The current instance doesn't allow - /// the request . - /// - public void Add (HttpRequestHeader header, string value) - { - doWithCheckingState (addWithoutCheckingName, Convert (header), value, false, true); - } + internal string ToStringMultiValue(bool response) + { + var buff = new StringBuilder(); + Count.Times( + i => + { + var key = GetKey(i); + if (IsMultiValue(key, response)) + foreach (var val in GetValues(i)) + buff.AppendFormat("{0}: {1}\r\n", key, val); + else + buff.AppendFormat("{0}: {1}\r\n", key, Get(i)); + }); + + return buff.Append("\r\n").ToString(); + } - /// - /// Adds the specified response with - /// the specified to the collection. - /// - /// - /// One of the enum values, represents - /// the response header to add. - /// - /// - /// A that represents the value of the header to add. - /// - /// - /// - /// is a restricted header. - /// - /// - /// -or- - /// - /// - /// contains invalid characters. - /// - /// - /// - /// The length of is greater than 65,535 characters. - /// - /// - /// The current instance doesn't allow - /// the response . - /// - public void Add (HttpResponseHeader header, string value) - { - doWithCheckingState (addWithoutCheckingName, Convert (header), value, true, true); - } + /// + /// Adds a header to the collection without checking if the header is on + /// the restricted header list. + /// + /// + /// A that represents the name of the header to add. + /// + /// + /// A that represents the value of the header to add. + /// + /// + /// is or empty. + /// + /// + /// or contains invalid characters. + /// + /// + /// The length of is greater than 65,535 characters. + /// + /// + /// The current instance doesn't allow + /// the . + /// + protected void AddWithoutValidate(string headerName, string headerValue) + { + add(headerName, headerValue, true); + } - /// - /// Adds a header with the specified and - /// to the collection. - /// - /// - /// A that represents the name of the header to add. - /// - /// - /// A that represents the value of the header to add. - /// - /// - /// is or empty. - /// - /// - /// - /// or contains invalid characters. - /// - /// - /// -or- - /// - /// - /// is a restricted header name. - /// - /// - /// - /// The length of is greater than 65,535 characters. - /// - /// - /// The current instance doesn't allow - /// the header . - /// - public override void Add (string name, string value) - { - add (name, value, false); - } + /// + /// Adds the specified to the collection. + /// + /// + /// A that represents the header with the name and value separated by + /// a colon (':'). + /// + /// + /// is , empty, or the name part of + /// is empty. + /// + /// + /// + /// doesn't contain a colon. + /// + /// + /// -or- + /// + /// + /// is a restricted header. + /// + /// + /// -or- + /// + /// + /// The name or value part of contains invalid characters. + /// + /// + /// + /// The length of the value part of is greater than 65,535 characters. + /// + /// + /// The current instance doesn't allow + /// the . + /// + public void Add(string header) + { + if (header == null || header.Length == 0) + throw new ArgumentNullException("header"); + + var pos = checkColonSeparated(header); + add(header.Substring(0, pos), header.Substring(pos + 1), false); + } - /// - /// Removes all headers from the collection. - /// - public override void Clear () - { - base.Clear (); - _state = HttpHeaderType.Unspecified; - } + /// + /// Adds the specified request with + /// the specified to the collection. + /// + /// + /// One of the enum values, represents + /// the request header to add. + /// + /// + /// A that represents the value of the header to add. + /// + /// + /// + /// is a restricted header. + /// + /// + /// -or- + /// + /// + /// contains invalid characters. + /// + /// + /// + /// The length of is greater than 65,535 characters. + /// + /// + /// The current instance doesn't allow + /// the request . + /// + public void Add(HttpRequestHeader header, string value) + { + doWithCheckingState(addWithoutCheckingName, Convert(header), value, false, true); + } - /// - /// Get the value of the header at the specified in the collection. - /// - /// - /// A that receives the value of the header. - /// - /// - /// An that represents the zero-based index of the header to find. - /// - /// - /// is out of allowable range of indexes for the collection. - /// - public override string Get (int index) - { - return base.Get (index); - } + /// + /// Adds the specified response with + /// the specified to the collection. + /// + /// + /// One of the enum values, represents + /// the response header to add. + /// + /// + /// A that represents the value of the header to add. + /// + /// + /// + /// is a restricted header. + /// + /// + /// -or- + /// + /// + /// contains invalid characters. + /// + /// + /// + /// The length of is greater than 65,535 characters. + /// + /// + /// The current instance doesn't allow + /// the response . + /// + public void Add(HttpResponseHeader header, string value) + { + doWithCheckingState(addWithoutCheckingName, Convert(header), value, true, true); + } - /// - /// Get the value of the header with the specified in the collection. - /// - /// - /// A that receives the value of the header if found; - /// otherwise, . - /// - /// - /// A that represents the name of the header to find. - /// - public override string Get (string name) - { - return base.Get (name); - } + /// + /// Adds a header with the specified and + /// to the collection. + /// + /// + /// A that represents the name of the header to add. + /// + /// + /// A that represents the value of the header to add. + /// + /// + /// is or empty. + /// + /// + /// + /// or contains invalid characters. + /// + /// + /// -or- + /// + /// + /// is a restricted header name. + /// + /// + /// + /// The length of is greater than 65,535 characters. + /// + /// + /// The current instance doesn't allow + /// the header . + /// + public override void Add(string name, string value) + { + add(name, value, false); + } - /// - /// Gets the enumerator used to iterate through the collection. - /// - /// - /// An instance used to iterate through the collection. - /// - public override IEnumerator GetEnumerator () - { - return base.GetEnumerator (); - } + /// + /// Removes all headers from the collection. + /// + public override void Clear() + { + base.Clear(); + _state = HttpHeaderType.Unspecified; + } - /// - /// Get the name of the header at the specified in the collection. - /// - /// - /// A that receives the header name. - /// - /// - /// An that represents the zero-based index of the header to find. - /// - /// - /// is out of allowable range of indexes for the collection. - /// - public override string GetKey (int index) - { - return base.GetKey (index); - } + /// + /// Get the value of the header at the specified in the collection. + /// + /// + /// A that receives the value of the header. + /// + /// + /// An that represents the zero-based index of the header to find. + /// + /// + /// is out of allowable range of indexes for the collection. + /// + public override string Get(int index) + { + return base.Get(index); + } - /// - /// Gets an array of header values stored in the specified position of - /// the collection. - /// - /// - /// An array of that receives the header values if found; - /// otherwise, . - /// - /// - /// An that represents the zero-based index of the header to find. - /// - /// - /// is out of allowable range of indexes for the collection. - /// - public override string[] GetValues (int index) - { - var vals = base.GetValues (index); - return vals != null && vals.Length > 0 ? vals : null; - } + /// + /// Get the value of the header with the specified in the collection. + /// + /// + /// A that receives the value of the header if found; + /// otherwise, . + /// + /// + /// A that represents the name of the header to find. + /// + public override string Get(string name) + { + return base.Get(name); + } - /// - /// Gets an array of header values stored in the specified . - /// - /// - /// An array of that receives the header values if found; - /// otherwise, . - /// - /// - /// A that represents the name of the header to find. - /// - public override string[] GetValues (string header) - { - var vals = base.GetValues (header); - return vals != null && vals.Length > 0 ? vals : null; - } + /// + /// Gets the enumerator used to iterate through the collection. + /// + /// + /// An instance used to iterate through the collection. + /// + public override IEnumerator GetEnumerator() + { + return base.GetEnumerator(); + } - /// - /// Populates the specified with the data needed to serialize - /// the . - /// - /// - /// A that holds the serialized object data. - /// - /// - /// A that specifies the destination for the serialization. - /// - /// - /// is . - /// - [SecurityPermission ( - SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] - public override void GetObjectData ( - SerializationInfo serializationInfo, StreamingContext streamingContext) - { - if (serializationInfo == null) - throw new ArgumentNullException ("serializationInfo"); - - serializationInfo.AddValue ("InternallyUsed", _internallyUsed); - serializationInfo.AddValue ("State", (int) _state); - - var cnt = Count; - serializationInfo.AddValue ("Count", cnt); - cnt.Times ( - i => { - serializationInfo.AddValue (i.ToString (), GetKey (i)); - serializationInfo.AddValue ((cnt + i).ToString (), Get (i)); - }); - } + /// + /// Get the name of the header at the specified in the collection. + /// + /// + /// A that receives the header name. + /// + /// + /// An that represents the zero-based index of the header to find. + /// + /// + /// is out of allowable range of indexes for the collection. + /// + public override string GetKey(int index) + { + return base.GetKey(index); + } - /// - /// Determines whether the specified header can be set for the request. - /// - /// - /// true if the header is restricted; otherwise, false. - /// - /// - /// A that represents the name of the header to test. - /// - /// - /// is or empty. - /// - /// - /// contains invalid characters. - /// - public static bool IsRestricted (string headerName) - { - return isRestricted (checkName (headerName), false); - } + /// + /// Gets an array of header values stored in the specified position of + /// the collection. + /// + /// + /// An array of that receives the header values if found; + /// otherwise, . + /// + /// + /// An that represents the zero-based index of the header to find. + /// + /// + /// is out of allowable range of indexes for the collection. + /// + public override string[] GetValues(int index) + { + var vals = base.GetValues(index); + return vals != null && vals.Length > 0 ? vals : null; + } - /// - /// Determines whether the specified header can be set for the request or the response. - /// - /// - /// true if the header is restricted; otherwise, false. - /// - /// - /// A that represents the name of the header to test. - /// - /// - /// true if does the test for the response; for the request, false. - /// - /// - /// is or empty. - /// - /// - /// contains invalid characters. - /// - public static bool IsRestricted (string headerName, bool response) - { - return isRestricted (checkName (headerName), response); - } + /// + /// Gets an array of header values stored in the specified . + /// + /// + /// An array of that receives the header values if found; + /// otherwise, . + /// + /// + /// A that represents the name of the header to find. + /// + public override string[] GetValues(string header) + { + var vals = base.GetValues(header); + return vals != null && vals.Length > 0 ? vals : null; + } - /// - /// Implements the interface and raises the deserialization event - /// when the deserialization is complete. - /// - /// - /// An that represents the source of the deserialization event. - /// - public override void OnDeserialization (object sender) - { - } + /// + /// Populates the specified with the data needed to serialize + /// the . + /// + /// + /// A that holds the serialized object data. + /// + /// + /// A that specifies the destination for the serialization. + /// + /// + /// is . + /// + [SecurityPermission( + SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] + public override void GetObjectData( + SerializationInfo serializationInfo, StreamingContext streamingContext) + { + if (serializationInfo == null) + throw new ArgumentNullException("serializationInfo"); + + serializationInfo.AddValue("InternallyUsed", _internallyUsed); + serializationInfo.AddValue("State", (int)_state); + + var cnt = Count; + serializationInfo.AddValue("Count", cnt); + cnt.Times( + i => + { + serializationInfo.AddValue(i.ToString(), GetKey(i)); + serializationInfo.AddValue((cnt + i).ToString(), Get(i)); + }); + } - /// - /// Removes the specified request from the collection. - /// - /// - /// One of the enum values, represents - /// the request header to remove. - /// - /// - /// is a restricted header. - /// - /// - /// The current instance doesn't allow - /// the request . - /// - public void Remove (HttpRequestHeader header) - { - doWithCheckingState (removeWithoutCheckingName, Convert (header), null, false, false); - } + /// + /// Determines whether the specified header can be set for the request. + /// + /// + /// true if the header is restricted; otherwise, false. + /// + /// + /// A that represents the name of the header to test. + /// + /// + /// is or empty. + /// + /// + /// contains invalid characters. + /// + public static bool IsRestricted(string headerName) + { + return isRestricted(checkName(headerName), false); + } - /// - /// Removes the specified response from the collection. - /// - /// - /// One of the enum values, represents - /// the response header to remove. - /// - /// - /// is a restricted header. - /// - /// - /// The current instance doesn't allow - /// the response . - /// - public void Remove (HttpResponseHeader header) - { - doWithCheckingState (removeWithoutCheckingName, Convert (header), null, true, false); - } + /// + /// Determines whether the specified header can be set for the request or the response. + /// + /// + /// true if the header is restricted; otherwise, false. + /// + /// + /// A that represents the name of the header to test. + /// + /// + /// true if does the test for the response; for the request, false. + /// + /// + /// is or empty. + /// + /// + /// contains invalid characters. + /// + public static bool IsRestricted(string headerName, bool response) + { + return isRestricted(checkName(headerName), response); + } - /// - /// Removes the specified header from the collection. - /// - /// - /// A that represents the name of the header to remove. - /// - /// - /// is or empty. - /// - /// - /// - /// contains invalid characters. - /// - /// - /// -or- - /// - /// - /// is a restricted header name. - /// - /// - /// - /// The current instance doesn't allow - /// the header . - /// - public override void Remove (string name) - { - doWithCheckingState (removeWithoutCheckingName, checkName (name), null, false); - } + /// + /// Implements the interface and raises the deserialization event + /// when the deserialization is complete. + /// + /// + /// An that represents the source of the deserialization event. + /// + public override void OnDeserialization(object sender) + { + } - /// - /// Sets the specified request to the specified value. - /// - /// - /// One of the enum values, represents - /// the request header to set. - /// - /// - /// A that represents the value of the request header to set. - /// - /// - /// - /// is a restricted header. - /// - /// - /// -or- - /// - /// - /// contains invalid characters. - /// - /// - /// - /// The length of is greater than 65,535 characters. - /// - /// - /// The current instance doesn't allow - /// the request . - /// - public void Set (HttpRequestHeader header, string value) - { - doWithCheckingState (setWithoutCheckingName, Convert (header), value, false, true); - } + /// + /// Removes the specified request from the collection. + /// + /// + /// One of the enum values, represents + /// the request header to remove. + /// + /// + /// is a restricted header. + /// + /// + /// The current instance doesn't allow + /// the request . + /// + public void Remove(HttpRequestHeader header) + { + doWithCheckingState(removeWithoutCheckingName, Convert(header), null, false, false); + } - /// - /// Sets the specified response to the specified value. - /// - /// - /// One of the enum values, represents - /// the response header to set. - /// - /// - /// A that represents the value of the response header to set. - /// - /// - /// - /// is a restricted header. - /// - /// - /// -or- - /// - /// - /// contains invalid characters. - /// - /// - /// - /// The length of is greater than 65,535 characters. - /// - /// - /// The current instance doesn't allow - /// the response . - /// - public void Set (HttpResponseHeader header, string value) - { - doWithCheckingState (setWithoutCheckingName, Convert (header), value, true, true); - } + /// + /// Removes the specified response from the collection. + /// + /// + /// One of the enum values, represents + /// the response header to remove. + /// + /// + /// is a restricted header. + /// + /// + /// The current instance doesn't allow + /// the response . + /// + public void Remove(HttpResponseHeader header) + { + doWithCheckingState(removeWithoutCheckingName, Convert(header), null, true, false); + } - /// - /// Sets the specified header to the specified value. - /// - /// - /// A that represents the name of the header to set. - /// - /// - /// A that represents the value of the header to set. - /// - /// - /// is or empty. - /// - /// - /// - /// or contains invalid characters. - /// - /// - /// -or- - /// - /// - /// is a restricted header name. - /// - /// - /// - /// The length of is greater than 65,535 characters. - /// - /// - /// The current instance doesn't allow - /// the header . - /// - public override void Set (string name, string value) - { - doWithCheckingState (setWithoutCheckingName, checkName (name), value, true); - } + /// + /// Removes the specified header from the collection. + /// + /// + /// A that represents the name of the header to remove. + /// + /// + /// is or empty. + /// + /// + /// + /// contains invalid characters. + /// + /// + /// -or- + /// + /// + /// is a restricted header name. + /// + /// + /// + /// The current instance doesn't allow + /// the header . + /// + public override void Remove(string name) + { + doWithCheckingState(removeWithoutCheckingName, checkName(name), null, false); + } - /// - /// Converts the current to an array of . - /// - /// - /// An array of that receives the converted current - /// . - /// - public byte[] ToByteArray () - { - return Encoding.UTF8.GetBytes (ToString ()); - } + /// + /// Sets the specified request to the specified value. + /// + /// + /// One of the enum values, represents + /// the request header to set. + /// + /// + /// A that represents the value of the request header to set. + /// + /// + /// + /// is a restricted header. + /// + /// + /// -or- + /// + /// + /// contains invalid characters. + /// + /// + /// + /// The length of is greater than 65,535 characters. + /// + /// + /// The current instance doesn't allow + /// the request . + /// + public void Set(HttpRequestHeader header, string value) + { + doWithCheckingState(setWithoutCheckingName, Convert(header), value, false, true); + } - /// - /// Returns a that represents the current - /// . - /// - /// - /// A that represents the current . - /// - public override string ToString () - { - var buff = new StringBuilder (); - Count.Times (i => buff.AppendFormat ("{0}: {1}\r\n", GetKey (i), Get (i))); + /// + /// Sets the specified response to the specified value. + /// + /// + /// One of the enum values, represents + /// the response header to set. + /// + /// + /// A that represents the value of the response header to set. + /// + /// + /// + /// is a restricted header. + /// + /// + /// -or- + /// + /// + /// contains invalid characters. + /// + /// + /// + /// The length of is greater than 65,535 characters. + /// + /// + /// The current instance doesn't allow + /// the response . + /// + public void Set(HttpResponseHeader header, string value) + { + doWithCheckingState(setWithoutCheckingName, Convert(header), value, true, true); + } - return buff.Append ("\r\n").ToString (); - } + /// + /// Sets the specified header to the specified value. + /// + /// + /// A that represents the name of the header to set. + /// + /// + /// A that represents the value of the header to set. + /// + /// + /// is or empty. + /// + /// + /// + /// or contains invalid characters. + /// + /// + /// -or- + /// + /// + /// is a restricted header name. + /// + /// + /// + /// The length of is greater than 65,535 characters. + /// + /// + /// The current instance doesn't allow + /// the header . + /// + public override void Set(string name, string value) + { + doWithCheckingState(setWithoutCheckingName, checkName(name), value, true); + } - #endregion + /// + /// Converts the current to an array of . + /// + /// + /// An array of that receives the converted current + /// . + /// + public byte[] ToByteArray() + { + return Encoding.UTF8.GetBytes(ToString()); + } - #region Explicit Interface Implementations + /// + /// Returns a that represents the current + /// . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + var buff = new StringBuilder(); + Count.Times(i => buff.AppendFormat("{0}: {1}\r\n", GetKey(i), Get(i))); + + return buff.Append("\r\n").ToString(); + } - /// - /// Populates the specified with the data needed to serialize - /// the current . - /// - /// - /// A that holds the serialized object data. - /// - /// - /// A that specifies the destination for the serialization. - /// - /// - /// is . - /// - [SecurityPermission ( - SecurityAction.LinkDemand, - Flags = SecurityPermissionFlag.SerializationFormatter, - SerializationFormatter = true)] - void ISerializable.GetObjectData ( - SerializationInfo serializationInfo, StreamingContext streamingContext) - { - GetObjectData (serializationInfo, streamingContext); + /// + /// Populates the specified with the data needed to serialize + /// the current . + /// + /// + /// A that holds the serialized object data. + /// + /// + /// A that specifies the destination for the serialization. + /// + /// + /// is . + /// + [SecurityPermission( + SecurityAction.LinkDemand, + Flags = SecurityPermissionFlag.SerializationFormatter, + SerializationFormatter = true)] + void ISerializable.GetObjectData( + SerializationInfo serializationInfo, StreamingContext streamingContext) + { + GetObjectData(serializationInfo, streamingContext); + } } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/WebSockets/HttpListenerWebSocketContext.cs b/src/WebSocket/WebSocketCore/Net/WebSockets/HttpListenerWebSocketContext.cs index bbf679feb..4c5683553 100644 --- a/src/WebSocket/WebSocketCore/Net/WebSockets/HttpListenerWebSocketContext.cs +++ b/src/WebSocket/WebSocketCore/Net/WebSockets/HttpListenerWebSocketContext.cs @@ -1,4 +1,3 @@ -#region License /* * HttpListenerWebSocketContext.cs * @@ -24,7 +23,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; using System.Collections.Generic; @@ -34,361 +32,302 @@ namespace WebSocketCore.Net.WebSockets { - /// - /// Provides the access to the information in a WebSocket handshake request to - /// a instance. - /// - public class HttpListenerWebSocketContext : WebSocketContext - { - #region Private Fields + /// + /// Provides the access to the information in a WebSocket handshake request to + /// a instance. + /// + public class HttpListenerWebSocketContext : WebSocketContext + { + #region ֶ - private HttpListenerContext _context; - private WebSocket _websocket; + /// + /// Defines the _context + /// + private HttpListenerContext _context; - #endregion + /// + /// Defines the _websocket + /// + private WebSocket _websocket; - #region Internal Constructors + #endregion ֶ - internal HttpListenerWebSocketContext ( + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The context + /// The protocol + internal HttpListenerWebSocketContext( HttpListenerContext context, string protocol ) - { - _context = context; - _websocket = new WebSocket (this, protocol); - } - - #endregion - - #region Internal Properties - - internal Logger Log { - get { - return _context.Listener.Log; - } - } + { + _context = context; + _websocket = new WebSocket(this, protocol); + } - internal Stream Stream { - get { - return _context.Connection.Stream; - } - } + #endregion 캯 - #endregion + #region - #region Public Properties + /// + /// Gets the HTTP cookies included in the handshake request. + /// + public override CookieCollection CookieCollection + { + get + { + return _context.Request.Cookies; + } + } - /// - /// Gets the HTTP cookies included in the handshake request. - /// - /// - /// - /// A that contains - /// the cookies. - /// - /// - /// An empty collection if not included. - /// - /// - public override CookieCollection CookieCollection { - get { - return _context.Request.Cookies; - } - } + /// + /// Gets the HTTP headers included in the handshake request. + /// + public override NameValueCollection Headers + { + get + { + return _context.Request.Headers; + } + } - /// - /// Gets the HTTP headers included in the handshake request. - /// - /// - /// A that contains the headers. - /// - public override NameValueCollection Headers { - get { - return _context.Request.Headers; - } - } + /// + /// Gets the value of the Host header included in the handshake request. + /// + public override string Host + { + get + { + return _context.Request.UserHostName; + } + } - /// - /// Gets the value of the Host header included in the handshake request. - /// - /// - /// - /// A that represents the server host name requested - /// by the client. - /// - /// - /// It includes the port number if provided. - /// - /// - public override string Host { - get { - return _context.Request.UserHostName; - } - } + /// + /// Gets a value indicating whether the client is authenticated. + /// + public override bool IsAuthenticated + { + get + { + return _context.Request.IsAuthenticated; + } + } - /// - /// Gets a value indicating whether the client is authenticated. - /// - /// - /// true if the client is authenticated; otherwise, false. - /// - public override bool IsAuthenticated { - get { - return _context.Request.IsAuthenticated; - } - } + /// + /// Gets a value indicating whether the handshake request is sent from + /// the local computer. + /// + public override bool IsLocal + { + get + { + return _context.Request.IsLocal; + } + } - /// - /// Gets a value indicating whether the handshake request is sent from - /// the local computer. - /// - /// - /// true if the handshake request is sent from the same computer - /// as the server; otherwise, false. - /// - public override bool IsLocal { - get { - return _context.Request.IsLocal; - } - } + /// + /// Gets a value indicating whether a secure connection is used to send + /// the handshake request. + /// + public override bool IsSecureConnection + { + get + { + return _context.Request.IsSecureConnection; + } + } - /// - /// Gets a value indicating whether a secure connection is used to send - /// the handshake request. - /// - /// - /// true if the connection is secure; otherwise, false. - /// - public override bool IsSecureConnection { - get { - return _context.Request.IsSecureConnection; - } - } + /// + /// Gets a value indicating whether the request is a WebSocket handshake + /// request. + /// + public override bool IsWebSocketRequest + { + get + { + return _context.Request.IsWebSocketRequest; + } + } - /// - /// Gets a value indicating whether the request is a WebSocket handshake - /// request. - /// - /// - /// true if the request is a WebSocket handshake request; otherwise, - /// false. - /// - public override bool IsWebSocketRequest { - get { - return _context.Request.IsWebSocketRequest; - } - } + /// + /// Gets the value of the Origin header included in the handshake request. + /// + public override string Origin + { + get + { + return _context.Request.Headers["Origin"]; + } + } - /// - /// Gets the value of the Origin header included in the handshake request. - /// - /// - /// - /// A that represents the value of the Origin header. - /// - /// - /// if the header is not present. - /// - /// - public override string Origin { - get { - return _context.Request.Headers["Origin"]; - } - } + /// + /// Gets the query string included in the handshake request. + /// + public override NameValueCollection QueryString + { + get + { + return _context.Request.QueryString; + } + } - /// - /// Gets the query string included in the handshake request. - /// - /// - /// - /// A that contains the query - /// parameters. - /// - /// - /// An empty collection if not included. - /// - /// - public override NameValueCollection QueryString { - get { - return _context.Request.QueryString; - } - } + /// + /// Gets the URI requested by the client. + /// + public override Uri RequestUri + { + get + { + return _context.Request.Url; + } + } - /// - /// Gets the URI requested by the client. - /// - /// - /// - /// A that represents the URI parsed from the request. - /// - /// - /// if the URI cannot be parsed. - /// - /// - public override Uri RequestUri { - get { - return _context.Request.Url; - } - } + /// + /// Gets the value of the Sec-WebSocket-Key header included in + /// the handshake request. + /// + public override string SecWebSocketKey + { + get + { + return _context.Request.Headers["Sec-WebSocket-Key"]; + } + } - /// - /// Gets the value of the Sec-WebSocket-Key header included in - /// the handshake request. - /// - /// - /// - /// A that represents the value of - /// the Sec-WebSocket-Key header. - /// - /// - /// The value is used to prove that the server received - /// a valid WebSocket handshake request. - /// - /// - /// if the header is not present. - /// - /// - public override string SecWebSocketKey { - get { - return _context.Request.Headers["Sec-WebSocket-Key"]; - } - } + /// + /// Gets the names of the subprotocols from the Sec-WebSocket-Protocol + /// header included in the handshake request. + /// + public override IEnumerable SecWebSocketProtocols + { + get + { + var val = _context.Request.Headers["Sec-WebSocket-Protocol"]; + if (val == null || val.Length == 0) + yield break; + + foreach (var elm in val.Split(',')) + { + var protocol = elm.Trim(); + if (protocol.Length == 0) + continue; + + yield return protocol; + } + } + } - /// - /// Gets the names of the subprotocols from the Sec-WebSocket-Protocol - /// header included in the handshake request. - /// - /// - /// - /// An - /// instance. - /// - /// - /// It provides an enumerator which supports the iteration over - /// the collection of the names of the subprotocols. - /// - /// - public override IEnumerable SecWebSocketProtocols { - get { - var val = _context.Request.Headers["Sec-WebSocket-Protocol"]; - if (val == null || val.Length == 0) - yield break; - - foreach (var elm in val.Split (',')) { - var protocol = elm.Trim (); - if (protocol.Length == 0) - continue; - - yield return protocol; + /// + /// Gets the value of the Sec-WebSocket-Version header included in + /// the handshake request. + /// + public override string SecWebSocketVersion + { + get + { + return _context.Request.Headers["Sec-WebSocket-Version"]; + } } - } - } - /// - /// Gets the value of the Sec-WebSocket-Version header included in - /// the handshake request. - /// - /// - /// - /// A that represents the WebSocket protocol - /// version specified by the client. - /// - /// - /// if the header is not present. - /// - /// - public override string SecWebSocketVersion { - get { - return _context.Request.Headers["Sec-WebSocket-Version"]; - } - } + /// + /// Gets the endpoint to which the handshake request is sent. + /// + public override System.Net.IPEndPoint ServerEndPoint + { + get + { + return _context.Request.LocalEndPoint; + } + } - /// - /// Gets the endpoint to which the handshake request is sent. - /// - /// - /// A that represents the server IP - /// address and port number. - /// - public override System.Net.IPEndPoint ServerEndPoint { - get { - return _context.Request.LocalEndPoint; - } - } + /// + /// Gets the client information. + /// + public override IPrincipal User + { + get + { + return _context.User; + } + } - /// - /// Gets the client information. - /// - /// - /// - /// A instance that represents identity, - /// authentication, and security roles for the client. - /// - /// - /// if the client is not authenticated. - /// - /// - public override IPrincipal User { - get { - return _context.User; - } - } + /// + /// Gets the endpoint from which the handshake request is sent. + /// + public override System.Net.IPEndPoint UserEndPoint + { + get + { + return _context.Request.RemoteEndPoint; + } + } - /// - /// Gets the endpoint from which the handshake request is sent. - /// - /// - /// A that represents the client IP - /// address and port number. - /// - public override System.Net.IPEndPoint UserEndPoint { - get { - return _context.Request.RemoteEndPoint; - } - } + /// + /// Gets the WebSocket instance used for two-way communication between + /// the client and server. + /// + public override WebSocket WebSocket + { + get + { + return _websocket; + } + } - /// - /// Gets the WebSocket instance used for two-way communication between - /// the client and server. - /// - /// - /// A . - /// - public override WebSocket WebSocket { - get { - return _websocket; - } - } + /// + /// Gets the Log + /// + internal Logger Log + { + get + { + return _context.Listener.Log; + } + } - #endregion + /// + /// Gets the Stream + /// + internal Stream Stream + { + get + { + return _context.Connection.Stream; + } + } - #region Internal Methods + #endregion - internal void Close () - { - _context.Connection.Close (true); - } + #region - internal void Close (HttpStatusCode code) - { - _context.Response.Close (code); - } + /// + /// Returns a string that represents the current instance. + /// + /// The + public override string ToString() + { + return _context.Request.ToString(); + } - #endregion + /// + /// The Close + /// + internal void Close() + { + _context.Connection.Close(true); + } - #region Public Methods + /// + /// The Close + /// + /// The code + internal void Close(HttpStatusCode code) + { + _context.Response.Close(code); + } - /// - /// Returns a string that represents the current instance. - /// - /// - /// A that contains the request line and headers - /// included in the handshake request. - /// - public override string ToString () - { - return _context.Request.ToString (); + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/WebSockets/TcpListenerWebSocketContext.cs b/src/WebSocket/WebSocketCore/Net/WebSockets/TcpListenerWebSocketContext.cs index a76abfddb..2d03f2eb6 100644 --- a/src/WebSocket/WebSocketCore/Net/WebSockets/TcpListenerWebSocketContext.cs +++ b/src/WebSocket/WebSocketCore/Net/WebSockets/TcpListenerWebSocketContext.cs @@ -1,4 +1,3 @@ -#region License /* * TcpListenerWebSocketContext.cs * @@ -24,14 +23,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Contributors /* * Contributors: * - Liryna */ -#endregion using System; using System.Collections.Generic; @@ -44,475 +40,469 @@ namespace WebSocketCore.Net.WebSockets { - /// - /// Provides the access to the information in a WebSocket handshake request to - /// a instance. - /// - internal class TcpListenerWebSocketContext : WebSocketContext - { - #region Private Fields - - private Logger _log; - private NameValueCollection _queryString; - private HttpRequest _request; - private Uri _requestUri; - private bool _secure; - private System.Net.EndPoint _serverEndPoint; - private Stream _stream; - private TcpClient _tcpClient; - private IPrincipal _user; - private System.Net.EndPoint _userEndPoint; - private WebSocket _websocket; - - #endregion - - #region Internal Constructors - - internal TcpListenerWebSocketContext ( + /// + /// Provides the access to the information in a WebSocket handshake request to + /// a instance. + /// + internal class TcpListenerWebSocketContext : WebSocketContext + { + #region ֶ + + /// + /// Defines the _log + /// + private Logger _log; + + /// + /// Defines the _queryString + /// + private NameValueCollection _queryString; + + /// + /// Defines the _request + /// + private HttpRequest _request; + + /// + /// Defines the _requestUri + /// + private Uri _requestUri; + + /// + /// Defines the _secure + /// + private bool _secure; + + /// + /// Defines the _serverEndPoint + /// + private System.Net.EndPoint _serverEndPoint; + + /// + /// Defines the _stream + /// + private Stream _stream; + + /// + /// Defines the _tcpClient + /// + private TcpClient _tcpClient; + + /// + /// Defines the _user + /// + private IPrincipal _user; + + /// + /// Defines the _userEndPoint + /// + private System.Net.EndPoint _userEndPoint; + + /// + /// Defines the _websocket + /// + private WebSocket _websocket; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The tcpClient + /// The protocol + /// The secure + /// The sslConfig + /// The log + internal TcpListenerWebSocketContext( TcpClient tcpClient, string protocol, bool secure, ServerSslConfiguration sslConfig, Logger log ) - { - _tcpClient = tcpClient; - _secure = secure; - _log = log; - - var netStream = tcpClient.GetStream (); - if (secure) { - var sslStream = new SslStream ( - netStream, - false, - sslConfig.ClientCertificateValidationCallback - ); - - sslStream.AuthenticateAsServer ( - sslConfig.ServerCertificate, - sslConfig.ClientCertificateRequired, - sslConfig.EnabledSslProtocols, - sslConfig.CheckCertificateRevocation - ); - - _stream = sslStream; - } - else { - _stream = netStream; - } - - var sock = tcpClient.Client; - _serverEndPoint = sock.LocalEndPoint; - _userEndPoint = sock.RemoteEndPoint; - - _request = HttpRequest.Read (_stream, 90000); - _websocket = new WebSocket (this, protocol); - } - - #endregion - - #region Internal Properties - - internal Logger Log { - get { - return _log; - } - } - - internal Stream Stream { - get { - return _stream; - } - } - - #endregion - - #region Public Properties + { + _tcpClient = tcpClient; + _secure = secure; + _log = log; + + var netStream = tcpClient.GetStream(); + if (secure) + { + var sslStream = new SslStream( + netStream, + false, + sslConfig.ClientCertificateValidationCallback + ); + + sslStream.AuthenticateAsServer( + sslConfig.ServerCertificate, + sslConfig.ClientCertificateRequired, + sslConfig.EnabledSslProtocols, + sslConfig.CheckCertificateRevocation + ); + + _stream = sslStream; + } + else + { + _stream = netStream; + } + + var sock = tcpClient.Client; + _serverEndPoint = sock.LocalEndPoint; + _userEndPoint = sock.RemoteEndPoint; + + _request = HttpRequest.Read(_stream, 90000); + _websocket = new WebSocket(this, protocol); + } - /// - /// Gets the HTTP cookies included in the handshake request. - /// - /// - /// - /// A that contains - /// the cookies. - /// - /// - /// An empty collection if not included. - /// - /// - public override CookieCollection CookieCollection { - get { - return _request.Cookies; - } - } + #endregion 캯 - /// - /// Gets the HTTP headers included in the handshake request. - /// - /// - /// A that contains the headers. - /// - public override NameValueCollection Headers { - get { - return _request.Headers; - } - } + #region - /// - /// Gets the value of the Host header included in the handshake request. - /// - /// - /// - /// A that represents the server host name requested - /// by the client. - /// - /// - /// It includes the port number if provided. - /// - /// - public override string Host { - get { - return _request.Headers["Host"]; - } - } - - /// - /// Gets a value indicating whether the client is authenticated. - /// - /// - /// true if the client is authenticated; otherwise, false. - /// - public override bool IsAuthenticated { - get { - return _user != null; - } - } + /// + /// Gets the HTTP cookies included in the handshake request. + /// + public override CookieCollection CookieCollection + { + get + { + return _request.Cookies; + } + } - /// - /// Gets a value indicating whether the handshake request is sent from - /// the local computer. - /// - /// - /// true if the handshake request is sent from the same computer - /// as the server; otherwise, false. - /// - public override bool IsLocal { - get { - return UserEndPoint.Address.IsLocal (); - } - } + /// + /// Gets the HTTP headers included in the handshake request. + /// + public override NameValueCollection Headers + { + get + { + return _request.Headers; + } + } - /// - /// Gets a value indicating whether a secure connection is used to send - /// the handshake request. - /// - /// - /// true if the connection is secure; otherwise, false. - /// - public override bool IsSecureConnection { - get { - return _secure; - } - } + /// + /// Gets the value of the Host header included in the handshake request. + /// + public override string Host + { + get + { + return _request.Headers["Host"]; + } + } - /// - /// Gets a value indicating whether the request is a WebSocket handshake - /// request. - /// - /// - /// true if the request is a WebSocket handshake request; otherwise, - /// false. - /// - public override bool IsWebSocketRequest { - get { - return _request.IsWebSocketRequest; - } - } + /// + /// Gets a value indicating whether the client is authenticated. + /// + public override bool IsAuthenticated + { + get + { + return _user != null; + } + } - /// - /// Gets the value of the Origin header included in the handshake request. - /// - /// - /// - /// A that represents the value of the Origin header. - /// - /// - /// if the header is not present. - /// - /// - public override string Origin { - get { - return _request.Headers["Origin"]; - } - } + /// + /// Gets a value indicating whether the handshake request is sent from + /// the local computer. + /// + public override bool IsLocal + { + get + { + return UserEndPoint.Address.IsLocal(); + } + } - /// - /// Gets the query string included in the handshake request. - /// - /// - /// - /// A that contains the query - /// parameters. - /// - /// - /// An empty collection if not included. - /// - /// - public override NameValueCollection QueryString { - get { - if (_queryString == null) { - var uri = RequestUri; - _queryString = HttpUtility.InternalParseQueryString ( - uri != null ? uri.Query : null, - Encoding.UTF8 - ); + /// + /// Gets a value indicating whether a secure connection is used to send + /// the handshake request. + /// + public override bool IsSecureConnection + { + get + { + return _secure; + } } - return _queryString; - } - } + /// + /// Gets a value indicating whether the request is a WebSocket handshake + /// request. + /// + public override bool IsWebSocketRequest + { + get + { + return _request.IsWebSocketRequest; + } + } - /// - /// Gets the URI requested by the client. - /// - /// - /// - /// A that represents the URI parsed from the request. - /// - /// - /// if the URI cannot be parsed. - /// - /// - public override Uri RequestUri { - get { - if (_requestUri == null) { - _requestUri = HttpUtility.CreateRequestUrl ( - _request.RequestUri, - _request.Headers["Host"], - _request.IsWebSocketRequest, - _secure - ); + /// + /// Gets the value of the Origin header included in the handshake request. + /// + public override string Origin + { + get + { + return _request.Headers["Origin"]; + } } - return _requestUri; - } - } + /// + /// Gets the query string included in the handshake request. + /// + public override NameValueCollection QueryString + { + get + { + if (_queryString == null) + { + var uri = RequestUri; + _queryString = HttpUtility.InternalParseQueryString( + uri != null ? uri.Query : null, + Encoding.UTF8 + ); + } + + return _queryString; + } + } - /// - /// Gets the value of the Sec-WebSocket-Key header included in - /// the handshake request. - /// - /// - /// - /// A that represents the value of - /// the Sec-WebSocket-Key header. - /// - /// - /// The value is used to prove that the server received - /// a valid WebSocket handshake request. - /// - /// - /// if the header is not present. - /// - /// - public override string SecWebSocketKey { - get { - return _request.Headers["Sec-WebSocket-Key"]; - } - } + /// + /// Gets the URI requested by the client. + /// + public override Uri RequestUri + { + get + { + if (_requestUri == null) + { + _requestUri = HttpUtility.CreateRequestUrl( + _request.RequestUri, + _request.Headers["Host"], + _request.IsWebSocketRequest, + _secure + ); + } + + return _requestUri; + } + } - /// - /// Gets the names of the subprotocols from the Sec-WebSocket-Protocol - /// header included in the handshake request. - /// - /// - /// - /// An - /// instance. - /// - /// - /// It provides an enumerator which supports the iteration over - /// the collection of the names of the subprotocols. - /// - /// - public override IEnumerable SecWebSocketProtocols { - get { - var val = _request.Headers["Sec-WebSocket-Protocol"]; - if (val == null || val.Length == 0) - yield break; - - foreach (var elm in val.Split (',')) { - var protocol = elm.Trim (); - if (protocol.Length == 0) - continue; - - yield return protocol; + /// + /// Gets the value of the Sec-WebSocket-Key header included in + /// the handshake request. + /// + public override string SecWebSocketKey + { + get + { + return _request.Headers["Sec-WebSocket-Key"]; + } } - } - } - /// - /// Gets the value of the Sec-WebSocket-Version header included in - /// the handshake request. - /// - /// - /// - /// A that represents the WebSocket protocol - /// version specified by the client. - /// - /// - /// if the header is not present. - /// - /// - public override string SecWebSocketVersion { - get { - return _request.Headers["Sec-WebSocket-Version"]; - } - } + /// + /// Gets the names of the subprotocols from the Sec-WebSocket-Protocol + /// header included in the handshake request. + /// + public override IEnumerable SecWebSocketProtocols + { + get + { + var val = _request.Headers["Sec-WebSocket-Protocol"]; + if (val == null || val.Length == 0) + yield break; + + foreach (var elm in val.Split(',')) + { + var protocol = elm.Trim(); + if (protocol.Length == 0) + continue; + + yield return protocol; + } + } + } - /// - /// Gets the endpoint to which the handshake request is sent. - /// - /// - /// A that represents the server IP - /// address and port number. - /// - public override System.Net.IPEndPoint ServerEndPoint { - get { - return (System.Net.IPEndPoint) _serverEndPoint; - } - } + /// + /// Gets the value of the Sec-WebSocket-Version header included in + /// the handshake request. + /// + public override string SecWebSocketVersion + { + get + { + return _request.Headers["Sec-WebSocket-Version"]; + } + } - /// - /// Gets the client information. - /// - /// - /// - /// A instance that represents identity, - /// authentication, and security roles for the client. - /// - /// - /// if the client is not authenticated. - /// - /// - public override IPrincipal User { - get { - return _user; - } - } + /// + /// Gets the endpoint to which the handshake request is sent. + /// + public override System.Net.IPEndPoint ServerEndPoint + { + get + { + return (System.Net.IPEndPoint)_serverEndPoint; + } + } - /// - /// Gets the endpoint from which the handshake request is sent. - /// - /// - /// A that represents the client IP - /// address and port number. - /// - public override System.Net.IPEndPoint UserEndPoint { - get { - return (System.Net.IPEndPoint) _userEndPoint; - } - } + /// + /// Gets the client information. + /// + public override IPrincipal User + { + get + { + return _user; + } + } - /// - /// Gets the WebSocket instance used for two-way communication between - /// the client and server. - /// - /// - /// A . - /// - public override WebSocket WebSocket { - get { - return _websocket; - } - } + /// + /// Gets the endpoint from which the handshake request is sent. + /// + public override System.Net.IPEndPoint UserEndPoint + { + get + { + return (System.Net.IPEndPoint)_userEndPoint; + } + } - #endregion + /// + /// Gets the WebSocket instance used for two-way communication between + /// the client and server. + /// + public override WebSocket WebSocket + { + get + { + return _websocket; + } + } - #region Private Methods + /// + /// Gets the Log + /// + internal Logger Log + { + get + { + return _log; + } + } - private HttpRequest sendAuthenticationChallenge (string challenge) - { - var res = HttpResponse.CreateUnauthorizedResponse (challenge); - var bytes = res.ToByteArray (); - _stream.Write (bytes, 0, bytes.Length); + /// + /// Gets the Stream + /// + internal Stream Stream + { + get + { + return _stream; + } + } - return HttpRequest.Read (_stream, 15000); - } + #endregion - #endregion + #region - #region Internal Methods + /// + /// Returns a string that represents the current instance. + /// + /// The + public override string ToString() + { + return _request.ToString(); + } - internal bool Authenticate ( + /// + /// The Authenticate + /// + /// The scheme + /// The realm + /// The credentialsFinder + /// The + internal bool Authenticate( AuthenticationSchemes scheme, string realm, Func credentialsFinder ) - { - var chal = new AuthenticationChallenge (scheme, realm).ToString (); - - var retry = -1; - Func auth = null; - auth = - () => { - retry++; - if (retry > 99) - return false; - - var user = HttpUtility.CreateUser ( - _request.Headers["Authorization"], - scheme, - realm, - _request.HttpMethod, - credentialsFinder - ); - - if (user != null && user.Identity.IsAuthenticated) { - _user = user; - return true; - } - - _request = sendAuthenticationChallenge (chal); - return auth (); - }; - - return auth (); - } - - internal void Close () - { - _stream.Close (); - _tcpClient.Close (); - } - - internal void Close (HttpStatusCode code) - { - var res = HttpResponse.CreateCloseResponse (code); - var bytes = res.ToByteArray (); - _stream.Write (bytes, 0, bytes.Length); + { + var chal = new AuthenticationChallenge(scheme, realm).ToString(); + + var retry = -1; + Func auth = null; + auth = + () => + { + retry++; + if (retry > 99) + return false; + + var user = HttpUtility.CreateUser( + _request.Headers["Authorization"], + scheme, + realm, + _request.HttpMethod, + credentialsFinder + ); + + if (user != null && user.Identity.IsAuthenticated) + { + _user = user; + return true; + } + + _request = sendAuthenticationChallenge(chal); + return auth(); + }; + + return auth(); + } - _stream.Close (); - _tcpClient.Close (); - } + /// + /// The Close + /// + internal void Close() + { + _stream.Close(); + _tcpClient.Close(); + } - #endregion + /// + /// The Close + /// + /// The code + internal void Close(HttpStatusCode code) + { + var res = HttpResponse.CreateCloseResponse(code); + var bytes = res.ToByteArray(); + _stream.Write(bytes, 0, bytes.Length); + + _stream.Close(); + _tcpClient.Close(); + } - #region Public Methods + /// + /// The sendAuthenticationChallenge + /// + /// The challenge + /// The + private HttpRequest sendAuthenticationChallenge(string challenge) + { + var res = HttpResponse.CreateUnauthorizedResponse(challenge); + var bytes = res.ToByteArray(); + _stream.Write(bytes, 0, bytes.Length); + + return HttpRequest.Read(_stream, 15000); + } - /// - /// Returns a string that represents the current instance. - /// - /// - /// A that contains the request line and headers - /// included in the handshake request. - /// - public override string ToString () - { - return _request.ToString (); + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Net/WebSockets/WebSocketContext.cs b/src/WebSocket/WebSocketCore/Net/WebSockets/WebSocketContext.cs index 490dacdbb..973cb3384 100644 --- a/src/WebSocket/WebSocketCore/Net/WebSockets/WebSocketContext.cs +++ b/src/WebSocket/WebSocketCore/Net/WebSockets/WebSocketContext.cs @@ -1,4 +1,3 @@ -#region License /* * WebSocketContext.cs * @@ -24,7 +23,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; using System.Collections.Generic; @@ -33,192 +31,116 @@ namespace WebSocketCore.Net.WebSockets { - /// - /// Exposes the access to the information in a WebSocket handshake request. - /// - /// - /// This class is an abstract class. - /// - public abstract class WebSocketContext - { - #region Protected Constructors - /// - /// Initializes a new instance of the class. + /// Exposes the access to the information in a WebSocket handshake request. /// - protected WebSocketContext () + public abstract class WebSocketContext { + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + protected WebSocketContext() + { + } + + #endregion 캯 + + #region + + /// + /// Gets the HTTP cookies included in the handshake request. + /// + public abstract CookieCollection CookieCollection { get; } + + /// + /// Gets the HTTP headers included in the handshake request. + /// + public abstract NameValueCollection Headers { get; } + + /// + /// Gets the value of the Host header included in the handshake request. + /// + public abstract string Host { get; } + + /// + /// Gets a value indicating whether the client is authenticated. + /// + public abstract bool IsAuthenticated { get; } + + /// + /// Gets a value indicating whether the handshake request is sent from + /// the local computer. + /// + public abstract bool IsLocal { get; } + + /// + /// Gets a value indicating whether a secure connection is used to send + /// the handshake request. + /// + public abstract bool IsSecureConnection { get; } + + /// + /// Gets a value indicating whether the request is a WebSocket handshake + /// request. + /// + public abstract bool IsWebSocketRequest { get; } + + /// + /// Gets the value of the Origin header included in the handshake request. + /// + public abstract string Origin { get; } + + /// + /// Gets the query string included in the handshake request. + /// + public abstract NameValueCollection QueryString { get; } + + /// + /// Gets the URI requested by the client. + /// + public abstract Uri RequestUri { get; } + + /// + /// Gets the value of the Sec-WebSocket-Key header included in + /// the handshake request. + /// + public abstract string SecWebSocketKey { get; } + + /// + /// Gets the names of the subprotocols from the Sec-WebSocket-Protocol + /// header included in the handshake request. + /// + public abstract IEnumerable SecWebSocketProtocols { get; } + + /// + /// Gets the value of the Sec-WebSocket-Version header included in + /// the handshake request. + /// + public abstract string SecWebSocketVersion { get; } + + /// + /// Gets the endpoint to which the handshake request is sent. + /// + public abstract System.Net.IPEndPoint ServerEndPoint { get; } + + /// + /// Gets the client information. + /// + public abstract IPrincipal User { get; } + + /// + /// Gets the endpoint from which the handshake request is sent. + /// + public abstract System.Net.IPEndPoint UserEndPoint { get; } + + /// + /// Gets the WebSocket instance used for two-way communication between + /// the client and server. + /// + public abstract WebSocket WebSocket { get; } + + #endregion } - - #endregion - - #region Public Properties - - /// - /// Gets the HTTP cookies included in the handshake request. - /// - /// - /// A that contains - /// the cookies. - /// - public abstract CookieCollection CookieCollection { get; } - - /// - /// Gets the HTTP headers included in the handshake request. - /// - /// - /// A that contains the headers. - /// - public abstract NameValueCollection Headers { get; } - - /// - /// Gets the value of the Host header included in the handshake request. - /// - /// - /// A that represents the server host name requested - /// by the client. - /// - public abstract string Host { get; } - - /// - /// Gets a value indicating whether the client is authenticated. - /// - /// - /// true if the client is authenticated; otherwise, false. - /// - public abstract bool IsAuthenticated { get; } - - /// - /// Gets a value indicating whether the handshake request is sent from - /// the local computer. - /// - /// - /// true if the handshake request is sent from the same computer - /// as the server; otherwise, false. - /// - public abstract bool IsLocal { get; } - - /// - /// Gets a value indicating whether a secure connection is used to send - /// the handshake request. - /// - /// - /// true if the connection is secure; otherwise, false. - /// - public abstract bool IsSecureConnection { get; } - - /// - /// Gets a value indicating whether the request is a WebSocket handshake - /// request. - /// - /// - /// true if the request is a WebSocket handshake request; otherwise, - /// false. - /// - public abstract bool IsWebSocketRequest { get; } - - /// - /// Gets the value of the Origin header included in the handshake request. - /// - /// - /// A that represents the value of the Origin header. - /// - public abstract string Origin { get; } - - /// - /// Gets the query string included in the handshake request. - /// - /// - /// A that contains the query parameters. - /// - public abstract NameValueCollection QueryString { get; } - - /// - /// Gets the URI requested by the client. - /// - /// - /// A that represents the URI parsed from the request. - /// - public abstract Uri RequestUri { get; } - - /// - /// Gets the value of the Sec-WebSocket-Key header included in - /// the handshake request. - /// - /// - /// - /// A that represents the value of - /// the Sec-WebSocket-Key header. - /// - /// - /// The value is used to prove that the server received - /// a valid WebSocket handshake request. - /// - /// - public abstract string SecWebSocketKey { get; } - - /// - /// Gets the names of the subprotocols from the Sec-WebSocket-Protocol - /// header included in the handshake request. - /// - /// - /// - /// An - /// instance. - /// - /// - /// It provides an enumerator which supports the iteration over - /// the collection of the names of the subprotocols. - /// - /// - public abstract IEnumerable SecWebSocketProtocols { get; } - - /// - /// Gets the value of the Sec-WebSocket-Version header included in - /// the handshake request. - /// - /// - /// A that represents the WebSocket protocol - /// version specified by the client. - /// - public abstract string SecWebSocketVersion { get; } - - /// - /// Gets the endpoint to which the handshake request is sent. - /// - /// - /// A that represents the server IP - /// address and port number. - /// - public abstract System.Net.IPEndPoint ServerEndPoint { get; } - - /// - /// Gets the client information. - /// - /// - /// A instance that represents identity, - /// authentication, and security roles for the client. - /// - public abstract IPrincipal User { get; } - - /// - /// Gets the endpoint from which the handshake request is sent. - /// - /// - /// A that represents the client IP - /// address and port number. - /// - public abstract System.Net.IPEndPoint UserEndPoint { get; } - - /// - /// Gets the WebSocket instance used for two-way communication between - /// the client and server. - /// - /// - /// A . - /// - public abstract WebSocket WebSocket { get; } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Opcode.cs b/src/WebSocket/WebSocketCore/Opcode.cs index 728082bb1..d2c48236d 100644 --- a/src/WebSocket/WebSocketCore/Opcode.cs +++ b/src/WebSocket/WebSocketCore/Opcode.cs @@ -1,4 +1,3 @@ -#region License /* * Opcode.cs * @@ -24,45 +23,53 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; namespace WebSocketCore { - /// - /// Indicates the WebSocket frame type. - /// - /// - /// The values of this enumeration are defined in - /// - /// Section 5.2 of RFC 6455. - /// - internal enum Opcode : byte - { - /// - /// Equivalent to numeric value 0. Indicates continuation frame. - /// - Cont = 0x0, - /// - /// Equivalent to numeric value 1. Indicates text frame. - /// - Text = 0x1, - /// - /// Equivalent to numeric value 2. Indicates binary frame. - /// - Binary = 0x2, - /// - /// Equivalent to numeric value 8. Indicates connection close frame. - /// - Close = 0x8, - /// - /// Equivalent to numeric value 9. Indicates ping frame. - /// - Ping = 0x9, + #region ö + /// - /// Equivalent to numeric value 10. Indicates pong frame. + /// Indicates the WebSocket frame type. /// - Pong = 0xa - } -} + /// + /// The values of this enumeration are defined in + /// + /// Section 5.2 of RFC 6455. + /// + internal enum Opcode : byte + { + /// + /// Equivalent to numeric value 0. Indicates continuation frame. + /// + Cont = 0x0, + + /// + /// Equivalent to numeric value 1. Indicates text frame. + /// + Text = 0x1, + + /// + /// Equivalent to numeric value 2. Indicates binary frame. + /// + Binary = 0x2, + + /// + /// Equivalent to numeric value 8. Indicates connection close frame. + /// + Close = 0x8, + + /// + /// Equivalent to numeric value 9. Indicates ping frame. + /// + Ping = 0x9, + + /// + /// Equivalent to numeric value 10. Indicates pong frame. + /// + Pong = 0xa + } + + #endregion ö +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/PayloadData.cs b/src/WebSocket/WebSocketCore/PayloadData.cs index 4e4ccd14c..21c90d4a7 100644 --- a/src/WebSocket/WebSocketCore/PayloadData.cs +++ b/src/WebSocket/WebSocketCore/PayloadData.cs @@ -1,4 +1,3 @@ -#region License /* * PayloadData.cs * @@ -24,7 +23,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; using System.Collections; @@ -32,203 +30,281 @@ namespace WebSocketCore { - internal class PayloadData : IEnumerable - { - #region Private Fields - - private ushort _code; - private bool _codeSet; - private byte[] _data; - private long _extDataLength; - private long _length; - private string _reason; - private bool _reasonSet; - - #endregion - - #region Public Fields - /// - /// Represents the empty payload data. + /// Defines the /// - public static readonly PayloadData Empty; - - /// - /// Represents the allowable max length. - /// - /// - /// - /// A will occur if the payload data length is - /// greater than the value of this field. - /// - /// - /// If you would like to change the value, you must set it to a value between - /// WebSocket.FragmentLength and Int64.MaxValue inclusive. - /// - /// - public static readonly ulong MaxLength; - - #endregion - - #region Static Constructor - - static PayloadData () - { - Empty = new PayloadData (); - MaxLength = Int64.MaxValue; - } - - #endregion - - #region Internal Constructors - - internal PayloadData () - { - _code = 1005; - _reason = String.Empty; - - _data = WebSocket.EmptyBytes; - - _codeSet = true; - _reasonSet = true; - } - - internal PayloadData (byte[] data) - : this (data, data.LongLength) + internal class PayloadData : IEnumerable { - } - - internal PayloadData (byte[] data, long length) - { - _data = data; - _length = length; - } - - internal PayloadData (ushort code, string reason) - { - _code = code; - _reason = reason ?? String.Empty; - - _data = code.Append (reason); - _length = _data.LongLength; - - _codeSet = true; - _reasonSet = true; - } - - #endregion - - #region Internal Properties - - internal ushort Code { - get { - if (!_codeSet) { - _code = _length > 1 - ? _data.SubArray (0, 2).ToUInt16 (ByteOrder.Big) - : (ushort) 1005; - - _codeSet = true; + #region ֶ + + /// + /// Represents the empty payload data. + /// + public static readonly PayloadData Empty; + + /// + /// Represents the allowable max length. + /// + public static readonly ulong MaxLength; + + /// + /// Defines the _code + /// + private ushort _code; + + /// + /// Defines the _codeSet + /// + private bool _codeSet; + + /// + /// Defines the _data + /// + private byte[] _data; + + /// + /// Defines the _extDataLength + /// + private long _extDataLength; + + /// + /// Defines the _length + /// + private long _length; + + /// + /// Defines the _reason + /// + private string _reason; + + /// + /// Defines the _reasonSet + /// + private bool _reasonSet; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + internal PayloadData() + { + _code = 1005; + _reason = String.Empty; + + _data = WebSocket.EmptyBytes; + + _codeSet = true; + _reasonSet = true; } - return _code; - } - } - - internal long ExtensionDataLength { - get { - return _extDataLength; - } - - set { - _extDataLength = value; - } - } - - internal bool HasReservedCode { - get { - return _length > 1 && Code.IsReserved (); - } - } - - internal string Reason { - get { - if (!_reasonSet) { - _reason = _length > 2 - ? _data.SubArray (2, _length - 2).UTF8Decode () - : String.Empty; + /// + /// Initializes a new instance of the class. + /// + /// The data + internal PayloadData(byte[] data) + : this(data, data.LongLength) + { + } - _reasonSet = true; + /// + /// Initializes a new instance of the class. + /// + /// The data + /// The length + internal PayloadData(byte[] data, long length) + { + _data = data; + _length = length; } - return _reason; - } - } + /// + /// Initializes a new instance of the class. + /// + /// The code + /// The reason + internal PayloadData(ushort code, string reason) + { + _code = code; + _reason = reason ?? String.Empty; + + _data = code.Append(reason); + _length = _data.LongLength; + + _codeSet = true; + _reasonSet = true; + } - #endregion + /// + /// Initializes static members of the class. + /// + static PayloadData() + { + Empty = new PayloadData(); + MaxLength = Int64.MaxValue; + } - #region Public Properties + #endregion 캯 + + #region + + /// + /// Gets the ApplicationData + /// + public byte[] ApplicationData + { + get + { + return _extDataLength > 0 + ? _data.SubArray(_extDataLength, _length - _extDataLength) + : _data; + } + } - public byte[] ApplicationData { - get { - return _extDataLength > 0 - ? _data.SubArray (_extDataLength, _length - _extDataLength) - : _data; - } - } + /// + /// Gets the ExtensionData + /// + public byte[] ExtensionData + { + get + { + return _extDataLength > 0 + ? _data.SubArray(0, _extDataLength) + : WebSocket.EmptyBytes; + } + } - public byte[] ExtensionData { - get { - return _extDataLength > 0 - ? _data.SubArray (0, _extDataLength) - : WebSocket.EmptyBytes; - } - } + /// + /// Gets the Length + /// + public ulong Length + { + get + { + return (ulong)_length; + } + } - public ulong Length { - get { - return (ulong) _length; - } - } + /// + /// Gets the Code + /// + internal ushort Code + { + get + { + if (!_codeSet) + { + _code = _length > 1 + ? _data.SubArray(0, 2).ToUInt16(ByteOrder.Big) + : (ushort)1005; + + _codeSet = true; + } + + return _code; + } + } - #endregion + /// + /// Gets or sets the ExtensionDataLength + /// + internal long ExtensionDataLength + { + get + { + return _extDataLength; + } + + set + { + _extDataLength = value; + } + } - #region Internal Methods + /// + /// Gets a value indicating whether HasReservedCode + /// + internal bool HasReservedCode + { + get + { + return _length > 1 && Code.IsReserved(); + } + } - internal void Mask (byte[] key) - { - for (long i = 0; i < _length; i++) - _data[i] = (byte) (_data[i] ^ key[i % 4]); - } + /// + /// Gets the Reason + /// + internal string Reason + { + get + { + if (!_reasonSet) + { + _reason = _length > 2 + ? _data.SubArray(2, _length - 2).UTF8Decode() + : String.Empty; + + _reasonSet = true; + } + + return _reason; + } + } - #endregion + #endregion - #region Public Methods + #region - public IEnumerator GetEnumerator () - { - foreach (var b in _data) - yield return b; - } + /// + /// The GetEnumerator + /// + /// The + public IEnumerator GetEnumerator() + { + foreach (var b in _data) + yield return b; + } - public byte[] ToArray () - { - return _data; - } + /// + /// The ToArray + /// + /// The + public byte[] ToArray() + { + return _data; + } - public override string ToString () - { - return BitConverter.ToString (_data); - } + /// + /// The ToString + /// + /// The + public override string ToString() + { + return BitConverter.ToString(_data); + } - #endregion + /// + /// The Mask + /// + /// The key + internal void Mask(byte[] key) + { + for (long i = 0; i < _length; i++) + _data[i] = (byte)(_data[i] ^ key[i % 4]); + } - #region Explicit Interface Implementations + /// + /// The GetEnumerator + /// + /// The + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } - IEnumerator IEnumerable.GetEnumerator () - { - return GetEnumerator (); + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Program.cs b/src/WebSocket/WebSocketCore/Program.cs index 24c020bf7..62b8195f6 100644 --- a/src/WebSocket/WebSocketCore/Program.cs +++ b/src/WebSocket/WebSocketCore/Program.cs @@ -2,11 +2,22 @@ namespace WebSocketCore { - class Program + /// + /// Defines the + /// + internal class Program { - static void Main(string[] args) + #region 方法 + + /// + /// The Main + /// + /// The args + internal static void Main(string[] args) { Console.WriteLine("Hello World!"); } + + #endregion 方法 } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Rsv.cs b/src/WebSocket/WebSocketCore/Rsv.cs index 96474d407..5fbdb348e 100644 --- a/src/WebSocket/WebSocketCore/Rsv.cs +++ b/src/WebSocket/WebSocketCore/Rsv.cs @@ -1,4 +1,3 @@ -#region License /* * Rsv.cs * @@ -24,28 +23,32 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; namespace WebSocketCore { - /// - /// Indicates whether each RSV (RSV1, RSV2, and RSV3) of a WebSocket frame is non-zero. - /// - /// - /// The values of this enumeration are defined in - /// Section 5.2 of RFC 6455. - /// - internal enum Rsv : byte - { - /// - /// Equivalent to numeric value 0. Indicates zero. - /// - Off = 0x0, + #region ö + /// - /// Equivalent to numeric value 1. Indicates non-zero. + /// Indicates whether each RSV (RSV1, RSV2, and RSV3) of a WebSocket frame is non-zero. /// - On = 0x1 - } -} + /// + /// The values of this enumeration are defined in + /// Section 5.2 of RFC 6455. + /// + internal enum Rsv : byte + { + /// + /// Equivalent to numeric value 0. Indicates zero. + /// + Off = 0x0, + + /// + /// Equivalent to numeric value 1. Indicates non-zero. + /// + On = 0x1 + } + + #endregion ö +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Server/HttpRequestEventArgs.cs b/src/WebSocket/WebSocketCore/Server/HttpRequestEventArgs.cs index b4f0d4417..84c76c804 100644 --- a/src/WebSocket/WebSocketCore/Server/HttpRequestEventArgs.cs +++ b/src/WebSocket/WebSocketCore/Server/HttpRequestEventArgs.cs @@ -1,4 +1,3 @@ -#region License /* * HttpRequestEventArgs.cs * @@ -24,7 +23,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; using System.IO; @@ -34,222 +32,165 @@ namespace WebSocketCore.Server { - /// - /// Represents the event data for the HTTP request events of - /// the . - /// - /// - /// - /// An HTTP request event occurs when the - /// receives an HTTP request. - /// - /// - /// You should access the property if you would - /// like to get the request data sent from a client. - /// - /// - /// And you should access the property if you would - /// like to get the response data to return to the client. - /// - /// - public class HttpRequestEventArgs : EventArgs - { - #region Private Fields - - private HttpListenerContext _context; - private string _docRootPath; - - #endregion - - #region Internal Constructors - - internal HttpRequestEventArgs ( - HttpListenerContext context, string documentRootPath - ) - { - _context = context; - _docRootPath = documentRootPath; - } - - #endregion - - #region Public Properties - - /// - /// Gets the request data sent from a client. - /// - /// - /// A that provides the methods and - /// properties for the request data. - /// - public HttpListenerRequest Request { - get { - return _context.Request; - } - } - /// - /// Gets the response data to return to the client. - /// - /// - /// A that provides the methods and - /// properties for the response data. - /// - public HttpListenerResponse Response { - get { - return _context.Response; - } - } - - /// - /// Gets the information for the client. - /// - /// - /// - /// A instance or - /// if not authenticated. - /// - /// - /// That instance describes the identity, authentication scheme, - /// and security roles for the client. - /// - /// - public IPrincipal User { - get { - return _context.User; - } - } - - #endregion - - #region Private Methods - - private string createFilePath (string childPath) - { - childPath = childPath.TrimStart ('/', '\\'); - return new StringBuilder (_docRootPath, 32) - .AppendFormat ("/{0}", childPath) - .ToString () - .Replace ('\\', '/'); - } - - private static bool tryReadFile (string path, out byte[] contents) - { - contents = null; - - if (!File.Exists (path)) - return false; - - try { - contents = File.ReadAllBytes (path); - } - catch { - return false; - } - - return true; - } - - #endregion - - #region Public Methods - - /// - /// Reads the specified file from the document folder of + /// Represents the event data for the HTTP request events of /// the . /// - /// - /// - /// An array of or - /// if it fails. - /// - /// - /// That array receives the contents of the file. - /// - /// - /// - /// A that represents a virtual path to - /// find the file from the document folder. - /// - /// - /// is . - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// contains "..". - /// - /// - public byte[] ReadFile (string path) + public class HttpRequestEventArgs : EventArgs { - if (path == null) - throw new ArgumentNullException ("path"); + #region ֶ - if (path.Length == 0) - throw new ArgumentException ("An empty string.", "path"); + /// + /// Defines the _context + /// + private HttpListenerContext _context; - if (path.IndexOf ("..") > -1) - throw new ArgumentException ("It contains '..'.", "path"); + /// + /// Defines the _docRootPath + /// + private string _docRootPath; - byte[] contents; - tryReadFile (createFilePath (path), out contents); + #endregion ֶ - return contents; - } + #region 캯 - /// - /// Tries to read the specified file from the document folder of - /// the . - /// - /// - /// true if it succeeds to read; otherwise, false. - /// - /// - /// A that represents a virtual path to - /// find the file from the document folder. - /// - /// - /// - /// When this method returns, an array of or - /// if it fails. - /// - /// - /// That array receives the contents of the file. - /// - /// - /// - /// is . - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// contains "..". - /// - /// - public bool TryReadFile (string path, out byte[] contents) - { - if (path == null) - throw new ArgumentNullException ("path"); - - if (path.Length == 0) - throw new ArgumentException ("An empty string.", "path"); - - if (path.IndexOf ("..") > -1) - throw new ArgumentException ("It contains '..'.", "path"); - - return tryReadFile (createFilePath (path), out contents); + /// + /// Initializes a new instance of the class. + /// + /// The context + /// The documentRootPath + internal HttpRequestEventArgs( + HttpListenerContext context, string documentRootPath + ) + { + _context = context; + _docRootPath = documentRootPath; + } + + #endregion 캯 + + #region + + /// + /// Gets the request data sent from a client. + /// + public HttpListenerRequest Request + { + get + { + return _context.Request; + } + } + + /// + /// Gets the response data to return to the client. + /// + public HttpListenerResponse Response + { + get + { + return _context.Response; + } + } + + /// + /// Gets the information for the client. + /// + public IPrincipal User + { + get + { + return _context.User; + } + } + + #endregion + + #region + + /// + /// Reads the specified file from the document folder of + /// the . + /// + /// The path + /// The + public byte[] ReadFile(string path) + { + if (path == null) + throw new ArgumentNullException("path"); + + if (path.Length == 0) + throw new ArgumentException("An empty string.", "path"); + + if (path.IndexOf("..") > -1) + throw new ArgumentException("It contains '..'.", "path"); + + byte[] contents; + tryReadFile(createFilePath(path), out contents); + + return contents; + } + + /// + /// Tries to read the specified file from the document folder of + /// the . + /// + /// The path + /// The contents + /// The + public bool TryReadFile(string path, out byte[] contents) + { + if (path == null) + throw new ArgumentNullException("path"); + + if (path.Length == 0) + throw new ArgumentException("An empty string.", "path"); + + if (path.IndexOf("..") > -1) + throw new ArgumentException("It contains '..'.", "path"); + + return tryReadFile(createFilePath(path), out contents); + } + + /// + /// The tryReadFile + /// + /// The path + /// The contents + /// The + private static bool tryReadFile(string path, out byte[] contents) + { + contents = null; + + if (!File.Exists(path)) + return false; + + try + { + contents = File.ReadAllBytes(path); + } + catch + { + return false; + } + + return true; + } + + /// + /// The createFilePath + /// + /// The childPath + /// The + private string createFilePath(string childPath) + { + childPath = childPath.TrimStart('/', '\\'); + return new StringBuilder(_docRootPath, 32) + .AppendFormat("/{0}", childPath) + .ToString() + .Replace('\\', '/'); + } + + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Server/HttpServer.cs b/src/WebSocket/WebSocketCore/Server/HttpServer.cs index 50912ede9..d49b0433e 100644 --- a/src/WebSocket/WebSocketCore/Server/HttpServer.cs +++ b/src/WebSocket/WebSocketCore/Server/HttpServer.cs @@ -1,4 +1,3 @@ -#region License /* * HttpServer.cs * @@ -26,16 +25,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Contributors /* * Contributors: * - Juan Manuel Lallana * - Liryna * - Rohan Singh */ -#endregion using System; using System.Collections.Generic; @@ -51,1611 +47,1205 @@ namespace WebSocketCore.Server { - /// - /// Provides a simple HTTP server that allows to accept - /// WebSocket handshake requests. - /// - /// - /// This class can provide multiple WebSocket services. - /// - public class HttpServer - { - #region Private Fields - - private System.Net.IPAddress _address; - private string _docRootPath; - private string _hostname; - private HttpListener _listener; - private Logger _log; - private int _port; - private Thread _receiveThread; - private bool _secure; - private WebSocketServiceManager _services; - private volatile ServerState _state; - private object _sync; - - #endregion - - #region Public Constructors - - /// - /// Initializes a new instance of the class. - /// - /// - /// The new instance listens for incoming requests on - /// and port 80. - /// - public HttpServer () - { - init ("*", System.Net.IPAddress.Any, 80, false); - } - /// - /// Initializes a new instance of the class with - /// the specified . + /// Provides a simple HTTP server that allows to accept + /// WebSocket handshake requests. /// - /// - /// - /// The new instance listens for incoming requests on - /// and . - /// - /// - /// It provides secure connections if is 443. - /// - /// - /// - /// An that represents the number of the port - /// on which to listen. - /// - /// - /// is less than 1 or greater than 65535. - /// - public HttpServer (int port) - : this (port, port == 443) + public class HttpServer { - } + #region ֶ + + /// + /// Defines the _address + /// + private System.Net.IPAddress _address; + + /// + /// Defines the _docRootPath + /// + private string _docRootPath; + + /// + /// Defines the _hostname + /// + private string _hostname; + + /// + /// Defines the _listener + /// + private HttpListener _listener; + + /// + /// Defines the _log + /// + private Logger _log; + + /// + /// Defines the _port + /// + private int _port; + + /// + /// Defines the _receiveThread + /// + private Thread _receiveThread; + + /// + /// Defines the _secure + /// + private bool _secure; + + /// + /// Defines the _services + /// + private WebSocketServiceManager _services; + + /// + /// Defines the _state + /// + private volatile ServerState _state; + + /// + /// Defines the _sync + /// + private object _sync; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + public HttpServer() + { + init("*", System.Net.IPAddress.Any, 80, false); + } - /// - /// Initializes a new instance of the class with - /// the specified . - /// - /// - /// - /// The new instance listens for incoming requests on the IP address of the - /// host of and the port of . - /// - /// - /// Either port 80 or 443 is used if includes - /// no port. Port 443 is used if the scheme of - /// is https; otherwise, port 80 is used. - /// - /// - /// The new instance provides secure connections if the scheme of - /// is https. - /// - /// - /// - /// A that represents the HTTP URL of the server. - /// - /// - /// is . - /// - /// - /// - /// is empty. - /// - /// - /// -or- - /// - /// - /// is invalid. - /// - /// - public HttpServer (string url) - { - if (url == null) - throw new ArgumentNullException ("url"); + /// + /// Initializes a new instance of the class. + /// + /// The port + public HttpServer(int port) + : this(port, port == 443) + { + } - if (url.Length == 0) - throw new ArgumentException ("An empty string.", "url"); + /// + /// Initializes a new instance of the class. + /// + /// The port + /// The secure + public HttpServer(int port, bool secure) + { + if (!port.IsPortNumber()) + { + var msg = "Less than 1 or greater than 65535."; + throw new ArgumentOutOfRangeException("port", msg); + } - Uri uri; - string msg; - if (!tryCreateUri (url, out uri, out msg)) - throw new ArgumentException (msg, "url"); + init("*", System.Net.IPAddress.Any, port, secure); + } - var host = uri.GetDnsSafeHost (true); + /// + /// Initializes a new instance of the class. + /// + /// The url + public HttpServer(string url) + { + if (url == null) + throw new ArgumentNullException("url"); + + if (url.Length == 0) + throw new ArgumentException("An empty string.", "url"); + + Uri uri; + string msg; + if (!tryCreateUri(url, out uri, out msg)) + throw new ArgumentException(msg, "url"); + + var host = uri.GetDnsSafeHost(true); + + var addr = host.ToIPAddress(); + if (addr == null) + { + msg = "The host part could not be converted to an IP address."; + throw new ArgumentException(msg, "url"); + } - var addr = host.ToIPAddress (); - if (addr == null) { - msg = "The host part could not be converted to an IP address."; - throw new ArgumentException (msg, "url"); - } + if (!addr.IsLocal()) + { + msg = "The IP address of the host is not a local IP address."; + throw new ArgumentException(msg, "url"); + } - if (!addr.IsLocal ()) { - msg = "The IP address of the host is not a local IP address."; - throw new ArgumentException (msg, "url"); - } + init(host, addr, uri.Port, uri.Scheme == "https"); + } - init (host, addr, uri.Port, uri.Scheme == "https"); - } + /// + /// Initializes a new instance of the class. + /// + /// The address + /// The port + public HttpServer(System.Net.IPAddress address, int port) + : this(address, port, port == 443) + { + } - /// - /// Initializes a new instance of the class with - /// the specified and . - /// - /// - /// The new instance listens for incoming requests on - /// and . - /// - /// - /// An that represents the number of the port - /// on which to listen. - /// - /// - /// A : true if the new instance provides - /// secure connections; otherwise, false. - /// - /// - /// is less than 1 or greater than 65535. - /// - public HttpServer (int port, bool secure) - { - if (!port.IsPortNumber ()) { - var msg = "Less than 1 or greater than 65535."; - throw new ArgumentOutOfRangeException ("port", msg); - } + /// + /// Initializes a new instance of the class. + /// + /// The address + /// The port + /// The secure + public HttpServer(System.Net.IPAddress address, int port, bool secure) + { + if (address == null) + throw new ArgumentNullException("address"); + + if (!address.IsLocal()) + throw new ArgumentException("Not a local IP address.", "address"); + + if (!port.IsPortNumber()) + { + var msg = "Less than 1 or greater than 65535."; + throw new ArgumentOutOfRangeException("port", msg); + } - init ("*", System.Net.IPAddress.Any, port, secure); - } + init(address.ToString(true), address, port, secure); + } - /// - /// Initializes a new instance of the class with - /// the specified and . - /// - /// - /// - /// The new instance listens for incoming requests on - /// and . - /// - /// - /// It provides secure connections if is 443. - /// - /// - /// - /// A that represents - /// the local IP address on which to listen. - /// - /// - /// An that represents the number of the port - /// on which to listen. - /// - /// - /// is . - /// - /// - /// is not a local IP address. - /// - /// - /// is less than 1 or greater than 65535. - /// - public HttpServer (System.Net.IPAddress address, int port) - : this (address, port, port == 443) - { - } + #endregion 캯 + + #region ¼ + + /// + /// Occurs when the server receives an HTTP CONNECT request. + /// + public event EventHandler OnConnect; + + /// + /// Occurs when the server receives an HTTP DELETE request. + /// + public event EventHandler OnDelete; + + /// + /// Occurs when the server receives an HTTP GET request. + /// + public event EventHandler OnGet; + + /// + /// Occurs when the server receives an HTTP HEAD request. + /// + public event EventHandler OnHead; + + /// + /// Occurs when the server receives an HTTP OPTIONS request. + /// + public event EventHandler OnOptions; + + /// + /// Occurs when the server receives an HTTP POST request. + /// + public event EventHandler OnPost; + + /// + /// Occurs when the server receives an HTTP PUT request. + /// + public event EventHandler OnPut; + + /// + /// Occurs when the server receives an HTTP TRACE request. + /// + public event EventHandler OnTrace; + + #endregion ¼ + + #region + + /// + /// Gets the IP address of the server. + /// + public System.Net.IPAddress Address + { + get + { + return _address; + } + } - /// - /// Initializes a new instance of the class with - /// the specified , , - /// and . - /// - /// - /// The new instance listens for incoming requests on - /// and . - /// - /// - /// A that represents - /// the local IP address on which to listen. - /// - /// - /// An that represents the number of the port - /// on which to listen. - /// - /// - /// A : true if the new instance provides - /// secure connections; otherwise, false. - /// - /// - /// is . - /// - /// - /// is not a local IP address. - /// - /// - /// is less than 1 or greater than 65535. - /// - public HttpServer (System.Net.IPAddress address, int port, bool secure) - { - if (address == null) - throw new ArgumentNullException ("address"); + /// + /// Gets or sets the scheme used to authenticate the clients. + /// + public AuthenticationSchemes AuthenticationSchemes + { + get + { + return _listener.AuthenticationSchemes; + } - if (!address.IsLocal ()) - throw new ArgumentException ("Not a local IP address.", "address"); + set + { + string msg; + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } - if (!port.IsPortNumber ()) { - var msg = "Less than 1 or greater than 65535."; - throw new ArgumentOutOfRangeException ("port", msg); - } + lock (_sync) + { + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } - init (address.ToString (true), address, port, secure); - } + _listener.AuthenticationSchemes = value; + } + } + } - #endregion + /// + /// Gets or sets the path to the document folder of the server. + /// + public string DocumentRootPath + { + get + { + return _docRootPath; + } - #region Public Properties + set + { + if (value == null) + throw new ArgumentNullException("value"); - /// - /// Gets the IP address of the server. - /// - /// - /// A that represents the local - /// IP address on which to listen for incoming requests. - /// - public System.Net.IPAddress Address { - get { - return _address; - } - } + if (value.Length == 0) + throw new ArgumentException("An empty string.", "value"); - /// - /// Gets or sets the scheme used to authenticate the clients. - /// - /// - /// The set operation does nothing if the server has already - /// started or it is shutting down. - /// - /// - /// - /// One of the - /// enum values. - /// - /// - /// It represents the scheme used to authenticate the clients. - /// - /// - /// The default value is - /// . - /// - /// - public AuthenticationSchemes AuthenticationSchemes { - get { - return _listener.AuthenticationSchemes; - } - - set { - string msg; - if (!canSet (out msg)) { - _log.Warn (msg); - return; - } + value = value.TrimSlashOrBackslashFromEnd(); - lock (_sync) { - if (!canSet (out msg)) { - _log.Warn (msg); - return; - } + string full = null; + try + { + full = Path.GetFullPath(value); + } + catch (Exception ex) + { + throw new ArgumentException("An invalid path string.", "value", ex); + } - _listener.AuthenticationSchemes = value; - } - } - } + if (value == "/") + throw new ArgumentException("An absolute root.", "value"); - /// - /// Gets or sets the path to the document folder of the server. - /// - /// - /// - /// '/' or '\' is trimmed from the end of the value if any. - /// - /// - /// The set operation does nothing if the server has already - /// started or it is shutting down. - /// - /// - /// - /// - /// A that represents a path to the folder - /// from which to find the requested file. - /// - /// - /// The default value is "./Public". - /// - /// - /// - /// The value specified for a set operation is . - /// - /// - /// - /// The value specified for a set operation is an empty string. - /// - /// - /// -or- - /// - /// - /// The value specified for a set operation is an invalid path string. - /// - /// - /// -or- - /// - /// - /// The value specified for a set operation is an absolute root. - /// - /// - public string DocumentRootPath { - get { - return _docRootPath; - } - - set { - if (value == null) - throw new ArgumentNullException ("value"); - - if (value.Length == 0) - throw new ArgumentException ("An empty string.", "value"); - - value = value.TrimSlashOrBackslashFromEnd (); - - string full = null; - try { - full = Path.GetFullPath (value); - } - catch (Exception ex) { - throw new ArgumentException ("An invalid path string.", "value", ex); - } + if (value == "\\") + throw new ArgumentException("An absolute root.", "value"); - if (value == "/") - throw new ArgumentException ("An absolute root.", "value"); + if (value.Length == 2 && value[1] == ':') + throw new ArgumentException("An absolute root.", "value"); - if (value == "\\") - throw new ArgumentException ("An absolute root.", "value"); + if (full == "/") + throw new ArgumentException("An absolute root.", "value"); - if (value.Length == 2 && value[1] == ':') - throw new ArgumentException ("An absolute root.", "value"); + full = full.TrimSlashOrBackslashFromEnd(); + if (full.Length == 2 && full[1] == ':') + throw new ArgumentException("An absolute root.", "value"); - if (full == "/") - throw new ArgumentException ("An absolute root.", "value"); + string msg; + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } - full = full.TrimSlashOrBackslashFromEnd (); - if (full.Length == 2 && full[1] == ':') - throw new ArgumentException ("An absolute root.", "value"); + lock (_sync) + { + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } - string msg; - if (!canSet (out msg)) { - _log.Warn (msg); - return; + _docRootPath = value; + } + } } - lock (_sync) { - if (!canSet (out msg)) { - _log.Warn (msg); - return; - } + /// + /// Gets a value indicating whether the server has started. + /// + public bool IsListening + { + get + { + return _state == ServerState.Start; + } + } - _docRootPath = value; + /// + /// Gets a value indicating whether secure connections are provided. + /// + public bool IsSecure + { + get + { + return _secure; + } } - } - } - /// - /// Gets a value indicating whether the server has started. - /// - /// - /// true if the server has started; otherwise, false. - /// - public bool IsListening { - get { - return _state == ServerState.Start; - } - } + /// + /// Gets or sets a value indicating whether the server cleans up + /// the inactive sessions periodically. + /// + public bool KeepClean + { + get + { + return _services.KeepClean; + } - /// - /// Gets a value indicating whether secure connections are provided. - /// - /// - /// true if this instance provides secure connections; otherwise, - /// false. - /// - public bool IsSecure { - get { - return _secure; - } - } + set + { + _services.KeepClean = value; + } + } - /// - /// Gets or sets a value indicating whether the server cleans up - /// the inactive sessions periodically. - /// - /// - /// The set operation does nothing if the server has already - /// started or it is shutting down. - /// - /// - /// - /// true if the server cleans up the inactive sessions - /// every 60 seconds; otherwise, false. - /// - /// - /// The default value is true. - /// - /// - public bool KeepClean { - get { - return _services.KeepClean; - } - - set { - _services.KeepClean = value; - } - } + /// + /// Gets the logging function for the server. + /// + public Logger Log + { + get + { + return _log; + } + } - /// - /// Gets the logging function for the server. - /// - /// - /// The default logging level is . - /// - /// - /// A that provides the logging function. - /// - public Logger Log { - get { - return _log; - } - } + /// + /// Gets the port of the server. + /// + public int Port + { + get + { + return _port; + } + } - /// - /// Gets the port of the server. - /// - /// - /// An that represents the number of the port - /// on which to listen for incoming requests. - /// - public int Port { - get { - return _port; - } - } + /// + /// Gets or sets the realm used for authentication. + /// + public string Realm + { + get + { + return _listener.Realm; + } - /// - /// Gets or sets the realm used for authentication. - /// - /// - /// - /// "SECRET AREA" is used as the realm if the value is - /// or an empty string. - /// - /// - /// The set operation does nothing if the server has - /// already started or it is shutting down. - /// - /// - /// - /// - /// A or by default. - /// - /// - /// That string represents the name of the realm. - /// - /// - public string Realm { - get { - return _listener.Realm; - } - - set { - string msg; - if (!canSet (out msg)) { - _log.Warn (msg); - return; - } + set + { + string msg; + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } - lock (_sync) { - if (!canSet (out msg)) { - _log.Warn (msg); - return; - } + lock (_sync) + { + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } - _listener.Realm = value; + _listener.Realm = value; + } + } } - } - } - /// - /// Gets or sets a value indicating whether the server is allowed to - /// be bound to an address that is already in use. - /// - /// - /// - /// You should set this property to true if you would - /// like to resolve to wait for socket in TIME_WAIT state. - /// - /// - /// The set operation does nothing if the server has already - /// started or it is shutting down. - /// - /// - /// - /// - /// true if the server is allowed to be bound to an address - /// that is already in use; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - public bool ReuseAddress { - get { - return _listener.ReuseAddress; - } - - set { - string msg; - if (!canSet (out msg)) { - _log.Warn (msg); - return; - } + /// + /// Gets or sets a value indicating whether the server is allowed to + /// be bound to an address that is already in use. + /// + public bool ReuseAddress + { + get + { + return _listener.ReuseAddress; + } - lock (_sync) { - if (!canSet (out msg)) { - _log.Warn (msg); - return; - } + set + { + string msg; + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } - _listener.ReuseAddress = value; - } - } - } + lock (_sync) + { + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } - /// - /// Gets the configuration for secure connections. - /// - /// - /// This configuration will be referenced when attempts to start, - /// so it must be configured before the start method is called. - /// - /// - /// A that represents - /// the configuration used to provide secure connections. - /// - /// - /// This instance does not provide secure connections. - /// - public ServerSslConfiguration SslConfiguration { - get { - if (!_secure) { - var msg = "This instance does not provide secure connections."; - throw new InvalidOperationException (msg); + _listener.ReuseAddress = value; + } + } } - return _listener.SslConfiguration; - } - } + /// + /// Gets the configuration for secure connections. + /// + public ServerSslConfiguration SslConfiguration + { + get + { + if (!_secure) + { + var msg = "This instance does not provide secure connections."; + throw new InvalidOperationException(msg); + } - /// - /// Gets or sets the delegate used to find the credentials - /// for an identity. - /// - /// - /// - /// No credentials are found if the method invoked by - /// the delegate returns or - /// the value is . - /// - /// - /// The set operation does nothing if the server has - /// already started or it is shutting down. - /// - /// - /// - /// - /// A Func<, - /// > delegate or - /// if not needed. - /// - /// - /// That delegate invokes the method called for finding - /// the credentials used to authenticate a client. - /// - /// - /// The default value is . - /// - /// - public Func UserCredentialsFinder { - get { - return _listener.UserCredentialsFinder; - } - - set { - string msg; - if (!canSet (out msg)) { - _log.Warn (msg); - return; + return _listener.SslConfiguration; + } } - lock (_sync) { - if (!canSet (out msg)) { - _log.Warn (msg); - return; - } + /// + /// Gets or sets the delegate used to find the credentials + /// for an identity. + /// + public Func UserCredentialsFinder + { + get + { + return _listener.UserCredentialsFinder; + } - _listener.UserCredentialsFinder = value; - } - } - } + set + { + string msg; + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } - /// - /// Gets or sets the time to wait for the response to the WebSocket Ping or - /// Close. - /// - /// - /// The set operation does nothing if the server has already started or - /// it is shutting down. - /// - /// - /// - /// A to wait for the response. - /// - /// - /// The default value is the same as 1 second. - /// - /// - /// - /// The value specified for a set operation is zero or less. - /// - public TimeSpan WaitTime { - get { - return _services.WaitTime; - } - - set { - _services.WaitTime = value; - } - } + lock (_sync) + { + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } - /// - /// Gets the management function for the WebSocket services - /// provided by the server. - /// - /// - /// A that manages - /// the WebSocket services provided by the server. - /// - public WebSocketServiceManager WebSocketServices { - get { - return _services; - } - } + _listener.UserCredentialsFinder = value; + } + } + } - #endregion + /// + /// Gets or sets the time to wait for the response to the WebSocket Ping or + /// Close. + /// + public TimeSpan WaitTime + { + get + { + return _services.WaitTime; + } - #region Public Events + set + { + _services.WaitTime = value; + } + } - /// - /// Occurs when the server receives an HTTP CONNECT request. - /// - public event EventHandler OnConnect; + /// + /// Gets the management function for the WebSocket services + /// provided by the server. + /// + public WebSocketServiceManager WebSocketServices + { + get + { + return _services; + } + } - /// - /// Occurs when the server receives an HTTP DELETE request. - /// - public event EventHandler OnDelete; + #endregion - /// - /// Occurs when the server receives an HTTP GET request. - /// - public event EventHandler OnGet; + #region - /// - /// Occurs when the server receives an HTTP HEAD request. - /// - public event EventHandler OnHead; + /// + /// Adds a WebSocket service with the specified behavior, + /// , and . + /// + /// + /// The path + /// The initializer + public void AddWebSocketService( + string path, Action initializer + ) + where TBehaviorWithNew : WebSocketBehavior, new() + { + _services.AddService(path, initializer); + } - /// - /// Occurs when the server receives an HTTP OPTIONS request. - /// - public event EventHandler OnOptions; + /// + /// Adds a WebSocket service with the specified behavior, + /// , and . + /// + /// + /// The path + /// The creator + [Obsolete("This method will be removed. Use added one instead.")] + public void AddWebSocketService( + string path, Func creator + ) + where TBehavior : WebSocketBehavior + { + if (path == null) + throw new ArgumentNullException("path"); - /// - /// Occurs when the server receives an HTTP POST request. - /// - public event EventHandler OnPost; + if (creator == null) + throw new ArgumentNullException("creator"); - /// - /// Occurs when the server receives an HTTP PUT request. - /// - public event EventHandler OnPut; + if (path.Length == 0) + throw new ArgumentException("An empty string.", "path"); - /// - /// Occurs when the server receives an HTTP TRACE request. - /// - public event EventHandler OnTrace; + if (path[0] != '/') + throw new ArgumentException("Not an absolute path.", "path"); - #endregion + if (path.IndexOfAny(new[] { '?', '#' }) > -1) + { + var msg = "It includes either or both query and fragment components."; + throw new ArgumentException(msg, "path"); + } - #region Private Methods + _services.Add(path, creator); + } - private void abort () - { - lock (_sync) { - if (_state != ServerState.Start) - return; + /// + /// Adds a WebSocket service with the specified behavior and + /// . + /// + /// + /// The path + public void AddWebSocketService(string path) + where TBehaviorWithNew : WebSocketBehavior, new() + { + _services.AddService(path, null); + } - _state = ServerState.ShuttingDown; - } + /// + /// Gets the contents of the specified file from the document + /// folder of the server. + /// + /// The path + /// The + [Obsolete("This method will be removed.")] + public byte[] GetFile(string path) + { + if (path == null) + throw new ArgumentNullException("path"); + + if (path.Length == 0) + throw new ArgumentException("An empty string.", "path"); + + if (path.IndexOf("..") > -1) + throw new ArgumentException("It contains '..'.", "path"); + + path = createFilePath(path); + return File.Exists(path) ? File.ReadAllBytes(path) : null; + } - try { - try { - _services.Stop (1006, String.Empty); + /// + /// Removes a WebSocket service with the specified . + /// + /// The path + /// The + public bool RemoveWebSocketService(string path) + { + return _services.RemoveService(path); } - finally { - _listener.Abort (); + + /// + /// Starts receiving incoming requests. + /// + public void Start() + { + if (_secure) + { + string msg; + if (!checkCertificate(out msg)) + throw new InvalidOperationException(msg); + } + + start(); } - } - catch { - } - _state = ServerState.Stop; - } + /// + /// Stops receiving incoming requests and closes each connection. + /// + public void Stop() + { + stop(1005, String.Empty); + } - private bool canSet (out string message) - { - message = null; + /// + /// Stops receiving incoming requests and closes each connection. + /// + /// The code + /// The reason + public void Stop(CloseStatusCode code, string reason) + { + if (code == CloseStatusCode.MandatoryExtension) + { + var msg = "MandatoryExtension cannot be used."; + throw new ArgumentException(msg, "code"); + } - if (_state == ServerState.Start) { - message = "The server has already started."; - return false; - } + if (!reason.IsNullOrEmpty()) + { + if (code == CloseStatusCode.NoStatus) + { + var msg = "NoStatus cannot be used."; + throw new ArgumentException(msg, "code"); + } - if (_state == ServerState.ShuttingDown) { - message = "The server is shutting down."; - return false; - } + byte[] bytes; + if (!reason.TryGetUTF8EncodedBytes(out bytes)) + { + var msg = "It could not be UTF-8-encoded."; + throw new ArgumentException(msg, "reason"); + } - return true; - } + if (bytes.Length > 123) + { + var msg = "Its size is greater than 123 bytes."; + throw new ArgumentOutOfRangeException("reason", msg); + } + } - private bool checkCertificate (out string message) - { - message = null; + stop((ushort)code, reason); + } - var byUser = _listener.SslConfiguration.ServerCertificate != null; + /// + /// Stops receiving incoming requests and closes each connection. + /// + /// The code + /// The reason + public void Stop(ushort code, string reason) + { + if (!code.IsCloseStatusCode()) + { + var msg = "Less than 1000 or greater than 4999."; + throw new ArgumentOutOfRangeException("code", msg); + } - var path = _listener.CertificateFolderPath; - var withPort = EndPointListener.CertificateExists (_port, path); + if (code == 1010) + { + var msg = "1010 cannot be used."; + throw new ArgumentException(msg, "code"); + } - var both = byUser && withPort; - if (both) { - _log.Warn ("A server certificate associated with the port is used."); - return true; - } + if (!reason.IsNullOrEmpty()) + { + if (code == 1005) + { + var msg = "1005 cannot be used."; + throw new ArgumentException(msg, "code"); + } - var either = byUser || withPort; - if (!either) { - message = "There is no server certificate for secure connections."; - return false; - } + byte[] bytes; + if (!reason.TryGetUTF8EncodedBytes(out bytes)) + { + var msg = "It could not be UTF-8-encoded."; + throw new ArgumentException(msg, "reason"); + } - return true; - } + if (bytes.Length > 123) + { + var msg = "Its size is greater than 123 bytes."; + throw new ArgumentOutOfRangeException("reason", msg); + } + } - private string createFilePath (string childPath) - { - childPath = childPath.TrimStart ('/', '\\'); - return new StringBuilder (_docRootPath, 32) - .AppendFormat ("/{0}", childPath) - .ToString () - .Replace ('\\', '/'); - } + stop(code, reason); + } - private static HttpListener createListener ( + /// + /// The createListener + /// + /// The hostname + /// The port + /// The secure + /// The + private static HttpListener createListener( string hostname, int port, bool secure ) - { - var lsnr = new HttpListener (); + { + var lsnr = new HttpListener(); - var schm = secure ? "https" : "http"; - var pref = String.Format ("{0}://{1}:{2}/", schm, hostname, port); - lsnr.Prefixes.Add (pref); + var schm = secure ? "https" : "http"; + var pref = String.Format("{0}://{1}:{2}/", schm, hostname, port); + lsnr.Prefixes.Add(pref); - return lsnr; - } + return lsnr; + } - private void init ( - string hostname, System.Net.IPAddress address, int port, bool secure + /// + /// The tryCreateUri + /// + /// The uriString + /// The result + /// The message + /// The + private static bool tryCreateUri( + string uriString, out Uri result, out string message ) - { - _hostname = hostname; - _address = address; - _port = port; - _secure = secure; - - _docRootPath = "./Public"; - _listener = createListener (_hostname, _port, _secure); - _log = _listener.Log; - _services = new WebSocketServiceManager (_log); - _sync = new object (); - } - - private void processRequest (HttpListenerContext context) - { - var method = context.Request.HttpMethod; - var evt = method == "GET" - ? OnGet - : method == "HEAD" - ? OnHead - : method == "POST" - ? OnPost - : method == "PUT" - ? OnPut - : method == "DELETE" - ? OnDelete - : method == "CONNECT" - ? OnConnect - : method == "OPTIONS" - ? OnOptions - : method == "TRACE" - ? OnTrace - : null; - - if (evt != null) - evt (this, new HttpRequestEventArgs (context, _docRootPath)); - else - context.Response.StatusCode = 501; // Not Implemented - - context.Response.Close (); - } - - private void processRequest (HttpListenerWebSocketContext context) - { - var uri = context.RequestUri; - if (uri == null) { - context.Close (HttpStatusCode.BadRequest); - return; - } - - var path = uri.AbsolutePath; - - WebSocketServiceHostBase host; - if (!_services.InternalTryGetServiceHost (path, out host)) { - context.Close (HttpStatusCode.NotImplemented); - return; - } + { + result = null; + message = null; + + var uri = uriString.ToUri(); + if (uri == null) + { + message = "An invalid URI string."; + return false; + } - host.StartSession (context); - } + if (!uri.IsAbsoluteUri) + { + message = "A relative URI."; + return false; + } - private void receiveRequest () - { - while (true) { - HttpListenerContext ctx = null; - try { - ctx = _listener.GetContext (); - ThreadPool.QueueUserWorkItem ( - state => { - try { - if (ctx.Request.IsUpgradeRequest ("websocket")) { - processRequest (ctx.AcceptWebSocket (null)); - return; - } + var schm = uri.Scheme; + if (!(schm == "http" || schm == "https")) + { + message = "The scheme part is not 'http' or 'https'."; + return false; + } - processRequest (ctx); - } - catch (Exception ex) { - _log.Fatal (ex.Message); - _log.Debug (ex.ToString ()); + if (uri.PathAndQuery != "/") + { + message = "It includes either or both path and query components."; + return false; + } - ctx.Connection.Close (true); - } + if (uri.Fragment.Length > 0) + { + message = "It includes the fragment component."; + return false; } - ); - } - catch (HttpListenerException) { - _log.Info ("The underlying listener is stopped."); - break; - } - catch (InvalidOperationException) { - _log.Info ("The underlying listener is stopped."); - break; - } - catch (Exception ex) { - _log.Fatal (ex.Message); - _log.Debug (ex.ToString ()); - if (ctx != null) - ctx.Connection.Close (true); + if (uri.Port == 0) + { + message = "The port part is zero."; + return false; + } - break; + result = uri; + return true; } - } - if (_state != ServerState.ShuttingDown) - abort (); - } + /// + /// The abort + /// + private void abort() + { + lock (_sync) + { + if (_state != ServerState.Start) + return; + + _state = ServerState.ShuttingDown; + } - private void start () - { - if (_state == ServerState.Start) { - _log.Info ("The server has already started."); - return; - } - - if (_state == ServerState.ShuttingDown) { - _log.Warn ("The server is shutting down."); - return; - } - - lock (_sync) { - if (_state == ServerState.Start) { - _log.Info ("The server has already started."); - return; - } + try + { + try + { + _services.Stop(1006, String.Empty); + } + finally + { + _listener.Abort(); + } + } + catch + { + } - if (_state == ServerState.ShuttingDown) { - _log.Warn ("The server is shutting down."); - return; + _state = ServerState.Stop; } - _services.Start (); + /// + /// The canSet + /// + /// The message + /// The + private bool canSet(out string message) + { + message = null; + + if (_state == ServerState.Start) + { + message = "The server has already started."; + return false; + } - try { - startReceiving (); - } - catch { - _services.Stop (1011, String.Empty); - throw; + if (_state == ServerState.ShuttingDown) + { + message = "The server is shutting down."; + return false; + } + + return true; } - _state = ServerState.Start; - } - } + /// + /// The checkCertificate + /// + /// The message + /// The + private bool checkCertificate(out string message) + { + message = null; + + var byUser = _listener.SslConfiguration.ServerCertificate != null; + + var path = _listener.CertificateFolderPath; + var withPort = EndPointListener.CertificateExists(_port, path); + + var both = byUser && withPort; + if (both) + { + _log.Warn("A server certificate associated with the port is used."); + return true; + } - private void startReceiving () - { - try { - _listener.Start (); - } - catch (Exception ex) { - var msg = "The underlying listener has failed to start."; - throw new InvalidOperationException (msg, ex); - } - - _receiveThread = new Thread (new ThreadStart (receiveRequest)); - _receiveThread.IsBackground = true; - _receiveThread.Start (); - } + var either = byUser || withPort; + if (!either) + { + message = "There is no server certificate for secure connections."; + return false; + } - private void stop (ushort code, string reason) - { - if (_state == ServerState.Ready) { - _log.Info ("The server is not started."); - return; - } - - if (_state == ServerState.ShuttingDown) { - _log.Info ("The server is shutting down."); - return; - } - - if (_state == ServerState.Stop) { - _log.Info ("The server has already stopped."); - return; - } - - lock (_sync) { - if (_state == ServerState.ShuttingDown) { - _log.Info ("The server is shutting down."); - return; + return true; } - if (_state == ServerState.Stop) { - _log.Info ("The server has already stopped."); - return; + /// + /// The createFilePath + /// + /// The childPath + /// The + private string createFilePath(string childPath) + { + childPath = childPath.TrimStart('/', '\\'); + return new StringBuilder(_docRootPath, 32) + .AppendFormat("/{0}", childPath) + .ToString() + .Replace('\\', '/'); } - _state = ServerState.ShuttingDown; - } - - try { - var threw = false; - try { - _services.Stop (code, reason); - } - catch { - threw = true; - throw; - } - finally { - try { - stopReceiving (5000); - } - catch { - if (!threw) - throw; - } + /// + /// The init + /// + /// The hostname + /// The address + /// The port + /// The secure + private void init( + string hostname, System.Net.IPAddress address, int port, bool secure + ) + { + _hostname = hostname; + _address = address; + _port = port; + _secure = secure; + + _docRootPath = "./Public"; + _listener = createListener(_hostname, _port, _secure); + _log = _listener.Log; + _services = new WebSocketServiceManager(_log); + _sync = new object(); } - } - finally { - _state = ServerState.Stop; - } - } - - private void stopReceiving (int millisecondsTimeout) - { - _listener.Stop (); - _receiveThread.Join (millisecondsTimeout); - } - private static bool tryCreateUri ( - string uriString, out Uri result, out string message - ) - { - result = null; - message = null; - - var uri = uriString.ToUri (); - if (uri == null) { - message = "An invalid URI string."; - return false; - } - - if (!uri.IsAbsoluteUri) { - message = "A relative URI."; - return false; - } - - var schm = uri.Scheme; - if (!(schm == "http" || schm == "https")) { - message = "The scheme part is not 'http' or 'https'."; - return false; - } - - if (uri.PathAndQuery != "/") { - message = "It includes either or both path and query components."; - return false; - } - - if (uri.Fragment.Length > 0) { - message = "It includes the fragment component."; - return false; - } - - if (uri.Port == 0) { - message = "The port part is zero."; - return false; - } - - result = uri; - return true; - } + /// + /// The processRequest + /// + /// The context + private void processRequest(HttpListenerContext context) + { + var method = context.Request.HttpMethod; + var evt = method == "GET" + ? OnGet + : method == "HEAD" + ? OnHead + : method == "POST" + ? OnPost + : method == "PUT" + ? OnPut + : method == "DELETE" + ? OnDelete + : method == "CONNECT" + ? OnConnect + : method == "OPTIONS" + ? OnOptions + : method == "TRACE" + ? OnTrace + : null; + + if (evt != null) + evt(this, new HttpRequestEventArgs(context, _docRootPath)); + else + context.Response.StatusCode = 501; // Not Implemented + + context.Response.Close(); + } - #endregion + /// + /// The processRequest + /// + /// The context + private void processRequest(HttpListenerWebSocketContext context) + { + var uri = context.RequestUri; + if (uri == null) + { + context.Close(HttpStatusCode.BadRequest); + return; + } - #region Public Methods + var path = uri.AbsolutePath; - /// - /// Adds a WebSocket service with the specified behavior, - /// , and . - /// - /// - /// is converted to a URL-decoded string and - /// '/' is trimmed from the end of the converted string if any. - /// - /// - /// A that represents an absolute path to - /// the service to add. - /// - /// - /// - /// A Func<TBehavior> delegate. - /// - /// - /// It invokes the method called for creating - /// a new session instance for the service. - /// - /// - /// The method must create a new instance of - /// the specified behavior class and return it. - /// - /// - /// - /// - /// The type of the behavior for the service. - /// - /// - /// It must inherit the class. - /// - /// - /// - /// - /// is . - /// - /// - /// -or- - /// - /// - /// is . - /// - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is not an absolute path. - /// - /// - /// -or- - /// - /// - /// includes either or both - /// query and fragment components. - /// - /// - /// -or- - /// - /// - /// is already in use. - /// - /// - [Obsolete ("This method will be removed. Use added one instead.")] - public void AddWebSocketService ( - string path, Func creator - ) - where TBehavior : WebSocketBehavior - { - if (path == null) - throw new ArgumentNullException ("path"); + WebSocketServiceHostBase host; + if (!_services.InternalTryGetServiceHost(path, out host)) + { + context.Close(HttpStatusCode.NotImplemented); + return; + } - if (creator == null) - throw new ArgumentNullException ("creator"); + host.StartSession(context); + } - if (path.Length == 0) - throw new ArgumentException ("An empty string.", "path"); + /// + /// The receiveRequest + /// + private void receiveRequest() + { + while (true) + { + HttpListenerContext ctx = null; + try + { + ctx = _listener.GetContext(); + ThreadPool.QueueUserWorkItem( + state => + { + try + { + if (ctx.Request.IsUpgradeRequest("websocket")) + { + processRequest(ctx.AcceptWebSocket(null)); + return; + } + + processRequest(ctx); + } + catch (Exception ex) + { + _log.Fatal(ex.Message); + _log.Debug(ex.ToString()); + + ctx.Connection.Close(true); + } + } + ); + } + catch (HttpListenerException) + { + _log.Info("The underlying listener is stopped."); + break; + } + catch (InvalidOperationException) + { + _log.Info("The underlying listener is stopped."); + break; + } + catch (Exception ex) + { + _log.Fatal(ex.Message); + _log.Debug(ex.ToString()); - if (path[0] != '/') - throw new ArgumentException ("Not an absolute path.", "path"); + if (ctx != null) + ctx.Connection.Close(true); - if (path.IndexOfAny (new[] { '?', '#' }) > -1) { - var msg = "It includes either or both query and fragment components."; - throw new ArgumentException (msg, "path"); - } + break; + } + } - _services.Add (path, creator); - } + if (_state != ServerState.ShuttingDown) + abort(); + } - /// - /// Adds a WebSocket service with the specified behavior and - /// . - /// - /// - /// is converted to a URL-decoded string and - /// '/' is trimmed from the end of the converted string if any. - /// - /// - /// A that represents an absolute path to - /// the service to add. - /// - /// - /// - /// The type of the behavior for the service. - /// - /// - /// It must inherit the class and - /// must have a public parameterless constructor. - /// - /// - /// - /// is . - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is not an absolute path. - /// - /// - /// -or- - /// - /// - /// includes either or both - /// query and fragment components. - /// - /// - /// -or- - /// - /// - /// is already in use. - /// - /// - public void AddWebSocketService (string path) - where TBehaviorWithNew : WebSocketBehavior, new () - { - _services.AddService (path, null); - } + /// + /// The start + /// + private void start() + { + if (_state == ServerState.Start) + { + _log.Info("The server has already started."); + return; + } - /// - /// Adds a WebSocket service with the specified behavior, - /// , and . - /// - /// - /// is converted to a URL-decoded string and - /// '/' is trimmed from the end of the converted string if any. - /// - /// - /// A that represents an absolute path to - /// the service to add. - /// - /// - /// - /// An Action<TBehaviorWithNew> delegate or - /// if not needed. - /// - /// - /// That delegate invokes the method called for initializing - /// a new session instance for the service. - /// - /// - /// - /// - /// The type of the behavior for the service. - /// - /// - /// It must inherit the class and - /// must have a public parameterless constructor. - /// - /// - /// - /// is . - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is not an absolute path. - /// - /// - /// -or- - /// - /// - /// includes either or both - /// query and fragment components. - /// - /// - /// -or- - /// - /// - /// is already in use. - /// - /// - public void AddWebSocketService ( - string path, Action initializer - ) - where TBehaviorWithNew : WebSocketBehavior, new () - { - _services.AddService (path, initializer); - } + if (_state == ServerState.ShuttingDown) + { + _log.Warn("The server is shutting down."); + return; + } - /// - /// Gets the contents of the specified file from the document - /// folder of the server. - /// - /// - /// - /// An array of or - /// if it fails. - /// - /// - /// That array represents the contents of the file. - /// - /// - /// - /// A that represents a virtual path to - /// find the file from the document folder. - /// - /// - /// is . - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// contains "..". - /// - /// - [Obsolete ("This method will be removed.")] - public byte[] GetFile (string path) - { - if (path == null) - throw new ArgumentNullException ("path"); + lock (_sync) + { + if (_state == ServerState.Start) + { + _log.Info("The server has already started."); + return; + } - if (path.Length == 0) - throw new ArgumentException ("An empty string.", "path"); + if (_state == ServerState.ShuttingDown) + { + _log.Warn("The server is shutting down."); + return; + } - if (path.IndexOf ("..") > -1) - throw new ArgumentException ("It contains '..'.", "path"); + _services.Start(); - path = createFilePath (path); - return File.Exists (path) ? File.ReadAllBytes (path) : null; - } + try + { + startReceiving(); + } + catch + { + _services.Stop(1011, String.Empty); + throw; + } - /// - /// Removes a WebSocket service with the specified . - /// - /// - /// - /// is converted to a URL-decoded string and - /// '/' is trimmed from the end of the converted string if any. - /// - /// - /// The service is stopped with close status 1001 (going away) - /// if it has already started. - /// - /// - /// - /// true if the service is successfully found and removed; - /// otherwise, false. - /// - /// - /// A that represents an absolute path to - /// the service to remove. - /// - /// - /// is . - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is not an absolute path. - /// - /// - /// -or- - /// - /// - /// includes either or both - /// query and fragment components. - /// - /// - public bool RemoveWebSocketService (string path) - { - return _services.RemoveService (path); - } + _state = ServerState.Start; + } + } - /// - /// Starts receiving incoming requests. - /// - /// - /// This method does nothing if the server has already started or - /// it is shutting down. - /// - /// - /// - /// There is no server certificate for secure connections. - /// - /// - /// -or- - /// - /// - /// The underlying has failed to start. - /// - /// - public void Start () - { - if (_secure) { - string msg; - if (!checkCertificate (out msg)) - throw new InvalidOperationException (msg); - } + /// + /// The startReceiving + /// + private void startReceiving() + { + try + { + _listener.Start(); + } + catch (Exception ex) + { + var msg = "The underlying listener has failed to start."; + throw new InvalidOperationException(msg, ex); + } - start (); - } + _receiveThread = new Thread(new ThreadStart(receiveRequest)); + _receiveThread.IsBackground = true; + _receiveThread.Start(); + } - /// - /// Stops receiving incoming requests and closes each connection. - /// - /// - /// This method does nothing if the server is not started, - /// it is shutting down, or it has already stopped. - /// - public void Stop () - { - stop (1005, String.Empty); - } + /// + /// The stop + /// + /// The code + /// The reason + private void stop(ushort code, string reason) + { + if (_state == ServerState.Ready) + { + _log.Info("The server is not started."); + return; + } - /// - /// Stops receiving incoming requests and closes each connection. - /// - /// - /// This method does nothing if the server is not started, - /// it is shutting down, or it has already stopped. - /// - /// - /// - /// A that represents the status code - /// indicating the reason for the WebSocket connection close. - /// - /// - /// The status codes are defined in - /// - /// Section 7.4 of RFC 6455. - /// - /// - /// - /// - /// A that represents the reason for - /// the WebSocket connection close. - /// - /// - /// The size must be 123 bytes or less in UTF-8. - /// - /// - /// - /// - /// is less than 1000 or greater than 4999. - /// - /// - /// -or- - /// - /// - /// The size of is greater than 123 bytes. - /// - /// - /// - /// - /// is 1010 (mandatory extension). - /// - /// - /// -or- - /// - /// - /// is 1005 (no status) and - /// there is . - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - public void Stop (ushort code, string reason) - { - if (!code.IsCloseStatusCode ()) { - var msg = "Less than 1000 or greater than 4999."; - throw new ArgumentOutOfRangeException ("code", msg); - } - - if (code == 1010) { - var msg = "1010 cannot be used."; - throw new ArgumentException (msg, "code"); - } - - if (!reason.IsNullOrEmpty ()) { - if (code == 1005) { - var msg = "1005 cannot be used."; - throw new ArgumentException (msg, "code"); - } + if (_state == ServerState.ShuttingDown) + { + _log.Info("The server is shutting down."); + return; + } - byte[] bytes; - if (!reason.TryGetUTF8EncodedBytes (out bytes)) { - var msg = "It could not be UTF-8-encoded."; - throw new ArgumentException (msg, "reason"); - } + if (_state == ServerState.Stop) + { + _log.Info("The server has already stopped."); + return; + } - if (bytes.Length > 123) { - var msg = "Its size is greater than 123 bytes."; - throw new ArgumentOutOfRangeException ("reason", msg); - } - } + lock (_sync) + { + if (_state == ServerState.ShuttingDown) + { + _log.Info("The server is shutting down."); + return; + } - stop (code, reason); - } + if (_state == ServerState.Stop) + { + _log.Info("The server has already stopped."); + return; + } - /// - /// Stops receiving incoming requests and closes each connection. - /// - /// - /// This method does nothing if the server is not started, - /// it is shutting down, or it has already stopped. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It represents the status code indicating the reason for - /// the WebSocket connection close. - /// - /// - /// - /// - /// A that represents the reason for - /// the WebSocket connection close. - /// - /// - /// The size must be 123 bytes or less in UTF-8. - /// - /// - /// - /// The size of is greater than 123 bytes. - /// - /// - /// - /// is - /// . - /// - /// - /// -or- - /// - /// - /// is - /// and - /// there is . - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - public void Stop (CloseStatusCode code, string reason) - { - if (code == CloseStatusCode.MandatoryExtension) { - var msg = "MandatoryExtension cannot be used."; - throw new ArgumentException (msg, "code"); - } - - if (!reason.IsNullOrEmpty ()) { - if (code == CloseStatusCode.NoStatus) { - var msg = "NoStatus cannot be used."; - throw new ArgumentException (msg, "code"); - } + _state = ServerState.ShuttingDown; + } - byte[] bytes; - if (!reason.TryGetUTF8EncodedBytes (out bytes)) { - var msg = "It could not be UTF-8-encoded."; - throw new ArgumentException (msg, "reason"); + try + { + var threw = false; + try + { + _services.Stop(code, reason); + } + catch + { + threw = true; + throw; + } + finally + { + try + { + stopReceiving(5000); + } + catch + { + if (!threw) + throw; + } + } + } + finally + { + _state = ServerState.Stop; + } } - if (bytes.Length > 123) { - var msg = "Its size is greater than 123 bytes."; - throw new ArgumentOutOfRangeException ("reason", msg); + /// + /// The stopReceiving + /// + /// The millisecondsTimeout + private void stopReceiving(int millisecondsTimeout) + { + _listener.Stop(); + _receiveThread.Join(millisecondsTimeout); } - } - stop ((ushort) code, reason); + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Server/IWebSocketSession.cs b/src/WebSocket/WebSocketCore/Server/IWebSocketSession.cs index 30ef4e47f..0ae1c1eb3 100644 --- a/src/WebSocket/WebSocketCore/Server/IWebSocketSession.cs +++ b/src/WebSocket/WebSocketCore/Server/IWebSocketSession.cs @@ -1,4 +1,3 @@ -#region License /* * IWebSocketSession.cs * @@ -24,68 +23,48 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; using WebSocketCore.Net.WebSockets; namespace WebSocketCore.Server { - /// - /// Exposes the access to the information in a WebSocket session. - /// - public interface IWebSocketSession - { - #region Properties + #region ӿ /// - /// Gets the current state of the WebSocket connection for the session. + /// Exposes the access to the information in a WebSocket session. /// - /// - /// - /// One of the enum values. - /// - /// - /// It indicates the current state of the connection. - /// - /// - WebSocketState ConnectionState { get; } + public interface IWebSocketSession + { + #region - /// - /// Gets the information in the WebSocket handshake request. - /// - /// - /// A instance that provides the access to - /// the information in the handshake request. - /// - WebSocketContext Context { get; } + /// + /// Gets the current state of the WebSocket connection for the session. + /// + WebSocketState ConnectionState { get; } - /// - /// Gets the unique ID of the session. - /// - /// - /// A that represents the unique ID of the session. - /// - string ID { get; } + /// + /// Gets the information in the WebSocket handshake request. + /// + WebSocketContext Context { get; } - /// - /// Gets the name of the WebSocket subprotocol for the session. - /// - /// - /// A that represents the name of the subprotocol - /// if present. - /// - string Protocol { get; } + /// + /// Gets the unique ID of the session. + /// + string ID { get; } - /// - /// Gets the time that the session has started. - /// - /// - /// A that represents the time that the session - /// has started. - /// - DateTime StartTime { get; } + /// + /// Gets the name of the WebSocket subprotocol for the session. + /// + string Protocol { get; } + + /// + /// Gets the time that the session has started. + /// + DateTime StartTime { get; } + + #endregion + } - #endregion - } -} + #endregion ӿ +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Server/ServerState.cs b/src/WebSocket/WebSocketCore/Server/ServerState.cs index 5659a570c..a73f67d2e 100644 --- a/src/WebSocket/WebSocketCore/Server/ServerState.cs +++ b/src/WebSocket/WebSocketCore/Server/ServerState.cs @@ -1,4 +1,3 @@ -#region License /* * ServerState.cs * @@ -24,17 +23,38 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; namespace WebSocketCore.Server { - internal enum ServerState - { - Ready, - Start, - ShuttingDown, - Stop - } -} + #region ö + + /// + /// Defines the ServerState + /// + internal enum ServerState + { + /// + /// Defines the Ready + /// + Ready, + + /// + /// Defines the Start + /// + Start, + + /// + /// Defines the ShuttingDown + /// + ShuttingDown, + + /// + /// Defines the Stop + /// + Stop + } + + #endregion ö +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Server/WebSocketBehavior.cs b/src/WebSocket/WebSocketCore/Server/WebSocketBehavior.cs index 7e63e6dd4..2de78d0ba 100644 --- a/src/WebSocket/WebSocketCore/Server/WebSocketBehavior.cs +++ b/src/WebSocket/WebSocketCore/Server/WebSocketBehavior.cs @@ -1,4 +1,3 @@ -#region License /* * WebSocketBehavior.cs * @@ -24,7 +23,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; using System.IO; @@ -33,842 +31,566 @@ namespace WebSocketCore.Server { - /// - /// Exposes a set of methods and properties used to define the behavior of - /// a WebSocket service provided by the or - /// . - /// - /// - /// This class is an abstract class. - /// - public abstract class WebSocketBehavior : IWebSocketSession - { - #region Private Fields - - private WebSocketContext _context; - private Func _cookiesValidator; - private bool _emitOnPing; - private string _id; - private bool _ignoreExtensions; - private Func _originValidator; - private string _protocol; - private WebSocketSessionManager _sessions; - private DateTime _startTime; - private WebSocket _websocket; - - #endregion - - #region Protected Constructors - - /// - /// Initializes a new instance of the class. - /// - protected WebSocketBehavior () - { - _startTime = DateTime.MaxValue; - } - - #endregion - - #region Protected Properties - - /// - /// Gets the logging function. - /// - /// - /// - /// A that provides the logging function. - /// - /// - /// if the session has not started yet. - /// - /// - [Obsolete ("This property will be removed.")] - protected Logger Log { - get { - return _websocket != null ? _websocket.Log : null; - } - } - - /// - /// Gets the management function for the sessions in the service. - /// - /// - /// - /// A that manages the sessions in - /// the service. - /// - /// - /// if the session has not started yet. - /// - /// - protected WebSocketSessionManager Sessions { - get { - return _sessions; - } - } - #endregion - - #region Public Properties - - /// - /// Gets the current state of the WebSocket connection for a session. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It indicates the current state of the connection. - /// - /// - /// if the session has not - /// started yet. - /// - /// - public WebSocketState ConnectionState { - get { - return _websocket != null - ? _websocket.ReadyState - : WebSocketState.Connecting; - } - } - - /// - /// Gets the information in a WebSocket handshake request to the service. - /// - /// - /// - /// A instance that provides the access to - /// the information in the handshake request. - /// - /// - /// if the session has not started yet. - /// - /// - public WebSocketContext Context { - get { - return _context; - } - } - - /// - /// Gets or sets the delegate used to validate the HTTP cookies included in - /// a WebSocket handshake request to the service. - /// - /// - /// - /// A Func<CookieCollection, CookieCollection, bool> delegate - /// or if not needed. - /// - /// - /// The delegate invokes the method called when the WebSocket instance - /// for a session validates the handshake request. - /// - /// - /// 1st parameter passed to the method - /// contains the cookies to validate if present. - /// - /// - /// 2nd parameter passed to the method - /// receives the cookies to send to the client. - /// - /// - /// The method must return true if the cookies are valid. - /// - /// - /// The default value is . - /// - /// - public Func CookiesValidator { - get { - return _cookiesValidator; - } - - set { - _cookiesValidator = value; - } - } - - /// - /// Gets or sets a value indicating whether the WebSocket instance for - /// a session emits the message event when receives a ping. - /// - /// - /// - /// true if the WebSocket instance emits the message event - /// when receives a ping; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - public bool EmitOnPing { - get { - return _websocket != null ? _websocket.EmitOnPing : _emitOnPing; - } - - set { - if (_websocket != null) { - _websocket.EmitOnPing = value; - return; - } - - _emitOnPing = value; - } - } - - /// - /// Gets the unique ID of a session. - /// - /// - /// - /// A that represents the unique ID of the session. - /// - /// - /// if the session has not started yet. - /// - /// - public string ID { - get { - return _id; - } - } - - /// - /// Gets or sets a value indicating whether the service ignores - /// the Sec-WebSocket-Extensions header included in a WebSocket - /// handshake request. - /// - /// - /// - /// true if the service ignores the extensions requested - /// from a client; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - public bool IgnoreExtensions { - get { - return _ignoreExtensions; - } - - set { - _ignoreExtensions = value; - } - } - - /// - /// Gets or sets the delegate used to validate the Origin header included in - /// a WebSocket handshake request to the service. - /// - /// - /// - /// A Func<string, bool> delegate or - /// if not needed. - /// - /// - /// The delegate invokes the method called when the WebSocket instance - /// for a session validates the handshake request. - /// - /// - /// The parameter passed to the method is the value - /// of the Origin header or if the header is not - /// present. - /// - /// - /// The method must return true if the header value is valid. - /// - /// - /// The default value is . - /// - /// - public Func OriginValidator { - get { - return _originValidator; - } - - set { - _originValidator = value; - } - } - - /// - /// Gets or sets the name of the WebSocket subprotocol for the service. - /// - /// - /// - /// A that represents the name of the subprotocol. - /// - /// - /// The value specified for a set must be a token defined in - /// - /// RFC 2616. - /// - /// - /// The default value is an empty string. - /// - /// - /// - /// The set operation is not available if the session has already started. - /// - /// - /// The value specified for a set operation is not a token. - /// - public string Protocol { - get { - return _websocket != null - ? _websocket.Protocol - : (_protocol ?? String.Empty); - } - - set { - if (ConnectionState != WebSocketState.Connecting) { - var msg = "The session has already started."; - throw new InvalidOperationException (msg); - } - - if (value == null || value.Length == 0) { - _protocol = null; - return; - } - - if (!value.IsToken ()) - throw new ArgumentException ("Not a token.", "value"); - - _protocol = value; - } - } - /// - /// Gets the time that a session has started. + /// Exposes a set of methods and properties used to define the behavior of + /// a WebSocket service provided by the or + /// . /// - /// - /// - /// A that represents the time that the session - /// has started. - /// - /// - /// if the session has not started yet. - /// - /// - public DateTime StartTime { - get { - return _startTime; - } - } - - #endregion - - #region Private Methods - - private string checkHandshakeRequest (WebSocketContext context) - { - if (_originValidator != null) { - if (!_originValidator (context.Origin)) - return "It includes no Origin header or an invalid one."; - } - - if (_cookiesValidator != null) { - var req = context.CookieCollection; - var res = context.WebSocket.CookieCollection; - if (!_cookiesValidator (req, res)) - return "It includes no cookie or an invalid one."; - } - - return null; - } - - private void onClose (object sender, CloseEventArgs e) + public abstract class WebSocketBehavior : IWebSocketSession { - if (_id == null) - return; - - _sessions.Remove (_id); - OnClose (e); - } - - private void onError (object sender, ErrorEventArgs e) - { - OnError (e); - } + #region ֶ + + /// + /// Defines the _context + /// + private WebSocketContext _context; + + /// + /// Defines the _cookiesValidator + /// + private Func _cookiesValidator; + + /// + /// Defines the _emitOnPing + /// + private bool _emitOnPing; + + /// + /// Defines the _id + /// + private string _id; + + /// + /// Defines the _ignoreExtensions + /// + private bool _ignoreExtensions; + + /// + /// Defines the _originValidator + /// + private Func _originValidator; + + /// + /// Defines the _protocol + /// + private string _protocol; + + /// + /// Defines the _sessions + /// + private WebSocketSessionManager _sessions; + + /// + /// Defines the _startTime + /// + private DateTime _startTime; + + /// + /// Defines the _websocket + /// + private WebSocket _websocket; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + protected WebSocketBehavior() + { + _startTime = DateTime.MaxValue; + } - private void onMessage (object sender, MessageEventArgs e) - { - OnMessage (e); - } + #endregion 캯 + + #region + + /// + /// Gets the current state of the WebSocket connection for a session. + /// + public WebSocketState ConnectionState + { + get + { + return _websocket != null + ? _websocket.ReadyState + : WebSocketState.Connecting; + } + } - private void onOpen (object sender, EventArgs e) - { - _id = _sessions.Add (this); - if (_id == null) { - _websocket.Close (CloseStatusCode.Away); - return; - } - - _startTime = DateTime.Now; - OnOpen (); - } + /// + /// Gets the information in a WebSocket handshake request to the service. + /// + public WebSocketContext Context + { + get + { + return _context; + } + } - #endregion + /// + /// Gets or sets the delegate used to validate the HTTP cookies included in + /// a WebSocket handshake request to the service. + /// + public Func CookiesValidator + { + get + { + return _cookiesValidator; + } + + set + { + _cookiesValidator = value; + } + } - #region Internal Methods + /// + /// Gets or sets a value indicating whether the WebSocket instance for + /// a session emits the message event when receives a ping. + /// + public bool EmitOnPing + { + get + { + return _websocket != null ? _websocket.EmitOnPing : _emitOnPing; + } + + set + { + if (_websocket != null) + { + _websocket.EmitOnPing = value; + return; + } + + _emitOnPing = value; + } + } - internal void Start (WebSocketContext context, WebSocketSessionManager sessions) - { - if (_websocket != null) { - _websocket.Log.Error ("A session instance cannot be reused."); - context.WebSocket.Close (HttpStatusCode.ServiceUnavailable); - - return; - } - - _context = context; - _sessions = sessions; - - _websocket = context.WebSocket; - _websocket.CustomHandshakeRequestChecker = checkHandshakeRequest; - _websocket.EmitOnPing = _emitOnPing; - _websocket.IgnoreExtensions = _ignoreExtensions; - _websocket.Protocol = _protocol; - - var waitTime = sessions.WaitTime; - if (waitTime != _websocket.WaitTime) - _websocket.WaitTime = waitTime; - - _websocket.OnOpen += onOpen; - _websocket.OnMessage += onMessage; - _websocket.OnError += onError; - _websocket.OnClose += onClose; - _websocket.InternalAccept (); - } + /// + /// Gets the unique ID of a session. + /// + public string ID + { + get + { + return _id; + } + } - #endregion + /// + /// Gets or sets a value indicating whether the service ignores + /// the Sec-WebSocket-Extensions header included in a WebSocket + /// handshake request. + /// + public bool IgnoreExtensions + { + get + { + return _ignoreExtensions; + } + + set + { + _ignoreExtensions = value; + } + } - #region Protected Methods + /// + /// Gets or sets the delegate used to validate the Origin header included in + /// a WebSocket handshake request to the service. + /// + public Func OriginValidator + { + get + { + return _originValidator; + } + + set + { + _originValidator = value; + } + } - /// - /// Closes the WebSocket connection for a session. - /// - /// - /// This method does nothing if the current state of the connection is - /// Closing or Closed. - /// - /// - /// The session has not started yet. - /// - protected void Close () - { - if (_websocket == null) { - var msg = "The session has not started yet."; - throw new InvalidOperationException (msg); - } + /// + /// Gets or sets the name of the WebSocket subprotocol for the service. + /// + public string Protocol + { + get + { + return _websocket != null + ? _websocket.Protocol + : (_protocol ?? String.Empty); + } + + set + { + if (ConnectionState != WebSocketState.Connecting) + { + var msg = "The session has already started."; + throw new InvalidOperationException(msg); + } + + if (value == null || value.Length == 0) + { + _protocol = null; + return; + } + + if (!value.IsToken()) + throw new ArgumentException("Not a token.", "value"); + + _protocol = value; + } + } - _websocket.Close (); - } + /// + /// Gets the time that a session has started. + /// + public DateTime StartTime + { + get + { + return _startTime; + } + } - /// - /// Calls the method with the specified message. - /// - /// - /// A that represents the error message. - /// - /// - /// An instance that represents the cause of - /// the error if present. - /// - /// - /// is . - /// - /// - /// is an empty string. - /// - [Obsolete ("This method will be removed.")] - protected void Error (string message, Exception exception) - { - if (message == null) - throw new ArgumentNullException ("message"); + /// + /// Gets the logging function. + /// + [Obsolete("This property will be removed.")] + protected Logger Log + { + get + { + return _websocket != null ? _websocket.Log : null; + } + } - if (message.Length == 0) - throw new ArgumentException ("An empty string.", "message"); + /// + /// Gets the management function for the sessions in the service. + /// + protected WebSocketSessionManager Sessions + { + get + { + return _sessions; + } + } - OnError (new ErrorEventArgs (message, exception)); - } + #endregion + + #region + + /// + /// The Start + /// + /// The context + /// The sessions + internal void Start(WebSocketContext context, WebSocketSessionManager sessions) + { + if (_websocket != null) + { + _websocket.Log.Error("A session instance cannot be reused."); + context.WebSocket.Close(HttpStatusCode.ServiceUnavailable); + + return; + } + + _context = context; + _sessions = sessions; + + _websocket = context.WebSocket; + _websocket.CustomHandshakeRequestChecker = checkHandshakeRequest; + _websocket.EmitOnPing = _emitOnPing; + _websocket.IgnoreExtensions = _ignoreExtensions; + _websocket.Protocol = _protocol; + + var waitTime = sessions.WaitTime; + if (waitTime != _websocket.WaitTime) + _websocket.WaitTime = waitTime; + + _websocket.OnOpen += onOpen; + _websocket.OnMessage += onMessage; + _websocket.OnError += onError; + _websocket.OnClose += onClose; + _websocket.InternalAccept(); + } - /// - /// Called when the WebSocket connection for a session has been closed. - /// - /// - /// A that represents the event data passed - /// from a event. - /// - protected virtual void OnClose (CloseEventArgs e) - { - } + /// + /// Closes the WebSocket connection for a session. + /// + protected void Close() + { + if (_websocket == null) + { + var msg = "The session has not started yet."; + throw new InvalidOperationException(msg); + } + + _websocket.Close(); + } - /// - /// Called when the WebSocket instance for a session gets an error. - /// - /// - /// A that represents the event data passed - /// from a event. - /// - protected virtual void OnError (ErrorEventArgs e) - { - } + /// + /// Calls the method with the specified message. + /// + /// The message + /// The exception + [Obsolete("This method will be removed.")] + protected void Error(string message, Exception exception) + { + if (message == null) + throw new ArgumentNullException("message"); + + if (message.Length == 0) + throw new ArgumentException("An empty string.", "message"); + + OnError(new ErrorEventArgs(message, exception)); + } - /// - /// Called when the WebSocket instance for a session receives a message. - /// - /// - /// A that represents the event data passed - /// from a event. - /// - protected virtual void OnMessage (MessageEventArgs e) - { - } + /// + /// Called when the WebSocket connection for a session has been closed. + /// + /// The e + protected virtual void OnClose(CloseEventArgs e) + { + } - /// - /// Called when the WebSocket connection for a session has been established. - /// - protected virtual void OnOpen () - { - } + /// + /// Called when the WebSocket instance for a session gets an error. + /// + /// The e + protected virtual void OnError(ErrorEventArgs e) + { + } - /// - /// Sends the specified data to a client using the WebSocket connection. - /// - /// - /// An array of that represents the binary data to send. - /// - /// - /// The current state of the connection is not Open. - /// - /// - /// is . - /// - protected void Send (byte[] data) - { - if (_websocket == null) { - var msg = "The current state of the connection is not Open."; - throw new InvalidOperationException (msg); - } + /// + /// Called when the WebSocket instance for a session receives a message. + /// + /// The e + protected virtual void OnMessage(MessageEventArgs e) + { + } - _websocket.Send (data); - } + /// + /// Called when the WebSocket connection for a session has been established. + /// + protected virtual void OnOpen() + { + } - /// - /// Sends the specified file to a client using the WebSocket connection. - /// - /// - /// - /// A that specifies the file to send. - /// - /// - /// The file is sent as the binary data. - /// - /// - /// - /// The current state of the connection is not Open. - /// - /// - /// is . - /// - /// - /// - /// The file does not exist. - /// - /// - /// -or- - /// - /// - /// The file could not be opened. - /// - /// - protected void Send (FileInfo fileInfo) - { - if (_websocket == null) { - var msg = "The current state of the connection is not Open."; - throw new InvalidOperationException (msg); - } + /// + /// Sends the specified data to a client using the WebSocket connection. + /// + /// The data + protected void Send(byte[] data) + { + if (_websocket == null) + { + var msg = "The current state of the connection is not Open."; + throw new InvalidOperationException(msg); + } + + _websocket.Send(data); + } - _websocket.Send (fileInfo); - } + /// + /// Sends the specified file to a client using the WebSocket connection. + /// + /// The fileInfo + protected void Send(FileInfo fileInfo) + { + if (_websocket == null) + { + var msg = "The current state of the connection is not Open."; + throw new InvalidOperationException(msg); + } + + _websocket.Send(fileInfo); + } - /// - /// Sends the specified data to a client using the WebSocket connection. - /// - /// - /// A that represents the text data to send. - /// - /// - /// The current state of the connection is not Open. - /// - /// - /// is . - /// - /// - /// could not be UTF-8-encoded. - /// - protected void Send (string data) - { - if (_websocket == null) { - var msg = "The current state of the connection is not Open."; - throw new InvalidOperationException (msg); - } + /// + /// Sends the data from the specified stream to a client using + /// the WebSocket connection. + /// + /// The stream + /// The length + protected void Send(Stream stream, int length) + { + if (_websocket == null) + { + var msg = "The current state of the connection is not Open."; + throw new InvalidOperationException(msg); + } + + _websocket.Send(stream, length); + } - _websocket.Send (data); - } + /// + /// Sends the specified data to a client using the WebSocket connection. + /// + /// The data + protected void Send(string data) + { + if (_websocket == null) + { + var msg = "The current state of the connection is not Open."; + throw new InvalidOperationException(msg); + } + + _websocket.Send(data); + } - /// - /// Sends the data from the specified stream to a client using - /// the WebSocket connection. - /// - /// - /// - /// A instance from which to read the data to send. - /// - /// - /// The data is sent as the binary data. - /// - /// - /// - /// An that specifies the number of bytes to send. - /// - /// - /// The current state of the connection is not Open. - /// - /// - /// is . - /// - /// - /// - /// cannot be read. - /// - /// - /// -or- - /// - /// - /// is less than 1. - /// - /// - /// -or- - /// - /// - /// No data could be read from . - /// - /// - protected void Send (Stream stream, int length) - { - if (_websocket == null) { - var msg = "The current state of the connection is not Open."; - throw new InvalidOperationException (msg); - } + /// + /// Sends the specified data to a client asynchronously using + /// the WebSocket connection. + /// + /// The data + /// The completed + protected void SendAsync(byte[] data, Action completed) + { + if (_websocket == null) + { + var msg = "The current state of the connection is not Open."; + throw new InvalidOperationException(msg); + } + + _websocket.SendAsync(data, completed); + } - _websocket.Send (stream, length); - } + /// + /// Sends the specified file to a client asynchronously using + /// the WebSocket connection. + /// + /// The fileInfo + /// The completed + protected void SendAsync(FileInfo fileInfo, Action completed) + { + if (_websocket == null) + { + var msg = "The current state of the connection is not Open."; + throw new InvalidOperationException(msg); + } + + _websocket.SendAsync(fileInfo, completed); + } - /// - /// Sends the specified data to a client asynchronously using - /// the WebSocket connection. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// An array of that represents the binary data to send. - /// - /// - /// - /// An Action<bool> delegate or - /// if not needed. - /// - /// - /// The delegate invokes the method called when the send is complete. - /// - /// - /// true is passed to the method if the send has done with - /// no error; otherwise, false. - /// - /// - /// - /// The current state of the connection is not Open. - /// - /// - /// is . - /// - protected void SendAsync (byte[] data, Action completed) - { - if (_websocket == null) { - var msg = "The current state of the connection is not Open."; - throw new InvalidOperationException (msg); - } + /// + /// Sends the data from the specified stream to a client asynchronously + /// using the WebSocket connection. + /// + /// The stream + /// The length + /// The completed + protected void SendAsync(Stream stream, int length, Action completed) + { + if (_websocket == null) + { + var msg = "The current state of the connection is not Open."; + throw new InvalidOperationException(msg); + } + + _websocket.SendAsync(stream, length, completed); + } - _websocket.SendAsync (data, completed); - } + /// + /// Sends the specified data to a client asynchronously using + /// the WebSocket connection. + /// + /// The data + /// The completed + protected void SendAsync(string data, Action completed) + { + if (_websocket == null) + { + var msg = "The current state of the connection is not Open."; + throw new InvalidOperationException(msg); + } + + _websocket.SendAsync(data, completed); + } - /// - /// Sends the specified file to a client asynchronously using - /// the WebSocket connection. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// - /// A that specifies the file to send. - /// - /// - /// The file is sent as the binary data. - /// - /// - /// - /// - /// An Action<bool> delegate or - /// if not needed. - /// - /// - /// The delegate invokes the method called when the send is complete. - /// - /// - /// true is passed to the method if the send has done with - /// no error; otherwise, false. - /// - /// - /// - /// The current state of the connection is not Open. - /// - /// - /// is . - /// - /// - /// - /// The file does not exist. - /// - /// - /// -or- - /// - /// - /// The file could not be opened. - /// - /// - protected void SendAsync (FileInfo fileInfo, Action completed) - { - if (_websocket == null) { - var msg = "The current state of the connection is not Open."; - throw new InvalidOperationException (msg); - } + /// + /// The checkHandshakeRequest + /// + /// The context + /// The + private string checkHandshakeRequest(WebSocketContext context) + { + if (_originValidator != null) + { + if (!_originValidator(context.Origin)) + return "It includes no Origin header or an invalid one."; + } + + if (_cookiesValidator != null) + { + var req = context.CookieCollection; + var res = context.WebSocket.CookieCollection; + if (!_cookiesValidator(req, res)) + return "It includes no cookie or an invalid one."; + } + + return null; + } - _websocket.SendAsync (fileInfo, completed); - } + /// + /// The onClose + /// + /// The sender + /// The e + private void onClose(object sender, CloseEventArgs e) + { + if (_id == null) + return; + + _sessions.Remove(_id); + OnClose(e); + } - /// - /// Sends the specified data to a client asynchronously using - /// the WebSocket connection. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// A that represents the text data to send. - /// - /// - /// - /// An Action<bool> delegate or - /// if not needed. - /// - /// - /// The delegate invokes the method called when the send is complete. - /// - /// - /// true is passed to the method if the send has done with - /// no error; otherwise, false. - /// - /// - /// - /// The current state of the connection is not Open. - /// - /// - /// is . - /// - /// - /// could not be UTF-8-encoded. - /// - protected void SendAsync (string data, Action completed) - { - if (_websocket == null) { - var msg = "The current state of the connection is not Open."; - throw new InvalidOperationException (msg); - } + /// + /// The onError + /// + /// The sender + /// The e + private void onError(object sender, ErrorEventArgs e) + { + OnError(e); + } - _websocket.SendAsync (data, completed); - } + /// + /// The onMessage + /// + /// The sender + /// The e + private void onMessage(object sender, MessageEventArgs e) + { + OnMessage(e); + } - /// - /// Sends the data from the specified stream to a client asynchronously - /// using the WebSocket connection. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// - /// A instance from which to read the data to send. - /// - /// - /// The data is sent as the binary data. - /// - /// - /// - /// An that specifies the number of bytes to send. - /// - /// - /// - /// An Action<bool> delegate or - /// if not needed. - /// - /// - /// The delegate invokes the method called when the send is complete. - /// - /// - /// true is passed to the method if the send has done with - /// no error; otherwise, false. - /// - /// - /// - /// The current state of the connection is not Open. - /// - /// - /// is . - /// - /// - /// - /// cannot be read. - /// - /// - /// -or- - /// - /// - /// is less than 1. - /// - /// - /// -or- - /// - /// - /// No data could be read from . - /// - /// - protected void SendAsync (Stream stream, int length, Action completed) - { - if (_websocket == null) { - var msg = "The current state of the connection is not Open."; - throw new InvalidOperationException (msg); - } + /// + /// The onOpen + /// + /// The sender + /// The e + private void onOpen(object sender, EventArgs e) + { + _id = _sessions.Add(this); + if (_id == null) + { + _websocket.Close(CloseStatusCode.Away); + return; + } + + _startTime = DateTime.Now; + OnOpen(); + } - _websocket.SendAsync (stream, length, completed); + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Server/WebSocketServer.cs b/src/WebSocket/WebSocketCore/Server/WebSocketServer.cs index 017d67b27..18d4bafa5 100644 --- a/src/WebSocket/WebSocketCore/Server/WebSocketServer.cs +++ b/src/WebSocket/WebSocketCore/Server/WebSocketServer.cs @@ -1,4 +1,3 @@ -#region License /* * WebSocketServer.cs * @@ -24,9 +23,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Contributors /* * Contributors: * - Juan Manuel Lallana @@ -34,7 +31,6 @@ * - Liryna * - Rohan Singh */ -#endregion using System; using System.Collections.Generic; @@ -48,1481 +44,1160 @@ namespace WebSocketCore.Server { - /// - /// Provides a WebSocket protocol server. - /// - /// - /// This class can provide multiple WebSocket services. - /// - public class WebSocketServer - { - #region Private Fields - - private System.Net.IPAddress _address; - private bool _allowForwardedRequest; - private AuthenticationSchemes _authSchemes; - private static readonly string _defaultRealm; - private bool _dnsStyle; - private string _hostname; - private TcpListener _listener; - private Logger _log; - private int _port; - private string _realm; - private string _realmInUse; - private Thread _receiveThread; - private bool _reuseAddress; - private bool _secure; - private WebSocketServiceManager _services; - private ServerSslConfiguration _sslConfig; - private ServerSslConfiguration _sslConfigInUse; - private volatile ServerState _state; - private object _sync; - private Func _userCredFinder; - - #endregion - - #region Static Constructor - - static WebSocketServer () - { - _defaultRealm = "SECRET AREA"; - } - - #endregion - - #region Public Constructors - /// - /// Initializes a new instance of the class. + /// Provides a WebSocket protocol server. /// - /// - /// The new instance listens for incoming handshake requests on - /// and port 80. - /// - public WebSocketServer () + public class WebSocketServer { - var addr = System.Net.IPAddress.Any; - init (addr.ToString (), addr, 80, false); - } + #region ֶ - /// - /// Initializes a new instance of the class - /// with the specified . - /// - /// - /// - /// The new instance listens for incoming handshake requests on - /// and . - /// - /// - /// It provides secure connections if is 443. - /// - /// - /// - /// An that represents the number of the port - /// on which to listen. - /// - /// - /// is less than 1 or greater than 65535. - /// - public WebSocketServer (int port) - : this (port, port == 443) - { - } + /// + /// Defines the _defaultRealm + /// + private static readonly string _defaultRealm; - /// - /// Initializes a new instance of the class - /// with the specified . - /// - /// - /// - /// The new instance listens for incoming handshake requests on - /// the IP address of the host of and - /// the port of . - /// - /// - /// Either port 80 or 443 is used if includes - /// no port. Port 443 is used if the scheme of - /// is wss; otherwise, port 80 is used. - /// - /// - /// The new instance provides secure connections if the scheme of - /// is wss. - /// - /// - /// - /// A that represents the WebSocket URL of the server. - /// - /// - /// is . - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is invalid. - /// - /// - public WebSocketServer (string url) - { - if (url == null) - throw new ArgumentNullException ("url"); + /// + /// Defines the _address + /// + private System.Net.IPAddress _address; - if (url.Length == 0) - throw new ArgumentException ("An empty string.", "url"); + /// + /// Defines the _allowForwardedRequest + /// + private bool _allowForwardedRequest; - Uri uri; - string msg; - if (!tryCreateUri (url, out uri, out msg)) - throw new ArgumentException (msg, "url"); + /// + /// Defines the _authSchemes + /// + private AuthenticationSchemes _authSchemes; - var host = uri.DnsSafeHost; + /// + /// Defines the _dnsStyle + /// + private bool _dnsStyle; - var addr = host.ToIPAddress (); - if (addr == null) { - msg = "The host part could not be converted to an IP address."; - throw new ArgumentException (msg, "url"); - } + /// + /// Defines the _hostname + /// + private string _hostname; - if (!addr.IsLocal ()) { - msg = "The IP address of the host is not a local IP address."; - throw new ArgumentException (msg, "url"); - } + /// + /// Defines the _listener + /// + private TcpListener _listener; - init (host, addr, uri.Port, uri.Scheme == "wss"); - } + /// + /// Defines the _log + /// + private Logger _log; - /// - /// Initializes a new instance of the class - /// with the specified and . - /// - /// - /// The new instance listens for incoming handshake requests on - /// and . - /// - /// - /// An that represents the number of the port - /// on which to listen. - /// - /// - /// A : true if the new instance provides - /// secure connections; otherwise, false. - /// - /// - /// is less than 1 or greater than 65535. - /// - public WebSocketServer (int port, bool secure) - { - if (!port.IsPortNumber ()) { - var msg = "Less than 1 or greater than 65535."; - throw new ArgumentOutOfRangeException ("port", msg); - } + /// + /// Defines the _port + /// + private int _port; - var addr = System.Net.IPAddress.Any; - init (addr.ToString (), addr, port, secure); - } + /// + /// Defines the _realm + /// + private string _realm; - /// - /// Initializes a new instance of the class - /// with the specified and . - /// - /// - /// - /// The new instance listens for incoming handshake requests on - /// and . - /// - /// - /// It provides secure connections if is 443. - /// - /// - /// - /// A that represents the local - /// IP address on which to listen. - /// - /// - /// An that represents the number of the port - /// on which to listen. - /// - /// - /// is . - /// - /// - /// is not a local IP address. - /// - /// - /// is less than 1 or greater than 65535. - /// - public WebSocketServer (System.Net.IPAddress address, int port) - : this (address, port, port == 443) - { - } + /// + /// Defines the _realmInUse + /// + private string _realmInUse; - /// - /// Initializes a new instance of the class - /// with the specified , , - /// and . - /// - /// - /// The new instance listens for incoming handshake requests on - /// and . - /// - /// - /// A that represents the local - /// IP address on which to listen. - /// - /// - /// An that represents the number of the port - /// on which to listen. - /// - /// - /// A : true if the new instance provides - /// secure connections; otherwise, false. - /// - /// - /// is . - /// - /// - /// is not a local IP address. - /// - /// - /// is less than 1 or greater than 65535. - /// - public WebSocketServer (System.Net.IPAddress address, int port, bool secure) - { - if (address == null) - throw new ArgumentNullException ("address"); + /// + /// Defines the _receiveThread + /// + private Thread _receiveThread; - if (!address.IsLocal ()) - throw new ArgumentException ("Not a local IP address.", "address"); + /// + /// Defines the _reuseAddress + /// + private bool _reuseAddress; - if (!port.IsPortNumber ()) { - var msg = "Less than 1 or greater than 65535."; - throw new ArgumentOutOfRangeException ("port", msg); - } + /// + /// Defines the _secure + /// + private bool _secure; - init (address.ToString (), address, port, secure); - } + /// + /// Defines the _services + /// + private WebSocketServiceManager _services; - #endregion + /// + /// Defines the _sslConfig + /// + private ServerSslConfiguration _sslConfig; - #region Public Properties + /// + /// Defines the _sslConfigInUse + /// + private ServerSslConfiguration _sslConfigInUse; - /// - /// Gets the IP address of the server. - /// - /// - /// A that represents the local - /// IP address on which to listen for incoming handshake requests. - /// - public System.Net.IPAddress Address { - get { - return _address; - } - } + /// + /// Defines the _state + /// + private volatile ServerState _state; - /// - /// Gets or sets a value indicating whether the server accepts every - /// handshake request without checking the request URI. - /// - /// - /// The set operation does nothing if the server has already started or - /// it is shutting down. - /// - /// - /// - /// true if the server accepts every handshake request without - /// checking the request URI; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - public bool AllowForwardedRequest { - get { - return _allowForwardedRequest; - } - - set { - string msg; - if (!canSet (out msg)) { - _log.Warn (msg); - return; - } + /// + /// Defines the _sync + /// + private object _sync; - lock (_sync) { - if (!canSet (out msg)) { - _log.Warn (msg); - return; - } + /// + /// Defines the _userCredFinder + /// + private Func _userCredFinder; - _allowForwardedRequest = value; - } - } - } + #endregion ֶ - /// - /// Gets or sets the scheme used to authenticate the clients. - /// - /// - /// The set operation does nothing if the server has already started or - /// it is shutting down. - /// - /// - /// - /// One of the - /// enum values. - /// - /// - /// It represents the scheme used to authenticate the clients. - /// - /// - /// The default value is - /// . - /// - /// - public AuthenticationSchemes AuthenticationSchemes { - get { - return _authSchemes; - } - - set { - string msg; - if (!canSet (out msg)) { - _log.Warn (msg); - return; - } + #region 캯 - lock (_sync) { - if (!canSet (out msg)) { - _log.Warn (msg); - return; - } + /// + /// Initializes a new instance of the class. + /// + public WebSocketServer() + { + var addr = System.Net.IPAddress.Any; + init(addr.ToString(), addr, 80, false); + } - _authSchemes = value; + /// + /// Initializes a new instance of the class. + /// + /// The port + public WebSocketServer(int port) + : this(port, port == 443) + { } - } - } - /// - /// Gets a value indicating whether the server has started. - /// - /// - /// true if the server has started; otherwise, false. - /// - public bool IsListening { - get { - return _state == ServerState.Start; - } - } + /// + /// Initializes a new instance of the class. + /// + /// The port + /// The secure + public WebSocketServer(int port, bool secure) + { + if (!port.IsPortNumber()) + { + var msg = "Less than 1 or greater than 65535."; + throw new ArgumentOutOfRangeException("port", msg); + } - /// - /// Gets a value indicating whether secure connections are provided. - /// - /// - /// true if this instance provides secure connections; otherwise, - /// false. - /// - public bool IsSecure { - get { - return _secure; - } - } + var addr = System.Net.IPAddress.Any; + init(addr.ToString(), addr, port, secure); + } - /// - /// Gets or sets a value indicating whether the server cleans up - /// the inactive sessions periodically. - /// - /// - /// The set operation does nothing if the server has already started or - /// it is shutting down. - /// - /// - /// - /// true if the server cleans up the inactive sessions every - /// 60 seconds; otherwise, false. - /// - /// - /// The default value is true. - /// - /// - public bool KeepClean { - get { - return _services.KeepClean; - } - - set { - _services.KeepClean = value; - } - } + /// + /// Initializes a new instance of the class. + /// + /// The url + public WebSocketServer(string url) + { + if (url == null) + throw new ArgumentNullException("url"); + + if (url.Length == 0) + throw new ArgumentException("An empty string.", "url"); + + Uri uri; + string msg; + if (!tryCreateUri(url, out uri, out msg)) + throw new ArgumentException(msg, "url"); + + var host = uri.DnsSafeHost; + + var addr = host.ToIPAddress(); + if (addr == null) + { + msg = "The host part could not be converted to an IP address."; + throw new ArgumentException(msg, "url"); + } - /// - /// Gets the logging function for the server. - /// - /// - /// The default logging level is . - /// - /// - /// A that provides the logging function. - /// - public Logger Log { - get { - return _log; - } - } + if (!addr.IsLocal()) + { + msg = "The IP address of the host is not a local IP address."; + throw new ArgumentException(msg, "url"); + } - /// - /// Gets the port of the server. - /// - /// - /// An that represents the number of the port - /// on which to listen for incoming handshake requests. - /// - public int Port { - get { - return _port; - } - } + init(host, addr, uri.Port, uri.Scheme == "wss"); + } - /// - /// Gets or sets the realm used for authentication. - /// - /// - /// - /// "SECRET AREA" is used as the realm if the value is - /// or an empty string. - /// - /// - /// The set operation does nothing if the server has - /// already started or it is shutting down. - /// - /// - /// - /// - /// A or by default. - /// - /// - /// That string represents the name of the realm. - /// - /// - public string Realm { - get { - return _realm; - } - - set { - string msg; - if (!canSet (out msg)) { - _log.Warn (msg); - return; + /// + /// Initializes a new instance of the class. + /// + /// The address + /// The port + public WebSocketServer(System.Net.IPAddress address, int port) + : this(address, port, port == 443) + { } - lock (_sync) { - if (!canSet (out msg)) { - _log.Warn (msg); - return; - } + /// + /// Initializes a new instance of the class. + /// + /// The address + /// The port + /// The secure + public WebSocketServer(System.Net.IPAddress address, int port, bool secure) + { + if (address == null) + throw new ArgumentNullException("address"); + + if (!address.IsLocal()) + throw new ArgumentException("Not a local IP address.", "address"); + + if (!port.IsPortNumber()) + { + var msg = "Less than 1 or greater than 65535."; + throw new ArgumentOutOfRangeException("port", msg); + } - _realm = value; + init(address.ToString(), address, port, secure); } - } - } - /// - /// Gets or sets a value indicating whether the server is allowed to - /// be bound to an address that is already in use. - /// - /// - /// - /// You should set this property to true if you would - /// like to resolve to wait for socket in TIME_WAIT state. - /// - /// - /// The set operation does nothing if the server has already - /// started or it is shutting down. - /// - /// - /// - /// - /// true if the server is allowed to be bound to an address - /// that is already in use; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - public bool ReuseAddress { - get { - return _reuseAddress; - } - - set { - string msg; - if (!canSet (out msg)) { - _log.Warn (msg); - return; + /// + /// Initializes static members of the class. + /// + static WebSocketServer() + { + _defaultRealm = "SECRET AREA"; } - lock (_sync) { - if (!canSet (out msg)) { - _log.Warn (msg); - return; - } + #endregion 캯 - _reuseAddress = value; - } - } - } + #region - /// - /// Gets the configuration for secure connections. - /// - /// - /// This configuration will be referenced when attempts to start, - /// so it must be configured before the start method is called. - /// - /// - /// A that represents - /// the configuration used to provide secure connections. - /// - /// - /// This instance does not provide secure connections. - /// - public ServerSslConfiguration SslConfiguration { - get { - if (!_secure) { - var msg = "This instance does not provide secure connections."; - throw new InvalidOperationException (msg); + /// + /// Gets the IP address of the server. + /// + public System.Net.IPAddress Address + { + get + { + return _address; + } } - return getSslConfiguration (); - } - } + /// + /// Gets or sets a value indicating whether the server accepts every + /// handshake request without checking the request URI. + /// + public bool AllowForwardedRequest + { + get + { + return _allowForwardedRequest; + } - /// - /// Gets or sets the delegate used to find the credentials - /// for an identity. - /// - /// - /// - /// No credentials are found if the method invoked by - /// the delegate returns or - /// the value is . - /// - /// - /// The set operation does nothing if the server has - /// already started or it is shutting down. - /// - /// - /// - /// - /// A Func<, - /// > delegate or - /// if not needed. - /// - /// - /// That delegate invokes the method called for finding - /// the credentials used to authenticate a client. - /// - /// - /// The default value is . - /// - /// - public Func UserCredentialsFinder { - get { - return _userCredFinder; - } - - set { - string msg; - if (!canSet (out msg)) { - _log.Warn (msg); - return; + set + { + string msg; + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } + + lock (_sync) + { + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } + + _allowForwardedRequest = value; + } + } } - lock (_sync) { - if (!canSet (out msg)) { - _log.Warn (msg); - return; - } + /// + /// Gets or sets the scheme used to authenticate the clients. + /// + public AuthenticationSchemes AuthenticationSchemes + { + get + { + return _authSchemes; + } - _userCredFinder = value; + set + { + string msg; + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } + + lock (_sync) + { + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } + + _authSchemes = value; + } + } } - } - } - /// - /// Gets or sets the time to wait for the response to the WebSocket Ping or - /// Close. - /// - /// - /// The set operation does nothing if the server has already started or - /// it is shutting down. - /// - /// - /// - /// A to wait for the response. - /// - /// - /// The default value is the same as 1 second. - /// - /// - /// - /// The value specified for a set operation is zero or less. - /// - public TimeSpan WaitTime { - get { - return _services.WaitTime; - } - - set { - _services.WaitTime = value; - } - } + /// + /// Gets a value indicating whether the server has started. + /// + public bool IsListening + { + get + { + return _state == ServerState.Start; + } + } - /// - /// Gets the management function for the WebSocket services - /// provided by the server. - /// - /// - /// A that manages - /// the WebSocket services provided by the server. - /// - public WebSocketServiceManager WebSocketServices { - get { - return _services; - } - } + /// + /// Gets a value indicating whether secure connections are provided. + /// + public bool IsSecure + { + get + { + return _secure; + } + } - #endregion + /// + /// Gets or sets a value indicating whether the server cleans up + /// the inactive sessions periodically. + /// + public bool KeepClean + { + get + { + return _services.KeepClean; + } - #region Private Methods + set + { + _services.KeepClean = value; + } + } - private void abort () - { - lock (_sync) { - if (_state != ServerState.Start) - return; + /// + /// Gets the logging function for the server. + /// + public Logger Log + { + get + { + return _log; + } + } + + /// + /// Gets the port of the server. + /// + public int Port + { + get + { + return _port; + } + } - _state = ServerState.ShuttingDown; - } + /// + /// Gets or sets the realm used for authentication. + /// + public string Realm + { + get + { + return _realm; + } - try { - try { - _listener.Stop (); + set + { + string msg; + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } + + lock (_sync) + { + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } + + _realm = value; + } + } } - finally { - _services.Stop (1006, String.Empty); + + /// + /// Gets or sets a value indicating whether the server is allowed to + /// be bound to an address that is already in use. + /// + public bool ReuseAddress + { + get + { + return _reuseAddress; + } + + set + { + string msg; + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } + + lock (_sync) + { + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } + + _reuseAddress = value; + } + } } - } - catch { - } - _state = ServerState.Stop; - } + /// + /// Gets the configuration for secure connections. + /// + public ServerSslConfiguration SslConfiguration + { + get + { + if (!_secure) + { + var msg = "This instance does not provide secure connections."; + throw new InvalidOperationException(msg); + } + + return getSslConfiguration(); + } + } - private bool authenticateClient (TcpListenerWebSocketContext context) - { - if (_authSchemes == AuthenticationSchemes.Anonymous) - return true; + /// + /// Gets or sets the delegate used to find the credentials + /// for an identity. + /// + public Func UserCredentialsFinder + { + get + { + return _userCredFinder; + } - if (_authSchemes == AuthenticationSchemes.None) - return false; + set + { + string msg; + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } + + lock (_sync) + { + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } + + _userCredFinder = value; + } + } + } - return context.Authenticate (_authSchemes, _realmInUse, _userCredFinder); - } + /// + /// Gets or sets the time to wait for the response to the WebSocket Ping or + /// Close. + /// + public TimeSpan WaitTime + { + get + { + return _services.WaitTime; + } - private bool canSet (out string message) - { - message = null; + set + { + _services.WaitTime = value; + } + } - if (_state == ServerState.Start) { - message = "The server has already started."; - return false; - } + /// + /// Gets the management function for the WebSocket services + /// provided by the server. + /// + public WebSocketServiceManager WebSocketServices + { + get + { + return _services; + } + } - if (_state == ServerState.ShuttingDown) { - message = "The server is shutting down."; - return false; - } + #endregion - return true; - } + #region - private bool checkHostNameForRequest (string name) - { - return !_dnsStyle - || Uri.CheckHostName (name) != UriHostNameType.Dns - || name == _hostname; - } + /// + /// Adds a WebSocket service with the specified behavior, + /// , and . + /// + /// + /// The path + /// The initializer + public void AddWebSocketService( + string path, Action initializer + ) + where TBehaviorWithNew : WebSocketBehavior, new() + { + _services.AddService(path, initializer); + } - private static bool checkSslConfiguration ( - ServerSslConfiguration configuration, out string message + /// + /// Adds a WebSocket service with the specified behavior, + /// , and . + /// + /// + /// The path + /// The creator + public void AddWebSocketService( + string path, Func creator ) - { - message = null; + where TBehavior : WebSocketBehavior + { + if (path == null) + throw new ArgumentNullException("path"); - if (configuration.ServerCertificate == null) { - message = "There is no server certificate for secure connections."; - return false; - } + if (creator == null) + throw new ArgumentNullException("creator"); - return true; - } + if (path.Length == 0) + throw new ArgumentException("An empty string.", "path"); - private string getRealm () - { - var realm = _realm; - return realm != null && realm.Length > 0 ? realm : _defaultRealm; - } + if (path[0] != '/') + throw new ArgumentException("Not an absolute path.", "path"); - private ServerSslConfiguration getSslConfiguration () - { - if (_sslConfig == null) - _sslConfig = new ServerSslConfiguration (); + if (path.IndexOfAny(new[] { '?', '#' }) > -1) + { + var msg = "It includes either or both query and fragment components."; + throw new ArgumentException(msg, "path"); + } - return _sslConfig; - } + _services.Add(path, creator); + } - private void init ( - string hostname, System.Net.IPAddress address, int port, bool secure - ) - { - _hostname = hostname; - _address = address; - _port = port; - _secure = secure; - - _authSchemes = AuthenticationSchemes.Anonymous; - _dnsStyle = Uri.CheckHostName (hostname) == UriHostNameType.Dns; - _listener = new TcpListener (address, port); - _log = new Logger (); - _services = new WebSocketServiceManager (_log); - _sync = new object (); - } + /// + /// Adds a WebSocket service with the specified behavior and + /// . + /// + /// + /// The path + public void AddWebSocketService(string path) + where TBehaviorWithNew : WebSocketBehavior, new() + { + _services.AddService(path, null); + } - private void processRequest (TcpListenerWebSocketContext context) - { - if (!authenticateClient (context)) { - context.Close (HttpStatusCode.Forbidden); - return; - } - - var uri = context.RequestUri; - if (uri == null) { - context.Close (HttpStatusCode.BadRequest); - return; - } - - if (!_allowForwardedRequest) { - if (uri.Port != _port) { - context.Close (HttpStatusCode.BadRequest); - return; + /// + /// The AddWebSocketService + /// + /// The path + /// The webSocketBehavior + public void AddWebSocketService(string path, WebSocketBehavior webSocketBehavior) + { + _services.AddService(path, webSocketBehavior); } - if (!checkHostNameForRequest (uri.DnsSafeHost)) { - context.Close (HttpStatusCode.NotFound); - return; + /// + /// Removes a WebSocket service with the specified . + /// + /// The path + /// The + public bool RemoveWebSocketService(string path) + { + return _services.RemoveService(path); } - } - WebSocketServiceHostBase host; - if (!_services.InternalTryGetServiceHost (uri.AbsolutePath, out host)) { - context.Close (HttpStatusCode.NotImplemented); - return; - } + /// + /// Starts receiving incoming handshake requests. + /// + public void Start() + { + ServerSslConfiguration sslConfig = null; - host.StartSession (context); - } + if (_secure) + { + sslConfig = new ServerSslConfiguration(getSslConfiguration()); - private void receiveRequest () - { - while (true) { - TcpClient cl = null; - try { - cl = _listener.AcceptTcpClient (); - ThreadPool.QueueUserWorkItem ( - state => { - try { - var ctx = new TcpListenerWebSocketContext ( - cl, null, _secure, _sslConfigInUse, _log - ); - - processRequest (ctx); - } - catch (Exception ex) { - _log.Error (ex.Message); - _log.Debug (ex.ToString ()); - - cl.Close (); - } + string msg; + if (!checkSslConfiguration(sslConfig, out msg)) + throw new InvalidOperationException(msg); } - ); - } - catch (SocketException ex) { - if (_state == ServerState.ShuttingDown) { - _log.Info ("The underlying listener is stopped."); - break; - } - _log.Fatal (ex.Message); - _log.Debug (ex.ToString ()); - - break; + start(sslConfig); } - catch (Exception ex) { - _log.Fatal (ex.Message); - _log.Debug (ex.ToString ()); - - if (cl != null) - cl.Close (); - break; + /// + /// Stops receiving incoming handshake requests and closes + /// each connection. + /// + public void Stop() + { + stop(1005, String.Empty); } - } - if (_state != ServerState.ShuttingDown) - abort (); - } + /// + /// Stops receiving incoming handshake requests and closes each + /// connection with the specified and + /// . + /// + /// The code + /// The reason + public void Stop(CloseStatusCode code, string reason) + { + if (code == CloseStatusCode.MandatoryExtension) + { + var msg = "MandatoryExtension cannot be used."; + throw new ArgumentException(msg, "code"); + } - private void start (ServerSslConfiguration sslConfig) - { - if (_state == ServerState.Start) { - _log.Info ("The server has already started."); - return; - } - - if (_state == ServerState.ShuttingDown) { - _log.Warn ("The server is shutting down."); - return; - } - - lock (_sync) { - if (_state == ServerState.Start) { - _log.Info ("The server has already started."); - return; - } + if (!reason.IsNullOrEmpty()) + { + if (code == CloseStatusCode.NoStatus) + { + var msg = "NoStatus cannot be used."; + throw new ArgumentException(msg, "code"); + } + + byte[] bytes; + if (!reason.TryGetUTF8EncodedBytes(out bytes)) + { + var msg = "It could not be UTF-8-encoded."; + throw new ArgumentException(msg, "reason"); + } + + if (bytes.Length > 123) + { + var msg = "Its size is greater than 123 bytes."; + throw new ArgumentOutOfRangeException("reason", msg); + } + } - if (_state == ServerState.ShuttingDown) { - _log.Warn ("The server is shutting down."); - return; + stop((ushort)code, reason); } - _sslConfigInUse = sslConfig; - _realmInUse = getRealm (); - - _services.Start (); - try { - startReceiving (); - } - catch { - _services.Stop (1011, String.Empty); - throw; - } + /// + /// Stops receiving incoming handshake requests and closes each + /// connection with the specified and + /// . + /// + /// The code + /// The reason + public void Stop(ushort code, string reason) + { + if (!code.IsCloseStatusCode()) + { + var msg = "Less than 1000 or greater than 4999."; + throw new ArgumentOutOfRangeException("code", msg); + } - _state = ServerState.Start; - } - } + if (code == 1010) + { + var msg = "1010 cannot be used."; + throw new ArgumentException(msg, "code"); + } - private void startReceiving () - { - if (_reuseAddress) { - _listener.Server.SetSocketOption ( - SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true - ); - } - - try { - _listener.Start (); - } - catch (Exception ex) { - var msg = "The underlying listener has failed to start."; - throw new InvalidOperationException (msg, ex); - } - - _receiveThread = new Thread (new ThreadStart (receiveRequest)); - _receiveThread.IsBackground = true; - _receiveThread.Start (); - } + if (!reason.IsNullOrEmpty()) + { + if (code == 1005) + { + var msg = "1005 cannot be used."; + throw new ArgumentException(msg, "code"); + } + + byte[] bytes; + if (!reason.TryGetUTF8EncodedBytes(out bytes)) + { + var msg = "It could not be UTF-8-encoded."; + throw new ArgumentException(msg, "reason"); + } + + if (bytes.Length > 123) + { + var msg = "Its size is greater than 123 bytes."; + throw new ArgumentOutOfRangeException("reason", msg); + } + } - private void stop (ushort code, string reason) - { - if (_state == ServerState.Ready) { - _log.Info ("The server is not started."); - return; - } - - if (_state == ServerState.ShuttingDown) { - _log.Info ("The server is shutting down."); - return; - } - - if (_state == ServerState.Stop) { - _log.Info ("The server has already stopped."); - return; - } - - lock (_sync) { - if (_state == ServerState.ShuttingDown) { - _log.Info ("The server is shutting down."); - return; + stop(code, reason); } - if (_state == ServerState.Stop) { - _log.Info ("The server has already stopped."); - return; - } + /// + /// The checkSslConfiguration + /// + /// The configuration + /// The message + /// The + private static bool checkSslConfiguration( + ServerSslConfiguration configuration, out string message + ) + { + message = null; - _state = ServerState.ShuttingDown; - } + if (configuration.ServerCertificate == null) + { + message = "There is no server certificate for secure connections."; + return false; + } - try { - var threw = false; - try { - stopReceiving (5000); - } - catch { - threw = true; - throw; + return true; } - finally { - try { - _services.Stop (code, reason); - } - catch { - if (!threw) - throw; - } - } - } - finally { - _state = ServerState.Stop; - } - } - - private void stopReceiving (int millisecondsTimeout) - { - try { - _listener.Stop (); - } - catch (Exception ex) { - var msg = "The underlying listener has failed to stop."; - throw new InvalidOperationException (msg, ex); - } - - _receiveThread.Join (millisecondsTimeout); - } - private static bool tryCreateUri ( + /// + /// The tryCreateUri + /// + /// The uriString + /// The result + /// The message + /// The + private static bool tryCreateUri( string uriString, out Uri result, out string message ) - { - if (!uriString.TryCreateWebSocketUri (out result, out message)) - return false; + { + if (!uriString.TryCreateWebSocketUri(out result, out message)) + return false; - if (result.PathAndQuery != "/") { - result = null; - message = "It includes either or both path and query components."; + if (result.PathAndQuery != "/") + { + result = null; + message = "It includes either or both path and query components."; - return false; - } + return false; + } - return true; - } + return true; + } - #endregion + /// + /// The abort + /// + private void abort() + { + lock (_sync) + { + if (_state != ServerState.Start) + return; + + _state = ServerState.ShuttingDown; + } - #region Public Methods + try + { + try + { + _listener.Stop(); + } + finally + { + _services.Stop(1006, String.Empty); + } + } + catch + { + } - /// - /// Adds a WebSocket service with the specified behavior, - /// , and . - /// - /// - /// is converted to a URL-decoded string and - /// '/' is trimmed from the end of the converted string if any. - /// - /// - /// A that represents an absolute path to - /// the service to add. - /// - /// - /// - /// A Func<TBehavior> delegate. - /// - /// - /// It invokes the method called for creating a new session - /// instance for the service. - /// - /// - /// The method must create a new instance of the specified - /// behavior class and return it. - /// - /// - /// - /// - /// The type of the behavior for the service. - /// - /// - /// It must inherit the class. - /// - /// - /// - /// - /// is . - /// - /// - /// -or- - /// - /// - /// is . - /// - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is not an absolute path. - /// - /// - /// -or- - /// - /// - /// includes either or both - /// query and fragment components. - /// - /// - /// -or- - /// - /// - /// is already in use. - /// - /// - public void AddWebSocketService ( - string path, Func creator - ) - where TBehavior : WebSocketBehavior - { - if (path == null) - throw new ArgumentNullException ("path"); + _state = ServerState.Stop; + } - if (creator == null) - throw new ArgumentNullException ("creator"); + /// + /// The authenticateClient + /// + /// The context + /// The + private bool authenticateClient(TcpListenerWebSocketContext context) + { + if (_authSchemes == AuthenticationSchemes.Anonymous) + return true; - if (path.Length == 0) - throw new ArgumentException ("An empty string.", "path"); + if (_authSchemes == AuthenticationSchemes.None) + return false; - if (path[0] != '/') - throw new ArgumentException ("Not an absolute path.", "path"); + return context.Authenticate(_authSchemes, _realmInUse, _userCredFinder); + } - if (path.IndexOfAny (new[] { '?', '#' }) > -1) { - var msg = "It includes either or both query and fragment components."; - throw new ArgumentException (msg, "path"); - } + /// + /// The canSet + /// + /// The message + /// The + private bool canSet(out string message) + { + message = null; + + if (_state == ServerState.Start) + { + message = "The server has already started."; + return false; + } - _services.Add (path, creator); - } + if (_state == ServerState.ShuttingDown) + { + message = "The server is shutting down."; + return false; + } - /// - /// Adds a WebSocket service with the specified behavior and - /// . - /// - /// - /// is converted to a URL-decoded string and - /// '/' is trimmed from the end of the converted string if any. - /// - /// - /// A that represents an absolute path to - /// the service to add. - /// - /// - /// - /// The type of the behavior for the service. - /// - /// - /// It must inherit the class and - /// have a public parameterless constructor. - /// - /// - /// - /// is . - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is not an absolute path. - /// - /// - /// -or- - /// - /// - /// includes either or both - /// query and fragment components. - /// - /// - /// -or- - /// - /// - /// is already in use. - /// - /// - public void AddWebSocketService (string path) - where TBehaviorWithNew : WebSocketBehavior, new () - { - _services.AddService (path, null); - } + return true; + } - public void AddWebSocketService(string path, WebSocketBehavior webSocketBehavior) - { - _services.AddService(path, webSocketBehavior); - } + /// + /// The checkHostNameForRequest + /// + /// The name + /// The + private bool checkHostNameForRequest(string name) + { + return !_dnsStyle + || Uri.CheckHostName(name) != UriHostNameType.Dns + || name == _hostname; + } /// - /// Adds a WebSocket service with the specified behavior, - /// , and . + /// The getRealm /// - /// - /// is converted to a URL-decoded string and - /// '/' is trimmed from the end of the converted string if any. - /// - /// - /// A that represents an absolute path to - /// the service to add. - /// - /// - /// - /// An Action<TBehaviorWithNew> delegate or - /// if not needed. - /// - /// - /// That delegate invokes the method called for initializing - /// a new session instance for the service. - /// - /// - /// - /// - /// The type of the behavior for the service. - /// - /// - /// It must inherit the class and - /// have a public parameterless constructor. - /// - /// - /// - /// is . - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is not an absolute path. - /// - /// - /// -or- - /// - /// - /// includes either or both - /// query and fragment components. - /// - /// - /// -or- - /// - /// - /// is already in use. - /// - /// - public void AddWebSocketService ( - string path, Action initializer - ) - where TBehaviorWithNew : WebSocketBehavior, new () - { - _services.AddService (path, initializer); - } + /// The + private string getRealm() + { + var realm = _realm; + return realm != null && realm.Length > 0 ? realm : _defaultRealm; + } - /// - /// Removes a WebSocket service with the specified . - /// - /// - /// - /// is converted to a URL-decoded string and - /// '/' is trimmed from the end of the converted string if any. - /// - /// - /// The service is stopped with close status 1001 (going away) - /// if it has already started. - /// - /// - /// - /// true if the service is successfully found and removed; - /// otherwise, false. - /// - /// - /// A that represents an absolute path to - /// the service to remove. - /// - /// - /// is . - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is not an absolute path. - /// - /// - /// -or- - /// - /// - /// includes either or both - /// query and fragment components. - /// - /// - public bool RemoveWebSocketService (string path) - { - return _services.RemoveService (path); - } + /// + /// The getSslConfiguration + /// + /// The + private ServerSslConfiguration getSslConfiguration() + { + if (_sslConfig == null) + _sslConfig = new ServerSslConfiguration(); - /// - /// Starts receiving incoming handshake requests. - /// - /// - /// This method does nothing if the server has already started or - /// it is shutting down. - /// - /// - /// - /// There is no server certificate for secure connections. - /// - /// - /// -or- - /// - /// - /// The underlying has failed to start. - /// - /// - public void Start () - { - ServerSslConfiguration sslConfig = null; + return _sslConfig; + } - if (_secure) { - sslConfig = new ServerSslConfiguration (getSslConfiguration ()); + /// + /// The init + /// + /// The hostname + /// The address + /// The port + /// The secure + private void init( + string hostname, System.Net.IPAddress address, int port, bool secure + ) + { + _hostname = hostname; + _address = address; + _port = port; + _secure = secure; + + _authSchemes = AuthenticationSchemes.Anonymous; + _dnsStyle = Uri.CheckHostName(hostname) == UriHostNameType.Dns; + _listener = new TcpListener(address, port); + _log = new Logger(); + _services = new WebSocketServiceManager(_log); + _sync = new object(); + } - string msg; - if (!checkSslConfiguration (sslConfig, out msg)) - throw new InvalidOperationException (msg); - } + /// + /// The processRequest + /// + /// The context + private void processRequest(TcpListenerWebSocketContext context) + { + if (!authenticateClient(context)) + { + context.Close(HttpStatusCode.Forbidden); + return; + } - start (sslConfig); - } + var uri = context.RequestUri; + if (uri == null) + { + context.Close(HttpStatusCode.BadRequest); + return; + } - /// - /// Stops receiving incoming handshake requests and closes - /// each connection. - /// - /// - /// This method does nothing if the server is not started, - /// it is shutting down, or it has already stopped. - /// - /// - /// The underlying has failed to stop. - /// - public void Stop () - { - stop (1005, String.Empty); - } + if (!_allowForwardedRequest) + { + if (uri.Port != _port) + { + context.Close(HttpStatusCode.BadRequest); + return; + } + + if (!checkHostNameForRequest(uri.DnsSafeHost)) + { + context.Close(HttpStatusCode.NotFound); + return; + } + } - /// - /// Stops receiving incoming handshake requests and closes each - /// connection with the specified and - /// . - /// - /// - /// This method does nothing if the server is not started, - /// it is shutting down, or it has already stopped. - /// - /// - /// - /// A that represents the status code - /// indicating the reason for the close. - /// - /// - /// The status codes are defined in - /// - /// Section 7.4 of RFC 6455. - /// - /// - /// - /// - /// A that represents the reason for the close. - /// - /// - /// The size must be 123 bytes or less in UTF-8. - /// - /// - /// - /// - /// is less than 1000 or greater than 4999. - /// - /// - /// -or- - /// - /// - /// The size of is greater than 123 bytes. - /// - /// - /// - /// - /// is 1010 (mandatory extension). - /// - /// - /// -or- - /// - /// - /// is 1005 (no status) and - /// there is . - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// - /// The underlying has failed to stop. - /// - public void Stop (ushort code, string reason) - { - if (!code.IsCloseStatusCode ()) { - var msg = "Less than 1000 or greater than 4999."; - throw new ArgumentOutOfRangeException ("code", msg); - } - - if (code == 1010) { - var msg = "1010 cannot be used."; - throw new ArgumentException (msg, "code"); - } - - if (!reason.IsNullOrEmpty ()) { - if (code == 1005) { - var msg = "1005 cannot be used."; - throw new ArgumentException (msg, "code"); + WebSocketServiceHostBase host; + if (!_services.InternalTryGetServiceHost(uri.AbsolutePath, out host)) + { + context.Close(HttpStatusCode.NotImplemented); + return; + } + + host.StartSession(context); } - byte[] bytes; - if (!reason.TryGetUTF8EncodedBytes (out bytes)) { - var msg = "It could not be UTF-8-encoded."; - throw new ArgumentException (msg, "reason"); + /// + /// The receiveRequest + /// + private void receiveRequest() + { + while (true) + { + TcpClient cl = null; + try + { + cl = _listener.AcceptTcpClient(); + ThreadPool.QueueUserWorkItem( + state => + { + try + { + var ctx = new TcpListenerWebSocketContext( + cl, null, _secure, _sslConfigInUse, _log + ); + + processRequest(ctx); + } + catch (Exception ex) + { + _log.Error(ex.Message); + _log.Debug(ex.ToString()); + + cl.Close(); + } + } + ); + } + catch (SocketException ex) + { + if (_state == ServerState.ShuttingDown) + { + _log.Info("The underlying listener is stopped."); + break; + } + + _log.Fatal(ex.Message); + _log.Debug(ex.ToString()); + + break; + } + catch (Exception ex) + { + _log.Fatal(ex.Message); + _log.Debug(ex.ToString()); + + if (cl != null) + cl.Close(); + + break; + } + } + + if (_state != ServerState.ShuttingDown) + abort(); } - if (bytes.Length > 123) { - var msg = "Its size is greater than 123 bytes."; - throw new ArgumentOutOfRangeException ("reason", msg); + /// + /// The start + /// + /// The sslConfig + private void start(ServerSslConfiguration sslConfig) + { + if (_state == ServerState.Start) + { + _log.Info("The server has already started."); + return; + } + + if (_state == ServerState.ShuttingDown) + { + _log.Warn("The server is shutting down."); + return; + } + + lock (_sync) + { + if (_state == ServerState.Start) + { + _log.Info("The server has already started."); + return; + } + + if (_state == ServerState.ShuttingDown) + { + _log.Warn("The server is shutting down."); + return; + } + + _sslConfigInUse = sslConfig; + _realmInUse = getRealm(); + + _services.Start(); + try + { + startReceiving(); + } + catch + { + _services.Stop(1011, String.Empty); + throw; + } + + _state = ServerState.Start; + } } - } - stop (code, reason); - } + /// + /// The startReceiving + /// + private void startReceiving() + { + if (_reuseAddress) + { + _listener.Server.SetSocketOption( + SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true + ); + } - /// - /// Stops receiving incoming handshake requests and closes each - /// connection with the specified and - /// . - /// - /// - /// This method does nothing if the server is not started, - /// it is shutting down, or it has already stopped. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It represents the status code indicating the reason for the close. - /// - /// - /// - /// - /// A that represents the reason for the close. - /// - /// - /// The size must be 123 bytes or less in UTF-8. - /// - /// - /// - /// - /// is - /// . - /// - /// - /// -or- - /// - /// - /// is - /// and - /// there is . - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// - /// The size of is greater than 123 bytes. - /// - /// - /// The underlying has failed to stop. - /// - public void Stop (CloseStatusCode code, string reason) - { - if (code == CloseStatusCode.MandatoryExtension) { - var msg = "MandatoryExtension cannot be used."; - throw new ArgumentException (msg, "code"); - } - - if (!reason.IsNullOrEmpty ()) { - if (code == CloseStatusCode.NoStatus) { - var msg = "NoStatus cannot be used."; - throw new ArgumentException (msg, "code"); + try + { + _listener.Start(); + } + catch (Exception ex) + { + var msg = "The underlying listener has failed to start."; + throw new InvalidOperationException(msg, ex); + } + + _receiveThread = new Thread(new ThreadStart(receiveRequest)); + _receiveThread.IsBackground = true; + _receiveThread.Start(); } - byte[] bytes; - if (!reason.TryGetUTF8EncodedBytes (out bytes)) { - var msg = "It could not be UTF-8-encoded."; - throw new ArgumentException (msg, "reason"); + /// + /// The stop + /// + /// The code + /// The reason + private void stop(ushort code, string reason) + { + if (_state == ServerState.Ready) + { + _log.Info("The server is not started."); + return; + } + + if (_state == ServerState.ShuttingDown) + { + _log.Info("The server is shutting down."); + return; + } + + if (_state == ServerState.Stop) + { + _log.Info("The server has already stopped."); + return; + } + + lock (_sync) + { + if (_state == ServerState.ShuttingDown) + { + _log.Info("The server is shutting down."); + return; + } + + if (_state == ServerState.Stop) + { + _log.Info("The server has already stopped."); + return; + } + + _state = ServerState.ShuttingDown; + } + + try + { + var threw = false; + try + { + stopReceiving(5000); + } + catch + { + threw = true; + throw; + } + finally + { + try + { + _services.Stop(code, reason); + } + catch + { + if (!threw) + throw; + } + } + } + finally + { + _state = ServerState.Stop; + } } - if (bytes.Length > 123) { - var msg = "Its size is greater than 123 bytes."; - throw new ArgumentOutOfRangeException ("reason", msg); + /// + /// The stopReceiving + /// + /// The millisecondsTimeout + private void stopReceiving(int millisecondsTimeout) + { + try + { + _listener.Stop(); + } + catch (Exception ex) + { + var msg = "The underlying listener has failed to stop."; + throw new InvalidOperationException(msg, ex); + } + + _receiveThread.Join(millisecondsTimeout); } - } - stop ((ushort) code, reason); + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Server/WebSocketServiceHost.cs b/src/WebSocket/WebSocketCore/Server/WebSocketServiceHost.cs index ccb2394ce..e9cbeb152 100644 --- a/src/WebSocket/WebSocketCore/Server/WebSocketServiceHost.cs +++ b/src/WebSocket/WebSocketCore/Server/WebSocketServiceHost.cs @@ -1,10 +1,42 @@ -using System; +using System; namespace WebSocketCore.Server { + /// + /// Defines the + /// public class WebSocketServiceHost : WebSocketServiceHostBase { + #region ֶ + + /// + /// Defines the _webSocketBehavior + /// private readonly WebSocketBehavior _webSocketBehavior; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The path + /// The webSocketBehavior + /// The log + internal WebSocketServiceHost(string path, WebSocketBehavior webSocketBehavior, Logger log) + : base(path, log) + { + _webSocketBehavior = webSocketBehavior; + } + + #endregion 캯 + + #region + + /// + /// Gets the BehaviorType + /// public override Type BehaviorType { get @@ -13,17 +45,19 @@ public override Type BehaviorType } } - internal WebSocketServiceHost(string path, WebSocketBehavior webSocketBehavior, Logger log) - : base(path, log) - { - _webSocketBehavior = webSocketBehavior; - } + #endregion + #region + + /// + /// The CreateSession + /// + /// The protected override WebSocketBehavior CreateSession() { return _webSocketBehavior; } - + #endregion } -} \ No newline at end of file +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Server/WebSocketServiceHostBase.cs b/src/WebSocket/WebSocketCore/Server/WebSocketServiceHostBase.cs index 7daedee98..b6c9d01c6 100644 --- a/src/WebSocket/WebSocketCore/Server/WebSocketServiceHostBase.cs +++ b/src/WebSocket/WebSocketCore/Server/WebSocketServiceHostBase.cs @@ -1,4 +1,3 @@ -#region License /* * WebSocketServiceHost.cs * @@ -24,50 +23,50 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Contributors /* * Contributors: * - Juan Manuel Lallana */ -#endregion using System; using WebSocketCore.Net.WebSockets; namespace WebSocketCore.Server { - /// - /// Exposes the methods and properties used to access the information in - /// a WebSocket service provided by the or - /// . - /// - /// - /// This class is an abstract class. - /// - public abstract class WebSocketServiceHostBase - { - #region Private Fields + /// + /// Exposes the methods and properties used to access the information in + /// a WebSocket service provided by the or + /// . + /// + public abstract class WebSocketServiceHostBase + { + #region ֶ + /// + /// Defines the _log + /// private Logger _log; + + /// + /// Defines the _path + /// private string _path; + + /// + /// Defines the _sessions + /// private WebSocketSessionManager _sessions; - #endregion + #endregion ֶ - #region Protected Constructors + #region 캯 /// - /// Initializes a new instance of the class - /// with the specified and . + /// Initializes a new instance of the class. /// - /// - /// A that represents the absolute path to the service. - /// - /// - /// A that represents the logging function for the service. - /// + /// The path + /// The log protected WebSocketServiceHostBase(string path, Logger log) { _path = path; @@ -76,52 +75,19 @@ protected WebSocketServiceHostBase(string path, Logger log) _sessions = new WebSocketSessionManager(log); } - #endregion - - #region Internal Properties - - internal ServerState State - { - get - { - return _sessions.State; - } - } - - #endregion + #endregion 캯 - #region Protected Properties + #region /// - /// Gets the logging function for the service. + /// Gets the of the behavior of the service. /// - /// - /// A that provides the logging function. - /// - protected Logger Log - { - get - { - return _log; - } - } - - #endregion - - #region Public Properties + public abstract Type BehaviorType { get; } /// /// Gets or sets a value indicating whether the service cleans up /// the inactive sessions periodically. /// - /// - /// The set operation does nothing if the service has already started or - /// it is shutting down. - /// - /// - /// true if the service cleans up the inactive sessions every - /// 60 seconds; otherwise, false. - /// public bool KeepClean { get @@ -138,10 +104,6 @@ public bool KeepClean /// /// Gets the path to the service. /// - /// - /// A that represents the absolute path to - /// the service. - /// public string Path { get @@ -153,10 +115,6 @@ public string Path /// /// Gets the management function for the sessions in the service. /// - /// - /// A that manages the sessions in - /// the service. - /// public WebSocketSessionManager Sessions { get @@ -165,29 +123,10 @@ public WebSocketSessionManager Sessions } } - /// - /// Gets the of the behavior of the service. - /// - /// - /// A that represents the type of the behavior of - /// the service. - /// - public abstract Type BehaviorType { get; } - /// /// Gets or sets the time to wait for the response to the WebSocket Ping or /// Close. /// - /// - /// The set operation does nothing if the service has already started or - /// it is shutting down. - /// - /// - /// A to wait for the response. - /// - /// - /// The value specified for a set operation is zero or less. - /// public TimeSpan WaitTime { get @@ -201,38 +140,65 @@ public TimeSpan WaitTime } } - #endregion + /// + /// Gets the State + /// + internal ServerState State + { + get + { + return _sessions.State; + } + } + + /// + /// Gets the logging function for the service. + /// + protected Logger Log + { + get + { + return _log; + } + } + + #endregion - #region Internal Methods + #region + /// + /// The Start + /// internal void Start() { _sessions.Start(); } + /// + /// The StartSession + /// + /// The context internal void StartSession(WebSocketContext context) { CreateSession().Start(context, _sessions); } + /// + /// The Stop + /// + /// The code + /// The reason internal void Stop(ushort code, string reason) { _sessions.Stop(code, reason); } - #endregion - - #region Protected Methods - /// /// Creates a new session for the service. /// - /// - /// A instance that represents - /// the new session. - /// + /// The protected abstract WebSocketBehavior CreateSession(); - #endregion + #endregion } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Server/WebSocketServiceHost`1.cs b/src/WebSocket/WebSocketCore/Server/WebSocketServiceHost`1.cs index 7572dac63..4887d76e3 100644 --- a/src/WebSocket/WebSocketCore/Server/WebSocketServiceHost`1.cs +++ b/src/WebSocket/WebSocketCore/Server/WebSocketServiceHost`1.cs @@ -1,4 +1,3 @@ -#region License /* * WebSocketServiceHost`1.cs * @@ -24,79 +23,110 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; namespace WebSocketCore.Server { - internal class WebSocketServiceHost : WebSocketServiceHostBase + /// + /// Defines the + /// + /// + internal class WebSocketServiceHost : WebSocketServiceHostBase where TBehavior : WebSocketBehavior - { - #region Private Fields + { + #region ֶ - private Func _creator; + /// + /// Defines the _creator + /// + private Func _creator; - #endregion + #endregion ֶ - #region Internal Constructors + #region 캯 - internal WebSocketServiceHost ( + /// + /// Initializes a new instance of the class. + /// + /// The path + /// The creator + /// The log + internal WebSocketServiceHost( string path, Func creator, Logger log ) - : this (path, creator, null, log) - { - } - - internal WebSocketServiceHost ( + : this(path, creator, null, log) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The path + /// The creator + /// The initializer + /// The log + internal WebSocketServiceHost( string path, Func creator, Action initializer, Logger log ) - : base (path, log) - { - _creator = createCreator (creator, initializer); - } - - #endregion - - #region Public Properties - - public override Type BehaviorType { - get { - return typeof (TBehavior); - } - } - - #endregion - - #region Private Methods - - private Func createCreator ( + : base(path, log) + { + _creator = createCreator(creator, initializer); + } + + #endregion 캯 + + #region + + /// + /// Gets the BehaviorType + /// + public override Type BehaviorType + { + get + { + return typeof(TBehavior); + } + } + + #endregion + + #region + + /// + /// The CreateSession + /// + /// The + protected override WebSocketBehavior CreateSession() + { + return _creator(); + } + + /// + /// The createCreator + /// + /// The creator + /// The initializer + /// The + private Func createCreator( Func creator, Action initializer ) - { - if (initializer == null) - return creator; + { + if (initializer == null) + return creator; - return () => { - var ret = creator (); - initializer (ret); + return () => + { + var ret = creator(); + initializer(ret); - return ret; - }; - } + return ret; + }; + } - #endregion - - #region Protected Methods - - protected override WebSocketBehavior CreateSession () - { - return _creator (); + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Server/WebSocketServiceManager.cs b/src/WebSocket/WebSocketCore/Server/WebSocketServiceManager.cs index 88eaa8924..b19e28ac1 100644 --- a/src/WebSocket/WebSocketCore/Server/WebSocketServiceManager.cs +++ b/src/WebSocket/WebSocketCore/Server/WebSocketServiceManager.cs @@ -1,4 +1,3 @@ -#region License /* * WebSocketServiceManager.cs * @@ -24,7 +23,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; using System.Collections; @@ -36,535 +34,556 @@ namespace WebSocketCore.Server { - /// - /// Provides the management function for the WebSocket services. - /// - /// - /// This class manages the WebSocket services provided by - /// the or . - /// - public class WebSocketServiceManager - { - #region Private Fields - - private volatile bool _clean; - private Dictionary _hosts; - private Logger _log; - private volatile ServerState _state; - private object _sync; - private TimeSpan _waitTime; - - #endregion - - #region Internal Constructors - - internal WebSocketServiceManager (Logger log) - { - _log = log; - - _clean = true; - _hosts = new Dictionary (); - _state = ServerState.Ready; - _sync = ((ICollection) _hosts).SyncRoot; - _waitTime = TimeSpan.FromSeconds (1); - } - - #endregion - - #region Public Properties - - /// - /// Gets the number of the WebSocket services. - /// - /// - /// An that represents the number of the services. - /// - public int Count { - get { - lock (_sync) - return _hosts.Count; - } - } - - /// - /// Gets the host instances for the WebSocket services. - /// - /// - /// - /// An IEnumerable<WebSocketServiceHost> instance. - /// - /// - /// It provides an enumerator which supports the iteration over - /// the collection of the host instances. - /// - /// - public IEnumerable Hosts { - get { - lock (_sync) - return _hosts.Values.ToList (); - } - } - /// - /// Gets the host instance for a WebSocket service with - /// the specified . + /// Provides the management function for the WebSocket services. /// /// - /// is converted to a URL-decoded string and - /// / is trimmed from the end of the converted string if any. + /// This class manages the WebSocket services provided by + /// the or . /// - /// - /// - /// A instance or - /// if not found. - /// - /// - /// That host instance provides the function to access - /// the information in the service. - /// - /// - /// - /// A that represents an absolute path to - /// the service to find. - /// - /// - /// is . - /// - /// - /// - /// is empty. - /// - /// - /// -or- - /// - /// - /// is not an absolute path. - /// - /// - /// -or- - /// - /// - /// includes either or both - /// query and fragment components. - /// - /// - public WebSocketServiceHostBase this[string path] { - get { - if (path == null) - throw new ArgumentNullException ("path"); - - if (path.Length == 0) - throw new ArgumentException ("An empty string.", "path"); - - if (path[0] != '/') - throw new ArgumentException ("Not an absolute path.", "path"); - - if (path.IndexOfAny (new[] { '?', '#' }) > -1) { - var msg = "It includes either or both query and fragment components."; - throw new ArgumentException (msg, "path"); - } - - WebSocketServiceHostBase host; - InternalTryGetServiceHost (path, out host); + public class WebSocketServiceManager + { + private volatile bool _clean; + private Dictionary _hosts; + private Logger _log; + private volatile ServerState _state; + private object _sync; + private TimeSpan _waitTime; + + internal WebSocketServiceManager(Logger log) + { + _log = log; - return host; - } - } + _clean = true; + _hosts = new Dictionary(); + _state = ServerState.Ready; + _sync = ((ICollection)_hosts).SyncRoot; + _waitTime = TimeSpan.FromSeconds(1); + } - /// - /// Gets or sets a value indicating whether the inactive sessions in - /// the WebSocket services are cleaned up periodically. - /// - /// - /// The set operation does nothing if the server has already started or - /// it is shutting down. - /// - /// - /// true if the inactive sessions are cleaned up every 60 seconds; - /// otherwise, false. - /// - public bool KeepClean { - get { - return _clean; - } - - set { - string msg; - if (!canSet (out msg)) { - _log.Warn (msg); - return; + /// + /// Gets the number of the WebSocket services. + /// + /// + /// An that represents the number of the services. + /// + public int Count + { + get + { + lock (_sync) + return _hosts.Count; + } } - lock (_sync) { - if (!canSet (out msg)) { - _log.Warn (msg); - return; - } + /// + /// Gets the host instances for the WebSocket services. + /// + /// + /// + /// An IEnumerable<WebSocketServiceHost> instance. + /// + /// + /// It provides an enumerator which supports the iteration over + /// the collection of the host instances. + /// + /// + public IEnumerable Hosts + { + get + { + lock (_sync) + return _hosts.Values.ToList(); + } + } - foreach (var host in _hosts.Values) - host.KeepClean = value; + /// + /// Gets the host instance for a WebSocket service with + /// the specified . + /// + /// + /// is converted to a URL-decoded string and + /// / is trimmed from the end of the converted string if any. + /// + /// + /// + /// A instance or + /// if not found. + /// + /// + /// That host instance provides the function to access + /// the information in the service. + /// + /// + /// + /// A that represents an absolute path to + /// the service to find. + /// + /// + /// is . + /// + /// + /// + /// is empty. + /// + /// + /// -or- + /// + /// + /// is not an absolute path. + /// + /// + /// -or- + /// + /// + /// includes either or both + /// query and fragment components. + /// + /// + public WebSocketServiceHostBase this[string path] + { + get + { + if (path == null) + throw new ArgumentNullException("path"); - _clean = value; - } - } - } + if (path.Length == 0) + throw new ArgumentException("An empty string.", "path"); - /// - /// Gets the paths for the WebSocket services. - /// - /// - /// - /// An IEnumerable<string> instance. - /// - /// - /// It provides an enumerator which supports the iteration over - /// the collection of the paths. - /// - /// - public IEnumerable Paths { - get { - lock (_sync) - return _hosts.Keys.ToList (); - } - } + if (path[0] != '/') + throw new ArgumentException("Not an absolute path.", "path"); - /// - /// Gets the total number of the sessions in the WebSocket services. - /// - /// - /// An that represents the total number of - /// the sessions in the services. - /// - [Obsolete ("This property will be removed.")] - public int SessionCount { - get { - var cnt = 0; - foreach (var host in Hosts) { - if (_state != ServerState.Start) - break; - - cnt += host.Sessions.Count; - } + if (path.IndexOfAny(new[] { '?', '#' }) > -1) + { + var msg = "It includes either or both query and fragment components."; + throw new ArgumentException(msg, "path"); + } - return cnt; - } - } + WebSocketServiceHostBase host; + InternalTryGetServiceHost(path, out host); - /// - /// Gets or sets the time to wait for the response to the WebSocket Ping or - /// Close. - /// - /// - /// The set operation does nothing if the server has already started or - /// it is shutting down. - /// - /// - /// A to wait for the response. - /// - /// - /// The value specified for a set operation is zero or less. - /// - public TimeSpan WaitTime { - get { - return _waitTime; - } - - set { - if (value <= TimeSpan.Zero) - throw new ArgumentOutOfRangeException ("value", "Zero or less."); - - string msg; - if (!canSet (out msg)) { - _log.Warn (msg); - return; + return host; + } } - lock (_sync) { - if (!canSet (out msg)) { - _log.Warn (msg); - return; - } - - foreach (var host in _hosts.Values) - host.WaitTime = value; + /// + /// Gets or sets a value indicating whether the inactive sessions in + /// the WebSocket services are cleaned up periodically. + /// + /// + /// The set operation does nothing if the server has already started or + /// it is shutting down. + /// + /// + /// true if the inactive sessions are cleaned up every 60 seconds; + /// otherwise, false. + /// + public bool KeepClean + { + get + { + return _clean; + } - _waitTime = value; + set + { + string msg; + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } + + lock (_sync) + { + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } + + foreach (var host in _hosts.Values) + host.KeepClean = value; + + _clean = value; + } + } } - } - } - - #endregion - #region Private Methods + /// + /// Gets the paths for the WebSocket services. + /// + /// + /// + /// An IEnumerable<string> instance. + /// + /// + /// It provides an enumerator which supports the iteration over + /// the collection of the paths. + /// + /// + public IEnumerable Paths + { + get + { + lock (_sync) + return _hosts.Keys.ToList(); + } + } - private void broadcast (Opcode opcode, byte[] data, Action completed) - { - var cache = new Dictionary (); + /// + /// Gets the total number of the sessions in the WebSocket services. + /// + /// + /// An that represents the total number of + /// the sessions in the services. + /// + [Obsolete("This property will be removed.")] + public int SessionCount + { + get + { + var cnt = 0; + foreach (var host in Hosts) + { + if (_state != ServerState.Start) + break; - try { - foreach (var host in Hosts) { - if (_state != ServerState.Start) { - _log.Error ("The server is shutting down."); - break; - } + cnt += host.Sessions.Count; + } - host.Sessions.Broadcast (opcode, data, cache); + return cnt; + } } - if (completed != null) - completed (); - } - catch (Exception ex) { - _log.Error (ex.Message); - _log.Debug (ex.ToString ()); - } - finally { - cache.Clear (); - } - } - - private void broadcast (Opcode opcode, Stream stream, Action completed) - { - var cache = new Dictionary (); - - try { - foreach (var host in Hosts) { - if (_state != ServerState.Start) { - _log.Error ("The server is shutting down."); - break; - } + /// + /// Gets or sets the time to wait for the response to the WebSocket Ping or + /// Close. + /// + /// + /// The set operation does nothing if the server has already started or + /// it is shutting down. + /// + /// + /// A to wait for the response. + /// + /// + /// The value specified for a set operation is zero or less. + /// + public TimeSpan WaitTime + { + get + { + return _waitTime; + } - host.Sessions.Broadcast (opcode, stream, cache); + set + { + if (value <= TimeSpan.Zero) + throw new ArgumentOutOfRangeException("value", "Zero or less."); + + string msg; + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } + + lock (_sync) + { + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } + + foreach (var host in _hosts.Values) + host.WaitTime = value; + + _waitTime = value; + } + } } - if (completed != null) - completed (); - } - catch (Exception ex) { - _log.Error (ex.Message); - _log.Debug (ex.ToString ()); - } - finally { - foreach (var cached in cache.Values) - cached.Dispose (); - - cache.Clear (); - } - } + private void broadcast(Opcode opcode, byte[] data, Action completed) + { + var cache = new Dictionary(); - private void broadcastAsync (Opcode opcode, byte[] data, Action completed) - { - ThreadPool.QueueUserWorkItem ( - state => broadcast (opcode, data, completed) - ); - } + try + { + foreach (var host in Hosts) + { + if (_state != ServerState.Start) + { + _log.Error("The server is shutting down."); + break; + } + + host.Sessions.Broadcast(opcode, data, cache); + } + + if (completed != null) + completed(); + } + catch (Exception ex) + { + _log.Error(ex.Message); + _log.Debug(ex.ToString()); + } + finally + { + cache.Clear(); + } + } - private void broadcastAsync (Opcode opcode, Stream stream, Action completed) - { - ThreadPool.QueueUserWorkItem ( - state => broadcast (opcode, stream, completed) - ); - } + private void broadcast(Opcode opcode, Stream stream, Action completed) + { + var cache = new Dictionary(); - private Dictionary> broadping ( - byte[] frameAsBytes, TimeSpan timeout - ) - { - var ret = new Dictionary> (); + try + { + foreach (var host in Hosts) + { + if (_state != ServerState.Start) + { + _log.Error("The server is shutting down."); + break; + } + + host.Sessions.Broadcast(opcode, stream, cache); + } + + if (completed != null) + completed(); + } + catch (Exception ex) + { + _log.Error(ex.Message); + _log.Debug(ex.ToString()); + } + finally + { + foreach (var cached in cache.Values) + cached.Dispose(); - foreach (var host in Hosts) { - if (_state != ServerState.Start) { - _log.Error ("The server is shutting down."); - break; + cache.Clear(); + } } - var res = host.Sessions.Broadping (frameAsBytes, timeout); - ret.Add (host.Path, res); - } - - return ret; - } + private void broadcastAsync(Opcode opcode, byte[] data, Action completed) + { + ThreadPool.QueueUserWorkItem( + state => broadcast(opcode, data, completed) + ); + } - private bool canSet (out string message) - { - message = null; + private void broadcastAsync(Opcode opcode, Stream stream, Action completed) + { + ThreadPool.QueueUserWorkItem( + state => broadcast(opcode, stream, completed) + ); + } - if (_state == ServerState.Start) { - message = "The server has already started."; - return false; - } + private Dictionary> broadping( + byte[] frameAsBytes, TimeSpan timeout + ) + { + var ret = new Dictionary>(); - if (_state == ServerState.ShuttingDown) { - message = "The server is shutting down."; - return false; - } + foreach (var host in Hosts) + { + if (_state != ServerState.Start) + { + _log.Error("The server is shutting down."); + break; + } + + var res = host.Sessions.Broadping(frameAsBytes, timeout); + ret.Add(host.Path, res); + } - return true; - } + return ret; + } - #endregion + private bool canSet(out string message) + { + message = null; - #region Internal Methods + if (_state == ServerState.Start) + { + message = "The server has already started."; + return false; + } - internal void Add (string path, Func creator) - where TBehavior : WebSocketBehavior - { - path = HttpUtility.UrlDecode (path).TrimSlashFromEnd (); + if (_state == ServerState.ShuttingDown) + { + message = "The server is shutting down."; + return false; + } - lock (_sync) { - WebSocketServiceHostBase host; - if (_hosts.TryGetValue (path, out host)) - throw new ArgumentException ("Already in use.", "path"); + return true; + } - host = new WebSocketServiceHost ( - path, creator, null, _log - ); + internal void Add(string path, Func creator) + where TBehavior : WebSocketBehavior + { + path = HttpUtility.UrlDecode(path).TrimSlashFromEnd(); - if (!_clean) - host.KeepClean = false; + lock (_sync) + { + WebSocketServiceHostBase host; + if (_hosts.TryGetValue(path, out host)) + throw new ArgumentException("Already in use.", "path"); - if (_waitTime != host.WaitTime) - host.WaitTime = _waitTime; + host = new WebSocketServiceHost( + path, creator, null, _log + ); - if (_state == ServerState.Start) - host.Start (); + if (!_clean) + host.KeepClean = false; - _hosts.Add (path, host); - } - } + if (_waitTime != host.WaitTime) + host.WaitTime = _waitTime; - internal bool InternalTryGetServiceHost ( - string path, out WebSocketServiceHostBase host - ) - { - path = HttpUtility.UrlDecode (path).TrimSlashFromEnd (); + if (_state == ServerState.Start) + host.Start(); - lock (_sync) - return _hosts.TryGetValue (path, out host); - } + _hosts.Add(path, host); + } + } - internal void Start () - { - lock (_sync) { - foreach (var host in _hosts.Values) - host.Start (); + internal bool InternalTryGetServiceHost( + string path, out WebSocketServiceHostBase host + ) + { + path = HttpUtility.UrlDecode(path).TrimSlashFromEnd(); - _state = ServerState.Start; - } - } + lock (_sync) + return _hosts.TryGetValue(path, out host); + } - internal void Stop (ushort code, string reason) - { - lock (_sync) { - _state = ServerState.ShuttingDown; + internal void Start() + { + lock (_sync) + { + foreach (var host in _hosts.Values) + host.Start(); - foreach (var host in _hosts.Values) - host.Stop (code, reason); + _state = ServerState.Start; + } + } - _state = ServerState.Stop; - } - } + internal void Stop(ushort code, string reason) + { + lock (_sync) + { + _state = ServerState.ShuttingDown; - #endregion + foreach (var host in _hosts.Values) + host.Stop(code, reason); - #region Public Methods + _state = ServerState.Stop; + } + } - /// - /// Adds a WebSocket service with the specified behavior, - /// , and . - /// - /// - /// is converted to a URL-decoded string and - /// / is trimmed from the end of the converted string if any. - /// - /// - /// A that represents an absolute path to - /// the service to add. - /// - /// - /// - /// An Action<TBehavior> delegate or - /// if not needed. - /// - /// - /// That delegate invokes the method called for initializing - /// a new session instance for the service. - /// - /// - /// - /// The type of the behavior for the service. It must inherit - /// the class and it must have - /// a public parameterless constructor. - /// - /// - /// is . - /// - /// - /// - /// is empty. - /// - /// - /// -or- - /// - /// - /// is not an absolute path. - /// - /// - /// -or- - /// - /// - /// includes either or both - /// query and fragment components. - /// - /// - /// -or- - /// - /// - /// is already in use. - /// - /// - public void AddService ( - string path, Action initializer - ) - where TBehavior : WebSocketBehavior, new () - { - if (path == null) - throw new ArgumentNullException ("path"); + /// + /// Adds a WebSocket service with the specified behavior, + /// , and . + /// + /// + /// is converted to a URL-decoded string and + /// / is trimmed from the end of the converted string if any. + /// + /// + /// A that represents an absolute path to + /// the service to add. + /// + /// + /// + /// An Action<TBehavior> delegate or + /// if not needed. + /// + /// + /// That delegate invokes the method called for initializing + /// a new session instance for the service. + /// + /// + /// + /// The type of the behavior for the service. It must inherit + /// the class and it must have + /// a public parameterless constructor. + /// + /// + /// is . + /// + /// + /// + /// is empty. + /// + /// + /// -or- + /// + /// + /// is not an absolute path. + /// + /// + /// -or- + /// + /// + /// includes either or both + /// query and fragment components. + /// + /// + /// -or- + /// + /// + /// is already in use. + /// + /// + public void AddService( + string path, Action initializer + ) + where TBehavior : WebSocketBehavior, new() + { + if (path == null) + throw new ArgumentNullException("path"); - if (path.Length == 0) - throw new ArgumentException ("An empty string.", "path"); + if (path.Length == 0) + throw new ArgumentException("An empty string.", "path"); - if (path[0] != '/') - throw new ArgumentException ("Not an absolute path.", "path"); + if (path[0] != '/') + throw new ArgumentException("Not an absolute path.", "path"); - if (path.IndexOfAny (new[] { '?', '#' }) > -1) { - var msg = "It includes either or both query and fragment components."; - throw new ArgumentException (msg, "path"); - } + if (path.IndexOfAny(new[] { '?', '#' }) > -1) + { + var msg = "It includes either or both query and fragment components."; + throw new ArgumentException(msg, "path"); + } - path = HttpUtility.UrlDecode (path).TrimSlashFromEnd (); + path = HttpUtility.UrlDecode(path).TrimSlashFromEnd(); - lock (_sync) { - WebSocketServiceHostBase host; - if (_hosts.TryGetValue (path, out host)) - throw new ArgumentException ("Already in use.", "path"); + lock (_sync) + { + WebSocketServiceHostBase host; + if (_hosts.TryGetValue(path, out host)) + throw new ArgumentException("Already in use.", "path"); - host = new WebSocketServiceHost ( - path, () => new TBehavior (), initializer, _log - ); + host = new WebSocketServiceHost( + path, () => new TBehavior(), initializer, _log + ); - if (!_clean) - host.KeepClean = false; + if (!_clean) + host.KeepClean = false; - if (_waitTime != host.WaitTime) - host.WaitTime = _waitTime; + if (_waitTime != host.WaitTime) + host.WaitTime = _waitTime; - if (_state == ServerState.Start) - host.Start (); + if (_state == ServerState.Start) + host.Start(); - _hosts.Add (path, host); - } - } + _hosts.Add(path, host); + } + } public void AddService( string path, WebSocketBehavior webSocketBehavior) @@ -621,494 +640,512 @@ public void AddService( /// /// is . /// - [Obsolete ("This method will be removed.")] - public void Broadcast (byte[] data) - { - if (_state != ServerState.Start) { - var msg = "The current state of the manager is not Start."; - throw new InvalidOperationException (msg); - } - - if (data == null) - throw new ArgumentNullException ("data"); - - if (data.LongLength <= WebSocket.FragmentLength) - broadcast (Opcode.Binary, data, null); - else - broadcast (Opcode.Binary, new MemoryStream (data), null); - } + [Obsolete("This method will be removed.")] + public void Broadcast(byte[] data) + { + if (_state != ServerState.Start) + { + var msg = "The current state of the manager is not Start."; + throw new InvalidOperationException(msg); + } - /// - /// Sends to every client in the WebSocket services. - /// - /// - /// A that represents the text data to send. - /// - /// - /// The current state of the manager is not Start. - /// - /// - /// is . - /// - /// - /// could not be UTF-8-encoded. - /// - [Obsolete ("This method will be removed.")] - public void Broadcast (string data) - { - if (_state != ServerState.Start) { - var msg = "The current state of the manager is not Start."; - throw new InvalidOperationException (msg); - } - - if (data == null) - throw new ArgumentNullException ("data"); - - byte[] bytes; - if (!data.TryGetUTF8EncodedBytes (out bytes)) { - var msg = "It could not be UTF-8-encoded."; - throw new ArgumentException (msg, "data"); - } - - if (bytes.LongLength <= WebSocket.FragmentLength) - broadcast (Opcode.Text, bytes, null); - else - broadcast (Opcode.Text, new MemoryStream (bytes), null); - } + if (data == null) + throw new ArgumentNullException("data"); - /// - /// Sends asynchronously to every client in - /// the WebSocket services. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// An array of that represents the binary data to send. - /// - /// - /// - /// An delegate or - /// if not needed. - /// - /// - /// The delegate invokes the method called when the send is complete. - /// - /// - /// - /// The current state of the manager is not Start. - /// - /// - /// is . - /// - [Obsolete ("This method will be removed.")] - public void BroadcastAsync (byte[] data, Action completed) - { - if (_state != ServerState.Start) { - var msg = "The current state of the manager is not Start."; - throw new InvalidOperationException (msg); - } - - if (data == null) - throw new ArgumentNullException ("data"); - - if (data.LongLength <= WebSocket.FragmentLength) - broadcastAsync (Opcode.Binary, data, completed); - else - broadcastAsync (Opcode.Binary, new MemoryStream (data), completed); - } + if (data.LongLength <= WebSocket.FragmentLength) + broadcast(Opcode.Binary, data, null); + else + broadcast(Opcode.Binary, new MemoryStream(data), null); + } - /// - /// Sends asynchronously to every client in - /// the WebSocket services. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// A that represents the text data to send. - /// - /// - /// - /// An delegate or - /// if not needed. - /// - /// - /// The delegate invokes the method called when the send is complete. - /// - /// - /// - /// The current state of the manager is not Start. - /// - /// - /// is . - /// - /// - /// could not be UTF-8-encoded. - /// - [Obsolete ("This method will be removed.")] - public void BroadcastAsync (string data, Action completed) - { - if (_state != ServerState.Start) { - var msg = "The current state of the manager is not Start."; - throw new InvalidOperationException (msg); - } - - if (data == null) - throw new ArgumentNullException ("data"); - - byte[] bytes; - if (!data.TryGetUTF8EncodedBytes (out bytes)) { - var msg = "It could not be UTF-8-encoded."; - throw new ArgumentException (msg, "data"); - } - - if (bytes.LongLength <= WebSocket.FragmentLength) - broadcastAsync (Opcode.Text, bytes, completed); - else - broadcastAsync (Opcode.Text, new MemoryStream (bytes), completed); - } + /// + /// Sends to every client in the WebSocket services. + /// + /// + /// A that represents the text data to send. + /// + /// + /// The current state of the manager is not Start. + /// + /// + /// is . + /// + /// + /// could not be UTF-8-encoded. + /// + [Obsolete("This method will be removed.")] + public void Broadcast(string data) + { + if (_state != ServerState.Start) + { + var msg = "The current state of the manager is not Start."; + throw new InvalidOperationException(msg); + } - /// - /// Sends the data from asynchronously to - /// every client in the WebSocket services. - /// - /// - /// - /// The data is sent as the binary data. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// - /// A instance from which to read the data to send. - /// - /// - /// An that specifies the number of bytes to send. - /// - /// - /// - /// An delegate or - /// if not needed. - /// - /// - /// The delegate invokes the method called when the send is complete. - /// - /// - /// - /// The current state of the manager is not Start. - /// - /// - /// is . - /// - /// - /// - /// cannot be read. - /// - /// - /// -or- - /// - /// - /// is less than 1. - /// - /// - /// -or- - /// - /// - /// No data could be read from . - /// - /// - [Obsolete ("This method will be removed.")] - public void BroadcastAsync (Stream stream, int length, Action completed) - { - if (_state != ServerState.Start) { - var msg = "The current state of the manager is not Start."; - throw new InvalidOperationException (msg); - } - - if (stream == null) - throw new ArgumentNullException ("stream"); - - if (!stream.CanRead) { - var msg = "It cannot be read."; - throw new ArgumentException (msg, "stream"); - } - - if (length < 1) { - var msg = "Less than 1."; - throw new ArgumentException (msg, "length"); - } - - var bytes = stream.ReadBytes (length); - - var len = bytes.Length; - if (len == 0) { - var msg = "No data could be read from it."; - throw new ArgumentException (msg, "stream"); - } - - if (len < length) { - _log.Warn ( - String.Format ( - "Only {0} byte(s) of data could be read from the stream.", - len - ) - ); - } - - if (len <= WebSocket.FragmentLength) - broadcastAsync (Opcode.Binary, bytes, completed); - else - broadcastAsync (Opcode.Binary, new MemoryStream (bytes), completed); - } + if (data == null) + throw new ArgumentNullException("data"); - /// - /// Sends a ping to every client in the WebSocket services. - /// - /// - /// - /// A Dictionary<string, Dictionary<string, bool>>. - /// - /// - /// It represents a collection of pairs of a service path and another - /// collection of pairs of a session ID and a value indicating whether - /// a pong has been received from the client within a time. - /// - /// - /// - /// The current state of the manager is not Start. - /// - [Obsolete ("This method will be removed.")] - public Dictionary> Broadping () - { - if (_state != ServerState.Start) { - var msg = "The current state of the manager is not Start."; - throw new InvalidOperationException (msg); - } + byte[] bytes; + if (!data.TryGetUTF8EncodedBytes(out bytes)) + { + var msg = "It could not be UTF-8-encoded."; + throw new ArgumentException(msg, "data"); + } - return broadping (WebSocketFrame.EmptyPingBytes, _waitTime); - } + if (bytes.LongLength <= WebSocket.FragmentLength) + broadcast(Opcode.Text, bytes, null); + else + broadcast(Opcode.Text, new MemoryStream(bytes), null); + } - /// - /// Sends a ping with to every client in - /// the WebSocket services. - /// - /// - /// - /// A Dictionary<string, Dictionary<string, bool>>. - /// - /// - /// It represents a collection of pairs of a service path and another - /// collection of pairs of a session ID and a value indicating whether - /// a pong has been received from the client within a time. - /// - /// - /// - /// - /// A that represents the message to send. - /// - /// - /// The size must be 125 bytes or less in UTF-8. - /// - /// - /// - /// The current state of the manager is not Start. - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// The size of is greater than 125 bytes. - /// - [Obsolete ("This method will be removed.")] - public Dictionary> Broadping (string message) - { - if (_state != ServerState.Start) { - var msg = "The current state of the manager is not Start."; - throw new InvalidOperationException (msg); - } - - if (message.IsNullOrEmpty ()) - return broadping (WebSocketFrame.EmptyPingBytes, _waitTime); - - byte[] bytes; - if (!message.TryGetUTF8EncodedBytes (out bytes)) { - var msg = "It could not be UTF-8-encoded."; - throw new ArgumentException (msg, "message"); - } - - if (bytes.Length > 125) { - var msg = "Its size is greater than 125 bytes."; - throw new ArgumentOutOfRangeException ("message", msg); - } - - var frame = WebSocketFrame.CreatePingFrame (bytes, false); - return broadping (frame.ToArray (), _waitTime); - } + /// + /// Sends asynchronously to every client in + /// the WebSocket services. + /// + /// + /// This method does not wait for the send to be complete. + /// + /// + /// An array of that represents the binary data to send. + /// + /// + /// + /// An delegate or + /// if not needed. + /// + /// + /// The delegate invokes the method called when the send is complete. + /// + /// + /// + /// The current state of the manager is not Start. + /// + /// + /// is . + /// + [Obsolete("This method will be removed.")] + public void BroadcastAsync(byte[] data, Action completed) + { + if (_state != ServerState.Start) + { + var msg = "The current state of the manager is not Start."; + throw new InvalidOperationException(msg); + } - /// - /// Removes all WebSocket services managed by the manager. - /// - /// - /// A service is stopped with close status 1001 (going away) - /// if it has already started. - /// - public void Clear () - { - List hosts = null; + if (data == null) + throw new ArgumentNullException("data"); - lock (_sync) { - hosts = _hosts.Values.ToList (); - _hosts.Clear (); - } + if (data.LongLength <= WebSocket.FragmentLength) + broadcastAsync(Opcode.Binary, data, completed); + else + broadcastAsync(Opcode.Binary, new MemoryStream(data), completed); + } - foreach (var host in hosts) { - if (host.State == ServerState.Start) - host.Stop (1001, String.Empty); - } - } + /// + /// Sends asynchronously to every client in + /// the WebSocket services. + /// + /// + /// This method does not wait for the send to be complete. + /// + /// + /// A that represents the text data to send. + /// + /// + /// + /// An delegate or + /// if not needed. + /// + /// + /// The delegate invokes the method called when the send is complete. + /// + /// + /// + /// The current state of the manager is not Start. + /// + /// + /// is . + /// + /// + /// could not be UTF-8-encoded. + /// + [Obsolete("This method will be removed.")] + public void BroadcastAsync(string data, Action completed) + { + if (_state != ServerState.Start) + { + var msg = "The current state of the manager is not Start."; + throw new InvalidOperationException(msg); + } - /// - /// Removes a WebSocket service with the specified . - /// - /// - /// - /// is converted to a URL-decoded string and - /// / is trimmed from the end of the converted string if any. - /// - /// - /// The service is stopped with close status 1001 (going away) - /// if it has already started. - /// - /// - /// - /// true if the service is successfully found and removed; - /// otherwise, false. - /// - /// - /// A that represents an absolute path to - /// the service to remove. - /// - /// - /// is . - /// - /// - /// - /// is empty. - /// - /// - /// -or- - /// - /// - /// is not an absolute path. - /// - /// - /// -or- - /// - /// - /// includes either or both - /// query and fragment components. - /// - /// - public bool RemoveService (string path) - { - if (path == null) - throw new ArgumentNullException ("path"); + if (data == null) + throw new ArgumentNullException("data"); - if (path.Length == 0) - throw new ArgumentException ("An empty string.", "path"); + byte[] bytes; + if (!data.TryGetUTF8EncodedBytes(out bytes)) + { + var msg = "It could not be UTF-8-encoded."; + throw new ArgumentException(msg, "data"); + } - if (path[0] != '/') - throw new ArgumentException ("Not an absolute path.", "path"); + if (bytes.LongLength <= WebSocket.FragmentLength) + broadcastAsync(Opcode.Text, bytes, completed); + else + broadcastAsync(Opcode.Text, new MemoryStream(bytes), completed); + } - if (path.IndexOfAny (new[] { '?', '#' }) > -1) { - var msg = "It includes either or both query and fragment components."; - throw new ArgumentException (msg, "path"); - } + /// + /// Sends the data from asynchronously to + /// every client in the WebSocket services. + /// + /// + /// + /// The data is sent as the binary data. + /// + /// + /// This method does not wait for the send to be complete. + /// + /// + /// + /// A instance from which to read the data to send. + /// + /// + /// An that specifies the number of bytes to send. + /// + /// + /// + /// An delegate or + /// if not needed. + /// + /// + /// The delegate invokes the method called when the send is complete. + /// + /// + /// + /// The current state of the manager is not Start. + /// + /// + /// is . + /// + /// + /// + /// cannot be read. + /// + /// + /// -or- + /// + /// + /// is less than 1. + /// + /// + /// -or- + /// + /// + /// No data could be read from . + /// + /// + [Obsolete("This method will be removed.")] + public void BroadcastAsync(Stream stream, int length, Action completed) + { + if (_state != ServerState.Start) + { + var msg = "The current state of the manager is not Start."; + throw new InvalidOperationException(msg); + } - path = HttpUtility.UrlDecode (path).TrimSlashFromEnd (); + if (stream == null) + throw new ArgumentNullException("stream"); - WebSocketServiceHostBase host; - lock (_sync) { - if (!_hosts.TryGetValue (path, out host)) - return false; + if (!stream.CanRead) + { + var msg = "It cannot be read."; + throw new ArgumentException(msg, "stream"); + } - _hosts.Remove (path); - } + if (length < 1) + { + var msg = "Less than 1."; + throw new ArgumentException(msg, "length"); + } - if (host.State == ServerState.Start) - host.Stop (1001, String.Empty); + var bytes = stream.ReadBytes(length); - return true; - } + var len = bytes.Length; + if (len == 0) + { + var msg = "No data could be read from it."; + throw new ArgumentException(msg, "stream"); + } - /// - /// Tries to get the host instance for a WebSocket service with - /// the specified . - /// - /// - /// is converted to a URL-decoded string and - /// / is trimmed from the end of the converted string if any. - /// - /// - /// true if the service is successfully found; - /// otherwise, false. - /// - /// - /// A that represents an absolute path to - /// the service to find. - /// - /// - /// - /// When this method returns, a - /// instance or if not found. - /// - /// - /// That host instance provides the function to access - /// the information in the service. - /// - /// - /// - /// is . - /// - /// - /// - /// is empty. - /// - /// - /// -or- - /// - /// - /// is not an absolute path. - /// - /// - /// -or- - /// - /// - /// includes either or both - /// query and fragment components. - /// - /// - public bool TryGetServiceHost (string path, out WebSocketServiceHostBase host) - { - if (path == null) - throw new ArgumentNullException ("path"); + if (len < length) + { + _log.Warn( + String.Format( + "Only {0} byte(s) of data could be read from the stream.", + len + ) + ); + } - if (path.Length == 0) - throw new ArgumentException ("An empty string.", "path"); + if (len <= WebSocket.FragmentLength) + broadcastAsync(Opcode.Binary, bytes, completed); + else + broadcastAsync(Opcode.Binary, new MemoryStream(bytes), completed); + } - if (path[0] != '/') - throw new ArgumentException ("Not an absolute path.", "path"); + /// + /// Sends a ping to every client in the WebSocket services. + /// + /// + /// + /// A Dictionary<string, Dictionary<string, bool>>. + /// + /// + /// It represents a collection of pairs of a service path and another + /// collection of pairs of a session ID and a value indicating whether + /// a pong has been received from the client within a time. + /// + /// + /// + /// The current state of the manager is not Start. + /// + [Obsolete("This method will be removed.")] + public Dictionary> Broadping() + { + if (_state != ServerState.Start) + { + var msg = "The current state of the manager is not Start."; + throw new InvalidOperationException(msg); + } - if (path.IndexOfAny (new[] { '?', '#' }) > -1) { - var msg = "It includes either or both query and fragment components."; - throw new ArgumentException (msg, "path"); - } + return broadping(WebSocketFrame.EmptyPingBytes, _waitTime); + } - return InternalTryGetServiceHost (path, out host); - } + /// + /// Sends a ping with to every client in + /// the WebSocket services. + /// + /// + /// + /// A Dictionary<string, Dictionary<string, bool>>. + /// + /// + /// It represents a collection of pairs of a service path and another + /// collection of pairs of a session ID and a value indicating whether + /// a pong has been received from the client within a time. + /// + /// + /// + /// + /// A that represents the message to send. + /// + /// + /// The size must be 125 bytes or less in UTF-8. + /// + /// + /// + /// The current state of the manager is not Start. + /// + /// + /// could not be UTF-8-encoded. + /// + /// + /// The size of is greater than 125 bytes. + /// + [Obsolete("This method will be removed.")] + public Dictionary> Broadping(string message) + { + if (_state != ServerState.Start) + { + var msg = "The current state of the manager is not Start."; + throw new InvalidOperationException(msg); + } + + if (message.IsNullOrEmpty()) + return broadping(WebSocketFrame.EmptyPingBytes, _waitTime); + + byte[] bytes; + if (!message.TryGetUTF8EncodedBytes(out bytes)) + { + var msg = "It could not be UTF-8-encoded."; + throw new ArgumentException(msg, "message"); + } - #endregion - } -} + if (bytes.Length > 125) + { + var msg = "Its size is greater than 125 bytes."; + throw new ArgumentOutOfRangeException("message", msg); + } + + var frame = WebSocketFrame.CreatePingFrame(bytes, false); + return broadping(frame.ToArray(), _waitTime); + } + + /// + /// Removes all WebSocket services managed by the manager. + /// + /// + /// A service is stopped with close status 1001 (going away) + /// if it has already started. + /// + public void Clear() + { + List hosts = null; + + lock (_sync) + { + hosts = _hosts.Values.ToList(); + _hosts.Clear(); + } + + foreach (var host in hosts) + { + if (host.State == ServerState.Start) + host.Stop(1001, String.Empty); + } + } + + /// + /// Removes a WebSocket service with the specified . + /// + /// + /// + /// is converted to a URL-decoded string and + /// / is trimmed from the end of the converted string if any. + /// + /// + /// The service is stopped with close status 1001 (going away) + /// if it has already started. + /// + /// + /// + /// true if the service is successfully found and removed; + /// otherwise, false. + /// + /// + /// A that represents an absolute path to + /// the service to remove. + /// + /// + /// is . + /// + /// + /// + /// is empty. + /// + /// + /// -or- + /// + /// + /// is not an absolute path. + /// + /// + /// -or- + /// + /// + /// includes either or both + /// query and fragment components. + /// + /// + public bool RemoveService(string path) + { + if (path == null) + throw new ArgumentNullException("path"); + + if (path.Length == 0) + throw new ArgumentException("An empty string.", "path"); + + if (path[0] != '/') + throw new ArgumentException("Not an absolute path.", "path"); + + if (path.IndexOfAny(new[] { '?', '#' }) > -1) + { + var msg = "It includes either or both query and fragment components."; + throw new ArgumentException(msg, "path"); + } + + path = HttpUtility.UrlDecode(path).TrimSlashFromEnd(); + + WebSocketServiceHostBase host; + lock (_sync) + { + if (!_hosts.TryGetValue(path, out host)) + return false; + + _hosts.Remove(path); + } + + if (host.State == ServerState.Start) + host.Stop(1001, String.Empty); + + return true; + } + + /// + /// Tries to get the host instance for a WebSocket service with + /// the specified . + /// + /// + /// is converted to a URL-decoded string and + /// / is trimmed from the end of the converted string if any. + /// + /// + /// true if the service is successfully found; + /// otherwise, false. + /// + /// + /// A that represents an absolute path to + /// the service to find. + /// + /// + /// + /// When this method returns, a + /// instance or if not found. + /// + /// + /// That host instance provides the function to access + /// the information in the service. + /// + /// + /// + /// is . + /// + /// + /// + /// is empty. + /// + /// + /// -or- + /// + /// + /// is not an absolute path. + /// + /// + /// -or- + /// + /// + /// includes either or both + /// query and fragment components. + /// + /// + public bool TryGetServiceHost(string path, out WebSocketServiceHostBase host) + { + if (path == null) + throw new ArgumentNullException("path"); + + if (path.Length == 0) + throw new ArgumentException("An empty string.", "path"); + + if (path[0] != '/') + throw new ArgumentException("Not an absolute path.", "path"); + + if (path.IndexOfAny(new[] { '?', '#' }) > -1) + { + var msg = "It includes either or both query and fragment components."; + throw new ArgumentException(msg, "path"); + } + + return InternalTryGetServiceHost(path, out host); + } + } +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/Server/WebSocketSessionManager.cs b/src/WebSocket/WebSocketCore/Server/WebSocketSessionManager.cs index 2edf5490a..9e5d1cee0 100644 --- a/src/WebSocket/WebSocketCore/Server/WebSocketSessionManager.cs +++ b/src/WebSocket/WebSocketCore/Server/WebSocketSessionManager.cs @@ -1,4 +1,3 @@ -#region License /* * WebSocketSessionManager.cs * @@ -24,7 +23,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; using System.Collections; @@ -37,1659 +35,1723 @@ namespace WebSocketCore.Server { - /// - /// Provides the management function for the sessions in a WebSocket service. - /// - /// - /// This class manages the sessions in a WebSocket service provided by - /// the or . - /// - public class WebSocketSessionManager - { - #region Private Fields - - private volatile bool _clean; - private object _forSweep; - private Logger _log; - private Dictionary _sessions; - private volatile ServerState _state; - private volatile bool _sweeping; - private System.Timers.Timer _sweepTimer; - private object _sync; - private TimeSpan _waitTime; - - #endregion - - #region Internal Constructors - - internal WebSocketSessionManager (Logger log) - { - _log = log; - - _clean = true; - _forSweep = new object (); - _sessions = new Dictionary (); - _state = ServerState.Ready; - _sync = ((ICollection) _sessions).SyncRoot; - _waitTime = TimeSpan.FromSeconds (1); - - setSweepTimer (60000); - } - - #endregion - - #region Internal Properties - - internal ServerState State { - get { - return _state; - } - } - - #endregion - - #region Public Properties - /// - /// Gets the IDs for the active sessions in the WebSocket service. + /// Provides the management function for the sessions in a WebSocket service. /// - /// - /// - /// An IEnumerable<string> instance. - /// - /// - /// It provides an enumerator which supports the iteration over - /// the collection of the IDs for the active sessions. - /// - /// - public IEnumerable ActiveIDs { - get { - foreach (var res in broadping (WebSocketFrame.EmptyPingBytes)) { - if (res.Value) - yield return res.Key; + /// + /// This class manages the sessions in a WebSocket service provided by + /// the or . + /// + public class WebSocketSessionManager + { + private volatile bool _clean; + private object _forSweep; + private Logger _log; + private Dictionary _sessions; + private volatile ServerState _state; + private volatile bool _sweeping; + private System.Timers.Timer _sweepTimer; + private object _sync; + private TimeSpan _waitTime; + + internal WebSocketSessionManager(Logger log) + { + _log = log; + + _clean = true; + _forSweep = new object(); + _sessions = new Dictionary(); + _state = ServerState.Ready; + _sync = ((ICollection)_sessions).SyncRoot; + _waitTime = TimeSpan.FromSeconds(1); + + setSweepTimer(60000); } - } - } - - /// - /// Gets the number of the sessions in the WebSocket service. - /// - /// - /// An that represents the number of the sessions. - /// - public int Count { - get { - lock (_sync) - return _sessions.Count; - } - } - /// - /// Gets the IDs for the sessions in the WebSocket service. - /// - /// - /// - /// An IEnumerable<string> instance. - /// - /// - /// It provides an enumerator which supports the iteration over - /// the collection of the IDs for the sessions. - /// - /// - public IEnumerable IDs { - get { - if (_state != ServerState.Start) - return Enumerable.Empty (); - - lock (_sync) { - if (_state != ServerState.Start) - return Enumerable.Empty (); - - return _sessions.Keys.ToList (); + internal ServerState State + { + get + { + return _state; + } } - } - } - /// - /// Gets the IDs for the inactive sessions in the WebSocket service. - /// - /// - /// - /// An IEnumerable<string> instance. - /// - /// - /// It provides an enumerator which supports the iteration over - /// the collection of the IDs for the inactive sessions. - /// - /// - public IEnumerable InactiveIDs { - get { - foreach (var res in broadping (WebSocketFrame.EmptyPingBytes)) { - if (!res.Value) - yield return res.Key; + /// + /// Gets the IDs for the active sessions in the WebSocket service. + /// + /// + /// + /// An IEnumerable<string> instance. + /// + /// + /// It provides an enumerator which supports the iteration over + /// the collection of the IDs for the active sessions. + /// + /// + public IEnumerable ActiveIDs + { + get + { + foreach (var res in broadping(WebSocketFrame.EmptyPingBytes)) + { + if (res.Value) + yield return res.Key; + } + } } - } - } - - /// - /// Gets the session instance with . - /// - /// - /// - /// A instance or - /// if not found. - /// - /// - /// The session instance provides the function to access the information - /// in the session. - /// - /// - /// - /// A that represents the ID of the session to find. - /// - /// - /// is . - /// - /// - /// is an empty string. - /// - public IWebSocketSession this[string id] { - get { - if (id == null) - throw new ArgumentNullException ("id"); - - if (id.Length == 0) - throw new ArgumentException ("An empty string.", "id"); - - IWebSocketSession session; - tryGetSession (id, out session); - - return session; - } - } - /// - /// Gets or sets a value indicating whether the inactive sessions in - /// the WebSocket service are cleaned up periodically. - /// - /// - /// The set operation does nothing if the service has already started or - /// it is shutting down. - /// - /// - /// true if the inactive sessions are cleaned up every 60 seconds; - /// otherwise, false. - /// - public bool KeepClean { - get { - return _clean; - } - - set { - string msg; - if (!canSet (out msg)) { - _log.Warn (msg); - return; + /// + /// Gets the number of the sessions in the WebSocket service. + /// + /// + /// An that represents the number of the sessions. + /// + public int Count + { + get + { + lock (_sync) + return _sessions.Count; + } } - lock (_sync) { - if (!canSet (out msg)) { - _log.Warn (msg); - return; - } - - _clean = value; + /// + /// Gets the IDs for the sessions in the WebSocket service. + /// + /// + /// + /// An IEnumerable<string> instance. + /// + /// + /// It provides an enumerator which supports the iteration over + /// the collection of the IDs for the sessions. + /// + /// + public IEnumerable IDs + { + get + { + if (_state != ServerState.Start) + return Enumerable.Empty(); + + lock (_sync) + { + if (_state != ServerState.Start) + return Enumerable.Empty(); + + return _sessions.Keys.ToList(); + } + } } - } - } - /// - /// Gets the session instances in the WebSocket service. - /// - /// - /// - /// An IEnumerable<IWebSocketSession> instance. - /// - /// - /// It provides an enumerator which supports the iteration over - /// the collection of the session instances. - /// - /// - public IEnumerable Sessions { - get { - if (_state != ServerState.Start) - return Enumerable.Empty (); - - lock (_sync) { - if (_state != ServerState.Start) - return Enumerable.Empty (); - - return _sessions.Values.ToList (); + /// + /// Gets the IDs for the inactive sessions in the WebSocket service. + /// + /// + /// + /// An IEnumerable<string> instance. + /// + /// + /// It provides an enumerator which supports the iteration over + /// the collection of the IDs for the inactive sessions. + /// + /// + public IEnumerable InactiveIDs + { + get + { + foreach (var res in broadping(WebSocketFrame.EmptyPingBytes)) + { + if (!res.Value) + yield return res.Key; + } + } } - } - } - /// - /// Gets or sets the time to wait for the response to the WebSocket Ping or - /// Close. - /// - /// - /// The set operation does nothing if the service has already started or - /// it is shutting down. - /// - /// - /// A to wait for the response. - /// - /// - /// The value specified for a set operation is zero or less. - /// - public TimeSpan WaitTime { - get { - return _waitTime; - } - - set { - if (value <= TimeSpan.Zero) - throw new ArgumentOutOfRangeException ("value", "Zero or less."); - - string msg; - if (!canSet (out msg)) { - _log.Warn (msg); - return; + /// + /// Gets the session instance with . + /// + /// + /// + /// A instance or + /// if not found. + /// + /// + /// The session instance provides the function to access the information + /// in the session. + /// + /// + /// + /// A that represents the ID of the session to find. + /// + /// + /// is . + /// + /// + /// is an empty string. + /// + public IWebSocketSession this[string id] + { + get + { + if (id == null) + throw new ArgumentNullException("id"); + + if (id.Length == 0) + throw new ArgumentException("An empty string.", "id"); + + IWebSocketSession session; + tryGetSession(id, out session); + + return session; + } } - lock (_sync) { - if (!canSet (out msg)) { - _log.Warn (msg); - return; - } - - _waitTime = value; + /// + /// Gets or sets a value indicating whether the inactive sessions in + /// the WebSocket service are cleaned up periodically. + /// + /// + /// The set operation does nothing if the service has already started or + /// it is shutting down. + /// + /// + /// true if the inactive sessions are cleaned up every 60 seconds; + /// otherwise, false. + /// + public bool KeepClean + { + get + { + return _clean; + } + + set + { + string msg; + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } + + lock (_sync) + { + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } + + _clean = value; + } + } } - } - } - - #endregion - - #region Private Methods - - private void broadcast (Opcode opcode, byte[] data, Action completed) - { - var cache = new Dictionary (); - try { - foreach (var session in Sessions) { - if (_state != ServerState.Start) { - _log.Error ("The service is shutting down."); - break; - } - - session.Context.WebSocket.Send (opcode, data, cache); + /// + /// Gets the session instances in the WebSocket service. + /// + /// + /// + /// An IEnumerable<IWebSocketSession> instance. + /// + /// + /// It provides an enumerator which supports the iteration over + /// the collection of the session instances. + /// + /// + public IEnumerable Sessions + { + get + { + if (_state != ServerState.Start) + return Enumerable.Empty(); + + lock (_sync) + { + if (_state != ServerState.Start) + return Enumerable.Empty(); + + return _sessions.Values.ToList(); + } + } } - if (completed != null) - completed (); - } - catch (Exception ex) { - _log.Error (ex.Message); - _log.Debug (ex.ToString ()); - } - finally { - cache.Clear (); - } - } - - private void broadcast (Opcode opcode, Stream stream, Action completed) - { - var cache = new Dictionary (); - - try { - foreach (var session in Sessions) { - if (_state != ServerState.Start) { - _log.Error ("The service is shutting down."); - break; - } - - session.Context.WebSocket.Send (opcode, stream, cache); + /// + /// Gets or sets the time to wait for the response to the WebSocket Ping or + /// Close. + /// + /// + /// The set operation does nothing if the service has already started or + /// it is shutting down. + /// + /// + /// A to wait for the response. + /// + /// + /// The value specified for a set operation is zero or less. + /// + public TimeSpan WaitTime + { + get + { + return _waitTime; + } + + set + { + if (value <= TimeSpan.Zero) + throw new ArgumentOutOfRangeException("value", "Zero or less."); + + string msg; + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } + + lock (_sync) + { + if (!canSet(out msg)) + { + _log.Warn(msg); + return; + } + + _waitTime = value; + } + } } - if (completed != null) - completed (); - } - catch (Exception ex) { - _log.Error (ex.Message); - _log.Debug (ex.ToString ()); - } - finally { - foreach (var cached in cache.Values) - cached.Dispose (); - - cache.Clear (); - } - } - - private void broadcastAsync (Opcode opcode, byte[] data, Action completed) - { - ThreadPool.QueueUserWorkItem ( - state => broadcast (opcode, data, completed) - ); - } - - private void broadcastAsync (Opcode opcode, Stream stream, Action completed) - { - ThreadPool.QueueUserWorkItem ( - state => broadcast (opcode, stream, completed) - ); - } - - private Dictionary broadping (byte[] frameAsBytes) - { - var ret = new Dictionary (); - - foreach (var session in Sessions) { - if (_state != ServerState.Start) { - _log.Error ("The service is shutting down."); - break; + private void broadcast(Opcode opcode, byte[] data, Action completed) + { + var cache = new Dictionary(); + + try + { + foreach (var session in Sessions) + { + if (_state != ServerState.Start) + { + _log.Error("The service is shutting down."); + break; + } + + session.Context.WebSocket.Send(opcode, data, cache); + } + + if (completed != null) + completed(); + } + catch (Exception ex) + { + _log.Error(ex.Message); + _log.Debug(ex.ToString()); + } + finally + { + cache.Clear(); + } } - var res = session.Context.WebSocket.Ping (frameAsBytes, _waitTime); - ret.Add (session.ID, res); - } - - return ret; - } - - private bool canSet (out string message) - { - message = null; - - if (_state == ServerState.Start) { - message = "The service has already started."; - return false; - } - - if (_state == ServerState.ShuttingDown) { - message = "The service is shutting down."; - return false; - } - - return true; - } - - private static string createID () - { - return Guid.NewGuid ().ToString ("N"); - } - - private void setSweepTimer (double interval) - { - _sweepTimer = new System.Timers.Timer (interval); - _sweepTimer.Elapsed += (sender, e) => Sweep (); - } - - private void stop (PayloadData payloadData, bool send) - { - var bytes = send - ? WebSocketFrame.CreateCloseFrame (payloadData, false).ToArray () - : null; - - lock (_sync) { - _state = ServerState.ShuttingDown; - - _sweepTimer.Enabled = false; - foreach (var session in _sessions.Values.ToList ()) - session.Context.WebSocket.Close (payloadData, bytes); - - _state = ServerState.Stop; - } - } + private void broadcast(Opcode opcode, Stream stream, Action completed) + { + var cache = new Dictionary(); + + try + { + foreach (var session in Sessions) + { + if (_state != ServerState.Start) + { + _log.Error("The service is shutting down."); + break; + } + + session.Context.WebSocket.Send(opcode, stream, cache); + } + + if (completed != null) + completed(); + } + catch (Exception ex) + { + _log.Error(ex.Message); + _log.Debug(ex.ToString()); + } + finally + { + foreach (var cached in cache.Values) + cached.Dispose(); + + cache.Clear(); + } + } - private bool tryGetSession (string id, out IWebSocketSession session) - { - session = null; + private void broadcastAsync(Opcode opcode, byte[] data, Action completed) + { + ThreadPool.QueueUserWorkItem( + state => broadcast(opcode, data, completed) + ); + } - if (_state != ServerState.Start) - return false; + private void broadcastAsync(Opcode opcode, Stream stream, Action completed) + { + ThreadPool.QueueUserWorkItem( + state => broadcast(opcode, stream, completed) + ); + } - lock (_sync) { - if (_state != ServerState.Start) - return false; + private Dictionary broadping(byte[] frameAsBytes) + { + var ret = new Dictionary(); - return _sessions.TryGetValue (id, out session); - } - } + foreach (var session in Sessions) + { + if (_state != ServerState.Start) + { + _log.Error("The service is shutting down."); + break; + } - #endregion + var res = session.Context.WebSocket.Ping(frameAsBytes, _waitTime); + ret.Add(session.ID, res); + } - #region Internal Methods + return ret; + } - internal string Add (IWebSocketSession session) - { - lock (_sync) { - if (_state != ServerState.Start) - return null; + private bool canSet(out string message) + { + message = null; - var id = createID (); - _sessions.Add (id, session); + if (_state == ServerState.Start) + { + message = "The service has already started."; + return false; + } - return id; - } - } + if (_state == ServerState.ShuttingDown) + { + message = "The service is shutting down."; + return false; + } - internal void Broadcast ( - Opcode opcode, byte[] data, Dictionary cache - ) - { - foreach (var session in Sessions) { - if (_state != ServerState.Start) { - _log.Error ("The service is shutting down."); - break; + return true; } - session.Context.WebSocket.Send (opcode, data, cache); - } - } - - internal void Broadcast ( - Opcode opcode, Stream stream, Dictionary cache - ) - { - foreach (var session in Sessions) { - if (_state != ServerState.Start) { - _log.Error ("The service is shutting down."); - break; + private static string createID() + { + return Guid.NewGuid().ToString("N"); } - session.Context.WebSocket.Send (opcode, stream, cache); - } - } - - internal Dictionary Broadping ( - byte[] frameAsBytes, TimeSpan timeout - ) - { - var ret = new Dictionary (); - - foreach (var session in Sessions) { - if (_state != ServerState.Start) { - _log.Error ("The service is shutting down."); - break; + private void setSweepTimer(double interval) + { + _sweepTimer = new System.Timers.Timer(interval); + _sweepTimer.Elapsed += (sender, e) => Sweep(); } - var res = session.Context.WebSocket.Ping (frameAsBytes, timeout); - ret.Add (session.ID, res); - } - - return ret; - } - - internal bool Remove (string id) - { - lock (_sync) - return _sessions.Remove (id); - } - - internal void Start () - { - lock (_sync) { - _sweepTimer.Enabled = _clean; - _state = ServerState.Start; - } - } - - internal void Stop (ushort code, string reason) - { - if (code == 1005) { // == no status - stop (PayloadData.Empty, true); - return; - } + private void stop(PayloadData payloadData, bool send) + { + var bytes = send + ? WebSocketFrame.CreateCloseFrame(payloadData, false).ToArray() + : null; - stop (new PayloadData (code, reason), !code.IsReserved ()); - } + lock (_sync) + { + _state = ServerState.ShuttingDown; - #endregion + _sweepTimer.Enabled = false; + foreach (var session in _sessions.Values.ToList()) + session.Context.WebSocket.Close(payloadData, bytes); - #region Public Methods - - /// - /// Sends to every client in the WebSocket service. - /// - /// - /// An array of that represents the binary data to send. - /// - /// - /// The current state of the manager is not Start. - /// - /// - /// is . - /// - public void Broadcast (byte[] data) - { - if (_state != ServerState.Start) { - var msg = "The current state of the manager is not Start."; - throw new InvalidOperationException (msg); - } - - if (data == null) - throw new ArgumentNullException ("data"); - - if (data.LongLength <= WebSocket.FragmentLength) - broadcast (Opcode.Binary, data, null); - else - broadcast (Opcode.Binary, new MemoryStream (data), null); - } - - /// - /// Sends to every client in the WebSocket service. - /// - /// - /// A that represents the text data to send. - /// - /// - /// The current state of the manager is not Start. - /// - /// - /// is . - /// - /// - /// could not be UTF-8-encoded. - /// - public void Broadcast (string data) - { - if (_state != ServerState.Start) { - var msg = "The current state of the manager is not Start."; - throw new InvalidOperationException (msg); - } - - if (data == null) - throw new ArgumentNullException ("data"); - - byte[] bytes; - if (!data.TryGetUTF8EncodedBytes (out bytes)) { - var msg = "It could not be UTF-8-encoded."; - throw new ArgumentException (msg, "data"); - } - - if (bytes.LongLength <= WebSocket.FragmentLength) - broadcast (Opcode.Text, bytes, null); - else - broadcast (Opcode.Text, new MemoryStream (bytes), null); - } - - /// - /// Sends the data from to every client in - /// the WebSocket service. - /// - /// - /// The data is sent as the binary data. - /// - /// - /// A instance from which to read the data to send. - /// - /// - /// An that specifies the number of bytes to send. - /// - /// - /// The current state of the manager is not Start. - /// - /// - /// is . - /// - /// - /// - /// cannot be read. - /// - /// - /// -or- - /// - /// - /// is less than 1. - /// - /// - /// -or- - /// - /// - /// No data could be read from . - /// - /// - public void Broadcast (Stream stream, int length) - { - if (_state != ServerState.Start) { - var msg = "The current state of the manager is not Start."; - throw new InvalidOperationException (msg); - } - - if (stream == null) - throw new ArgumentNullException ("stream"); - - if (!stream.CanRead) { - var msg = "It cannot be read."; - throw new ArgumentException (msg, "stream"); - } - - if (length < 1) { - var msg = "Less than 1."; - throw new ArgumentException (msg, "length"); - } - - var bytes = stream.ReadBytes (length); - - var len = bytes.Length; - if (len == 0) { - var msg = "No data could be read from it."; - throw new ArgumentException (msg, "stream"); - } - - if (len < length) { - _log.Warn ( - String.Format ( - "Only {0} byte(s) of data could be read from the stream.", - len - ) - ); - } - - if (len <= WebSocket.FragmentLength) - broadcast (Opcode.Binary, bytes, null); - else - broadcast (Opcode.Binary, new MemoryStream (bytes), null); - } + _state = ServerState.Stop; + } + } - /// - /// Sends asynchronously to every client in - /// the WebSocket service. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// An array of that represents the binary data to send. - /// - /// - /// - /// An delegate or - /// if not needed. - /// - /// - /// The delegate invokes the method called when the send is complete. - /// - /// - /// - /// The current state of the manager is not Start. - /// - /// - /// is . - /// - public void BroadcastAsync (byte[] data, Action completed) - { - if (_state != ServerState.Start) { - var msg = "The current state of the manager is not Start."; - throw new InvalidOperationException (msg); - } - - if (data == null) - throw new ArgumentNullException ("data"); - - if (data.LongLength <= WebSocket.FragmentLength) - broadcastAsync (Opcode.Binary, data, completed); - else - broadcastAsync (Opcode.Binary, new MemoryStream (data), completed); - } + private bool tryGetSession(string id, out IWebSocketSession session) + { + session = null; - /// - /// Sends asynchronously to every client in - /// the WebSocket service. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// A that represents the text data to send. - /// - /// - /// - /// An delegate or - /// if not needed. - /// - /// - /// The delegate invokes the method called when the send is complete. - /// - /// - /// - /// The current state of the manager is not Start. - /// - /// - /// is . - /// - /// - /// could not be UTF-8-encoded. - /// - public void BroadcastAsync (string data, Action completed) - { - if (_state != ServerState.Start) { - var msg = "The current state of the manager is not Start."; - throw new InvalidOperationException (msg); - } - - if (data == null) - throw new ArgumentNullException ("data"); - - byte[] bytes; - if (!data.TryGetUTF8EncodedBytes (out bytes)) { - var msg = "It could not be UTF-8-encoded."; - throw new ArgumentException (msg, "data"); - } - - if (bytes.LongLength <= WebSocket.FragmentLength) - broadcastAsync (Opcode.Text, bytes, completed); - else - broadcastAsync (Opcode.Text, new MemoryStream (bytes), completed); - } + if (_state != ServerState.Start) + return false; - /// - /// Sends the data from asynchronously to - /// every client in the WebSocket service. - /// - /// - /// - /// The data is sent as the binary data. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// - /// A instance from which to read the data to send. - /// - /// - /// An that specifies the number of bytes to send. - /// - /// - /// - /// An delegate or - /// if not needed. - /// - /// - /// The delegate invokes the method called when the send is complete. - /// - /// - /// - /// The current state of the manager is not Start. - /// - /// - /// is . - /// - /// - /// - /// cannot be read. - /// - /// - /// -or- - /// - /// - /// is less than 1. - /// - /// - /// -or- - /// - /// - /// No data could be read from . - /// - /// - public void BroadcastAsync (Stream stream, int length, Action completed) - { - if (_state != ServerState.Start) { - var msg = "The current state of the manager is not Start."; - throw new InvalidOperationException (msg); - } - - if (stream == null) - throw new ArgumentNullException ("stream"); - - if (!stream.CanRead) { - var msg = "It cannot be read."; - throw new ArgumentException (msg, "stream"); - } - - if (length < 1) { - var msg = "Less than 1."; - throw new ArgumentException (msg, "length"); - } - - var bytes = stream.ReadBytes (length); - - var len = bytes.Length; - if (len == 0) { - var msg = "No data could be read from it."; - throw new ArgumentException (msg, "stream"); - } - - if (len < length) { - _log.Warn ( - String.Format ( - "Only {0} byte(s) of data could be read from the stream.", - len - ) - ); - } - - if (len <= WebSocket.FragmentLength) - broadcastAsync (Opcode.Binary, bytes, completed); - else - broadcastAsync (Opcode.Binary, new MemoryStream (bytes), completed); - } + lock (_sync) + { + if (_state != ServerState.Start) + return false; - /// - /// Sends a ping to every client in the WebSocket service. - /// - /// - /// - /// A Dictionary<string, bool>. - /// - /// - /// It represents a collection of pairs of a session ID and - /// a value indicating whether a pong has been received from - /// the client within a time. - /// - /// - /// - /// The current state of the manager is not Start. - /// - [Obsolete ("This method will be removed.")] - public Dictionary Broadping () - { - if (_state != ServerState.Start) { - var msg = "The current state of the manager is not Start."; - throw new InvalidOperationException (msg); - } + return _sessions.TryGetValue(id, out session); + } + } - return Broadping (WebSocketFrame.EmptyPingBytes, _waitTime); - } + internal string Add(IWebSocketSession session) + { + lock (_sync) + { + if (_state != ServerState.Start) + return null; - /// - /// Sends a ping with to every client in - /// the WebSocket service. - /// - /// - /// - /// A Dictionary<string, bool>. - /// - /// - /// It represents a collection of pairs of a session ID and - /// a value indicating whether a pong has been received from - /// the client within a time. - /// - /// - /// - /// - /// A that represents the message to send. - /// - /// - /// The size must be 125 bytes or less in UTF-8. - /// - /// - /// - /// The current state of the manager is not Start. - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// The size of is greater than 125 bytes. - /// - [Obsolete ("This method will be removed.")] - public Dictionary Broadping (string message) - { - if (_state != ServerState.Start) { - var msg = "The current state of the manager is not Start."; - throw new InvalidOperationException (msg); - } - - if (message.IsNullOrEmpty ()) - return Broadping (WebSocketFrame.EmptyPingBytes, _waitTime); - - byte[] bytes; - if (!message.TryGetUTF8EncodedBytes (out bytes)) { - var msg = "It could not be UTF-8-encoded."; - throw new ArgumentException (msg, "message"); - } - - if (bytes.Length > 125) { - var msg = "Its size is greater than 125 bytes."; - throw new ArgumentOutOfRangeException ("message", msg); - } - - var frame = WebSocketFrame.CreatePingFrame (bytes, false); - return Broadping (frame.ToArray (), _waitTime); - } + var id = createID(); + _sessions.Add(id, session); - /// - /// Closes the specified session. - /// - /// - /// A that represents the ID of the session to close. - /// - /// - /// is . - /// - /// - /// is an empty string. - /// - /// - /// The session could not be found. - /// - public void CloseSession (string id) - { - IWebSocketSession session; - if (!TryGetSession (id, out session)) { - var msg = "The session could not be found."; - throw new InvalidOperationException (msg); - } - - session.Context.WebSocket.Close (); - } - - /// - /// Closes the specified session with and - /// . - /// - /// - /// A that represents the ID of the session to close. - /// - /// - /// - /// A that represents the status code indicating - /// the reason for the close. - /// - /// - /// The status codes are defined in - /// - /// Section 7.4 of RFC 6455. - /// - /// - /// - /// - /// A that represents the reason for the close. - /// - /// - /// The size must be 123 bytes or less in UTF-8. - /// - /// - /// - /// is . - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is 1010 (mandatory extension). - /// - /// - /// -or- - /// - /// - /// is 1005 (no status) and there is - /// . - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// - /// The session could not be found. - /// - /// - /// - /// is less than 1000 or greater than 4999. - /// - /// - /// -or- - /// - /// - /// The size of is greater than 123 bytes. - /// - /// - public void CloseSession (string id, ushort code, string reason) - { - IWebSocketSession session; - if (!TryGetSession (id, out session)) { - var msg = "The session could not be found."; - throw new InvalidOperationException (msg); - } + return id; + } + } - session.Context.WebSocket.Close (code, reason); - } + internal void Broadcast( + Opcode opcode, byte[] data, Dictionary cache + ) + { + foreach (var session in Sessions) + { + if (_state != ServerState.Start) + { + _log.Error("The service is shutting down."); + break; + } + + session.Context.WebSocket.Send(opcode, data, cache); + } + } - /// - /// Closes the specified session with and - /// . - /// - /// - /// A that represents the ID of the session to close. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It represents the status code indicating the reason for the close. - /// - /// - /// - /// - /// A that represents the reason for the close. - /// - /// - /// The size must be 123 bytes or less in UTF-8. - /// - /// - /// - /// is . - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is - /// . - /// - /// - /// -or- - /// - /// - /// is - /// and there is - /// . - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// - /// The session could not be found. - /// - /// - /// The size of is greater than 123 bytes. - /// - public void CloseSession (string id, CloseStatusCode code, string reason) - { - IWebSocketSession session; - if (!TryGetSession (id, out session)) { - var msg = "The session could not be found."; - throw new InvalidOperationException (msg); - } + internal void Broadcast( + Opcode opcode, Stream stream, Dictionary cache + ) + { + foreach (var session in Sessions) + { + if (_state != ServerState.Start) + { + _log.Error("The service is shutting down."); + break; + } + + session.Context.WebSocket.Send(opcode, stream, cache); + } + } - session.Context.WebSocket.Close (code, reason); - } + internal Dictionary Broadping( + byte[] frameAsBytes, TimeSpan timeout + ) + { + var ret = new Dictionary(); + + foreach (var session in Sessions) + { + if (_state != ServerState.Start) + { + _log.Error("The service is shutting down."); + break; + } + + var res = session.Context.WebSocket.Ping(frameAsBytes, timeout); + ret.Add(session.ID, res); + } + + return ret; + } - /// - /// Sends a ping to the client using the specified session. - /// - /// - /// true if the send has done with no error and a pong has been - /// received from the client within a time; otherwise, false. - /// - /// - /// A that represents the ID of the session. - /// - /// - /// is . - /// - /// - /// is an empty string. - /// - /// - /// The session could not be found. - /// - public bool PingTo (string id) - { - IWebSocketSession session; - if (!TryGetSession (id, out session)) { - var msg = "The session could not be found."; - throw new InvalidOperationException (msg); - } + internal bool Remove(string id) + { + lock (_sync) + return _sessions.Remove(id); + } - return session.Context.WebSocket.Ping (); - } + internal void Start() + { + lock (_sync) + { + _sweepTimer.Enabled = _clean; + _state = ServerState.Start; + } + } - /// - /// Sends a ping with to the client using - /// the specified session. - /// - /// - /// true if the send has done with no error and a pong has been - /// received from the client within a time; otherwise, false. - /// - /// - /// - /// A that represents the message to send. - /// - /// - /// The size must be 125 bytes or less in UTF-8. - /// - /// - /// - /// A that represents the ID of the session. - /// - /// - /// is . - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// - /// The session could not be found. - /// - /// - /// The size of is greater than 125 bytes. - /// - public bool PingTo (string message, string id) - { - IWebSocketSession session; - if (!TryGetSession (id, out session)) { - var msg = "The session could not be found."; - throw new InvalidOperationException (msg); - } + internal void Stop(ushort code, string reason) + { + if (code == 1005) + { // == no status + stop(PayloadData.Empty, true); + return; + } - return session.Context.WebSocket.Ping (message); - } + stop(new PayloadData(code, reason), !code.IsReserved()); + } - /// - /// Sends to the client using the specified session. - /// - /// - /// An array of that represents the binary data to send. - /// - /// - /// A that represents the ID of the session. - /// - /// - /// - /// is . - /// - /// - /// -or- - /// - /// - /// is . - /// - /// - /// - /// is an empty string. - /// - /// - /// - /// The session could not be found. - /// - /// - /// -or- - /// - /// - /// The current state of the WebSocket connection is not Open. - /// - /// - public void SendTo (byte[] data, string id) - { - IWebSocketSession session; - if (!TryGetSession (id, out session)) { - var msg = "The session could not be found."; - throw new InvalidOperationException (msg); - } + /// + /// Sends to every client in the WebSocket service. + /// + /// + /// An array of that represents the binary data to send. + /// + /// + /// The current state of the manager is not Start. + /// + /// + /// is . + /// + public void Broadcast(byte[] data) + { + if (_state != ServerState.Start) + { + var msg = "The current state of the manager is not Start."; + throw new InvalidOperationException(msg); + } + + if (data == null) + throw new ArgumentNullException("data"); + + if (data.LongLength <= WebSocket.FragmentLength) + broadcast(Opcode.Binary, data, null); + else + broadcast(Opcode.Binary, new MemoryStream(data), null); + } - session.Context.WebSocket.Send (data); - } + /// + /// Sends to every client in the WebSocket service. + /// + /// + /// A that represents the text data to send. + /// + /// + /// The current state of the manager is not Start. + /// + /// + /// is . + /// + /// + /// could not be UTF-8-encoded. + /// + public void Broadcast(string data) + { + if (_state != ServerState.Start) + { + var msg = "The current state of the manager is not Start."; + throw new InvalidOperationException(msg); + } + + if (data == null) + throw new ArgumentNullException("data"); + + byte[] bytes; + if (!data.TryGetUTF8EncodedBytes(out bytes)) + { + var msg = "It could not be UTF-8-encoded."; + throw new ArgumentException(msg, "data"); + } + + if (bytes.LongLength <= WebSocket.FragmentLength) + broadcast(Opcode.Text, bytes, null); + else + broadcast(Opcode.Text, new MemoryStream(bytes), null); + } - /// - /// Sends to the client using the specified session. - /// - /// - /// A that represents the text data to send. - /// - /// - /// A that represents the ID of the session. - /// - /// - /// - /// is . - /// - /// - /// -or- - /// - /// - /// is . - /// - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// - /// - /// The session could not be found. - /// - /// - /// -or- - /// - /// - /// The current state of the WebSocket connection is not Open. - /// - /// - public void SendTo (string data, string id) - { - IWebSocketSession session; - if (!TryGetSession (id, out session)) { - var msg = "The session could not be found."; - throw new InvalidOperationException (msg); - } + /// + /// Sends the data from to every client in + /// the WebSocket service. + /// + /// + /// The data is sent as the binary data. + /// + /// + /// A instance from which to read the data to send. + /// + /// + /// An that specifies the number of bytes to send. + /// + /// + /// The current state of the manager is not Start. + /// + /// + /// is . + /// + /// + /// + /// cannot be read. + /// + /// + /// -or- + /// + /// + /// is less than 1. + /// + /// + /// -or- + /// + /// + /// No data could be read from . + /// + /// + public void Broadcast(Stream stream, int length) + { + if (_state != ServerState.Start) + { + var msg = "The current state of the manager is not Start."; + throw new InvalidOperationException(msg); + } + + if (stream == null) + throw new ArgumentNullException("stream"); + + if (!stream.CanRead) + { + var msg = "It cannot be read."; + throw new ArgumentException(msg, "stream"); + } + + if (length < 1) + { + var msg = "Less than 1."; + throw new ArgumentException(msg, "length"); + } + + var bytes = stream.ReadBytes(length); + + var len = bytes.Length; + if (len == 0) + { + var msg = "No data could be read from it."; + throw new ArgumentException(msg, "stream"); + } + + if (len < length) + { + _log.Warn( + String.Format( + "Only {0} byte(s) of data could be read from the stream.", + len + ) + ); + } + + if (len <= WebSocket.FragmentLength) + broadcast(Opcode.Binary, bytes, null); + else + broadcast(Opcode.Binary, new MemoryStream(bytes), null); + } - session.Context.WebSocket.Send (data); - } + /// + /// Sends asynchronously to every client in + /// the WebSocket service. + /// + /// + /// This method does not wait for the send to be complete. + /// + /// + /// An array of that represents the binary data to send. + /// + /// + /// + /// An delegate or + /// if not needed. + /// + /// + /// The delegate invokes the method called when the send is complete. + /// + /// + /// + /// The current state of the manager is not Start. + /// + /// + /// is . + /// + public void BroadcastAsync(byte[] data, Action completed) + { + if (_state != ServerState.Start) + { + var msg = "The current state of the manager is not Start."; + throw new InvalidOperationException(msg); + } + + if (data == null) + throw new ArgumentNullException("data"); + + if (data.LongLength <= WebSocket.FragmentLength) + broadcastAsync(Opcode.Binary, data, completed); + else + broadcastAsync(Opcode.Binary, new MemoryStream(data), completed); + } - /// - /// Sends the data from to the client using - /// the specified session. - /// - /// - /// The data is sent as the binary data. - /// - /// - /// A instance from which to read the data to send. - /// - /// - /// An that specifies the number of bytes to send. - /// - /// - /// A that represents the ID of the session. - /// - /// - /// - /// is . - /// - /// - /// -or- - /// - /// - /// is . - /// - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// cannot be read. - /// - /// - /// -or- - /// - /// - /// is less than 1. - /// - /// - /// -or- - /// - /// - /// No data could be read from . - /// - /// - /// - /// - /// The session could not be found. - /// - /// - /// -or- - /// - /// - /// The current state of the WebSocket connection is not Open. - /// - /// - public void SendTo (Stream stream, int length, string id) - { - IWebSocketSession session; - if (!TryGetSession (id, out session)) { - var msg = "The session could not be found."; - throw new InvalidOperationException (msg); - } + /// + /// Sends asynchronously to every client in + /// the WebSocket service. + /// + /// + /// This method does not wait for the send to be complete. + /// + /// + /// A that represents the text data to send. + /// + /// + /// + /// An delegate or + /// if not needed. + /// + /// + /// The delegate invokes the method called when the send is complete. + /// + /// + /// + /// The current state of the manager is not Start. + /// + /// + /// is . + /// + /// + /// could not be UTF-8-encoded. + /// + public void BroadcastAsync(string data, Action completed) + { + if (_state != ServerState.Start) + { + var msg = "The current state of the manager is not Start."; + throw new InvalidOperationException(msg); + } + + if (data == null) + throw new ArgumentNullException("data"); + + byte[] bytes; + if (!data.TryGetUTF8EncodedBytes(out bytes)) + { + var msg = "It could not be UTF-8-encoded."; + throw new ArgumentException(msg, "data"); + } + + if (bytes.LongLength <= WebSocket.FragmentLength) + broadcastAsync(Opcode.Text, bytes, completed); + else + broadcastAsync(Opcode.Text, new MemoryStream(bytes), completed); + } - session.Context.WebSocket.Send (stream, length); - } + /// + /// Sends the data from asynchronously to + /// every client in the WebSocket service. + /// + /// + /// + /// The data is sent as the binary data. + /// + /// + /// This method does not wait for the send to be complete. + /// + /// + /// + /// A instance from which to read the data to send. + /// + /// + /// An that specifies the number of bytes to send. + /// + /// + /// + /// An delegate or + /// if not needed. + /// + /// + /// The delegate invokes the method called when the send is complete. + /// + /// + /// + /// The current state of the manager is not Start. + /// + /// + /// is . + /// + /// + /// + /// cannot be read. + /// + /// + /// -or- + /// + /// + /// is less than 1. + /// + /// + /// -or- + /// + /// + /// No data could be read from . + /// + /// + public void BroadcastAsync(Stream stream, int length, Action completed) + { + if (_state != ServerState.Start) + { + var msg = "The current state of the manager is not Start."; + throw new InvalidOperationException(msg); + } + + if (stream == null) + throw new ArgumentNullException("stream"); + + if (!stream.CanRead) + { + var msg = "It cannot be read."; + throw new ArgumentException(msg, "stream"); + } + + if (length < 1) + { + var msg = "Less than 1."; + throw new ArgumentException(msg, "length"); + } + + var bytes = stream.ReadBytes(length); + + var len = bytes.Length; + if (len == 0) + { + var msg = "No data could be read from it."; + throw new ArgumentException(msg, "stream"); + } + + if (len < length) + { + _log.Warn( + String.Format( + "Only {0} byte(s) of data could be read from the stream.", + len + ) + ); + } + + if (len <= WebSocket.FragmentLength) + broadcastAsync(Opcode.Binary, bytes, completed); + else + broadcastAsync(Opcode.Binary, new MemoryStream(bytes), completed); + } - /// - /// Sends asynchronously to the client using - /// the specified session. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// An array of that represents the binary data to send. - /// - /// - /// A that represents the ID of the session. - /// - /// - /// - /// An Action<bool> delegate or - /// if not needed. - /// - /// - /// The delegate invokes the method called when the send is complete. - /// - /// - /// true is passed to the method if the send has done with - /// no error; otherwise, false. - /// - /// - /// - /// - /// is . - /// - /// - /// -or- - /// - /// - /// is . - /// - /// - /// - /// is an empty string. - /// - /// - /// - /// The session could not be found. - /// - /// - /// -or- - /// - /// - /// The current state of the WebSocket connection is not Open. - /// - /// - public void SendToAsync (byte[] data, string id, Action completed) - { - IWebSocketSession session; - if (!TryGetSession (id, out session)) { - var msg = "The session could not be found."; - throw new InvalidOperationException (msg); - } + /// + /// Sends a ping to every client in the WebSocket service. + /// + /// + /// + /// A Dictionary<string, bool>. + /// + /// + /// It represents a collection of pairs of a session ID and + /// a value indicating whether a pong has been received from + /// the client within a time. + /// + /// + /// + /// The current state of the manager is not Start. + /// + [Obsolete("This method will be removed.")] + public Dictionary Broadping() + { + if (_state != ServerState.Start) + { + var msg = "The current state of the manager is not Start."; + throw new InvalidOperationException(msg); + } + + return Broadping(WebSocketFrame.EmptyPingBytes, _waitTime); + } - session.Context.WebSocket.SendAsync (data, completed); - } + /// + /// Sends a ping with to every client in + /// the WebSocket service. + /// + /// + /// + /// A Dictionary<string, bool>. + /// + /// + /// It represents a collection of pairs of a session ID and + /// a value indicating whether a pong has been received from + /// the client within a time. + /// + /// + /// + /// + /// A that represents the message to send. + /// + /// + /// The size must be 125 bytes or less in UTF-8. + /// + /// + /// + /// The current state of the manager is not Start. + /// + /// + /// could not be UTF-8-encoded. + /// + /// + /// The size of is greater than 125 bytes. + /// + [Obsolete("This method will be removed.")] + public Dictionary Broadping(string message) + { + if (_state != ServerState.Start) + { + var msg = "The current state of the manager is not Start."; + throw new InvalidOperationException(msg); + } + + if (message.IsNullOrEmpty()) + return Broadping(WebSocketFrame.EmptyPingBytes, _waitTime); + + byte[] bytes; + if (!message.TryGetUTF8EncodedBytes(out bytes)) + { + var msg = "It could not be UTF-8-encoded."; + throw new ArgumentException(msg, "message"); + } + + if (bytes.Length > 125) + { + var msg = "Its size is greater than 125 bytes."; + throw new ArgumentOutOfRangeException("message", msg); + } + + var frame = WebSocketFrame.CreatePingFrame(bytes, false); + return Broadping(frame.ToArray(), _waitTime); + } - /// - /// Sends asynchronously to the client using - /// the specified session. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// A that represents the text data to send. - /// - /// - /// A that represents the ID of the session. - /// - /// - /// - /// An Action<bool> delegate or - /// if not needed. - /// - /// - /// The delegate invokes the method called when the send is complete. - /// - /// - /// true is passed to the method if the send has done with - /// no error; otherwise, false. - /// - /// - /// - /// - /// is . - /// - /// - /// -or- - /// - /// - /// is . - /// - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// - /// - /// The session could not be found. - /// - /// - /// -or- - /// - /// - /// The current state of the WebSocket connection is not Open. - /// - /// - public void SendToAsync (string data, string id, Action completed) - { - IWebSocketSession session; - if (!TryGetSession (id, out session)) { - var msg = "The session could not be found."; - throw new InvalidOperationException (msg); - } + /// + /// Closes the specified session. + /// + /// + /// A that represents the ID of the session to close. + /// + /// + /// is . + /// + /// + /// is an empty string. + /// + /// + /// The session could not be found. + /// + public void CloseSession(string id) + { + IWebSocketSession session; + if (!TryGetSession(id, out session)) + { + var msg = "The session could not be found."; + throw new InvalidOperationException(msg); + } + + session.Context.WebSocket.Close(); + } - session.Context.WebSocket.SendAsync (data, completed); - } + /// + /// Closes the specified session with and + /// . + /// + /// + /// A that represents the ID of the session to close. + /// + /// + /// + /// A that represents the status code indicating + /// the reason for the close. + /// + /// + /// The status codes are defined in + /// + /// Section 7.4 of RFC 6455. + /// + /// + /// + /// + /// A that represents the reason for the close. + /// + /// + /// The size must be 123 bytes or less in UTF-8. + /// + /// + /// + /// is . + /// + /// + /// + /// is an empty string. + /// + /// + /// -or- + /// + /// + /// is 1010 (mandatory extension). + /// + /// + /// -or- + /// + /// + /// is 1005 (no status) and there is + /// . + /// + /// + /// -or- + /// + /// + /// could not be UTF-8-encoded. + /// + /// + /// + /// The session could not be found. + /// + /// + /// + /// is less than 1000 or greater than 4999. + /// + /// + /// -or- + /// + /// + /// The size of is greater than 123 bytes. + /// + /// + public void CloseSession(string id, ushort code, string reason) + { + IWebSocketSession session; + if (!TryGetSession(id, out session)) + { + var msg = "The session could not be found."; + throw new InvalidOperationException(msg); + } + + session.Context.WebSocket.Close(code, reason); + } - /// - /// Sends the data from asynchronously to - /// the client using the specified session. - /// - /// - /// - /// The data is sent as the binary data. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// - /// A instance from which to read the data to send. - /// - /// - /// An that specifies the number of bytes to send. - /// - /// - /// A that represents the ID of the session. - /// - /// - /// - /// An Action<bool> delegate or - /// if not needed. - /// - /// - /// The delegate invokes the method called when the send is complete. - /// - /// - /// true is passed to the method if the send has done with - /// no error; otherwise, false. - /// - /// - /// - /// - /// is . - /// - /// - /// -or- - /// - /// - /// is . - /// - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// cannot be read. - /// - /// - /// -or- - /// - /// - /// is less than 1. - /// - /// - /// -or- - /// - /// - /// No data could be read from . - /// - /// - /// - /// - /// The session could not be found. - /// - /// - /// -or- - /// - /// - /// The current state of the WebSocket connection is not Open. - /// - /// - public void SendToAsync ( - Stream stream, int length, string id, Action completed - ) - { - IWebSocketSession session; - if (!TryGetSession (id, out session)) { - var msg = "The session could not be found."; - throw new InvalidOperationException (msg); - } + /// + /// Closes the specified session with and + /// . + /// + /// + /// A that represents the ID of the session to close. + /// + /// + /// + /// One of the enum values. + /// + /// + /// It represents the status code indicating the reason for the close. + /// + /// + /// + /// + /// A that represents the reason for the close. + /// + /// + /// The size must be 123 bytes or less in UTF-8. + /// + /// + /// + /// is . + /// + /// + /// + /// is an empty string. + /// + /// + /// -or- + /// + /// + /// is + /// . + /// + /// + /// -or- + /// + /// + /// is + /// and there is + /// . + /// + /// + /// -or- + /// + /// + /// could not be UTF-8-encoded. + /// + /// + /// + /// The session could not be found. + /// + /// + /// The size of is greater than 123 bytes. + /// + public void CloseSession(string id, CloseStatusCode code, string reason) + { + IWebSocketSession session; + if (!TryGetSession(id, out session)) + { + var msg = "The session could not be found."; + throw new InvalidOperationException(msg); + } + + session.Context.WebSocket.Close(code, reason); + } - session.Context.WebSocket.SendAsync (stream, length, completed); - } + /// + /// Sends a ping to the client using the specified session. + /// + /// + /// true if the send has done with no error and a pong has been + /// received from the client within a time; otherwise, false. + /// + /// + /// A that represents the ID of the session. + /// + /// + /// is . + /// + /// + /// is an empty string. + /// + /// + /// The session could not be found. + /// + public bool PingTo(string id) + { + IWebSocketSession session; + if (!TryGetSession(id, out session)) + { + var msg = "The session could not be found."; + throw new InvalidOperationException(msg); + } + + return session.Context.WebSocket.Ping(); + } - /// - /// Cleans up the inactive sessions in the WebSocket service. - /// - public void Sweep () - { - if (_sweeping) { - _log.Info ("The sweeping is already in progress."); - return; - } - - lock (_forSweep) { - if (_sweeping) { - _log.Info ("The sweeping is already in progress."); - return; + /// + /// Sends a ping with to the client using + /// the specified session. + /// + /// + /// true if the send has done with no error and a pong has been + /// received from the client within a time; otherwise, false. + /// + /// + /// + /// A that represents the message to send. + /// + /// + /// The size must be 125 bytes or less in UTF-8. + /// + /// + /// + /// A that represents the ID of the session. + /// + /// + /// is . + /// + /// + /// + /// is an empty string. + /// + /// + /// -or- + /// + /// + /// could not be UTF-8-encoded. + /// + /// + /// + /// The session could not be found. + /// + /// + /// The size of is greater than 125 bytes. + /// + public bool PingTo(string message, string id) + { + IWebSocketSession session; + if (!TryGetSession(id, out session)) + { + var msg = "The session could not be found."; + throw new InvalidOperationException(msg); + } + + return session.Context.WebSocket.Ping(message); } - _sweeping = true; - } + /// + /// Sends to the client using the specified session. + /// + /// + /// An array of that represents the binary data to send. + /// + /// + /// A that represents the ID of the session. + /// + /// + /// + /// is . + /// + /// + /// -or- + /// + /// + /// is . + /// + /// + /// + /// is an empty string. + /// + /// + /// + /// The session could not be found. + /// + /// + /// -or- + /// + /// + /// The current state of the WebSocket connection is not Open. + /// + /// + public void SendTo(byte[] data, string id) + { + IWebSocketSession session; + if (!TryGetSession(id, out session)) + { + var msg = "The session could not be found."; + throw new InvalidOperationException(msg); + } + + session.Context.WebSocket.Send(data); + } - foreach (var id in InactiveIDs) { - if (_state != ServerState.Start) - break; + /// + /// Sends to the client using the specified session. + /// + /// + /// A that represents the text data to send. + /// + /// + /// A that represents the ID of the session. + /// + /// + /// + /// is . + /// + /// + /// -or- + /// + /// + /// is . + /// + /// + /// + /// + /// is an empty string. + /// + /// + /// -or- + /// + /// + /// could not be UTF-8-encoded. + /// + /// + /// + /// + /// The session could not be found. + /// + /// + /// -or- + /// + /// + /// The current state of the WebSocket connection is not Open. + /// + /// + public void SendTo(string data, string id) + { + IWebSocketSession session; + if (!TryGetSession(id, out session)) + { + var msg = "The session could not be found."; + throw new InvalidOperationException(msg); + } + + session.Context.WebSocket.Send(data); + } - lock (_sync) { - if (_state != ServerState.Start) - break; + /// + /// Sends the data from to the client using + /// the specified session. + /// + /// + /// The data is sent as the binary data. + /// + /// + /// A instance from which to read the data to send. + /// + /// + /// An that specifies the number of bytes to send. + /// + /// + /// A that represents the ID of the session. + /// + /// + /// + /// is . + /// + /// + /// -or- + /// + /// + /// is . + /// + /// + /// + /// + /// is an empty string. + /// + /// + /// -or- + /// + /// + /// cannot be read. + /// + /// + /// -or- + /// + /// + /// is less than 1. + /// + /// + /// -or- + /// + /// + /// No data could be read from . + /// + /// + /// + /// + /// The session could not be found. + /// + /// + /// -or- + /// + /// + /// The current state of the WebSocket connection is not Open. + /// + /// + public void SendTo(Stream stream, int length, string id) + { + IWebSocketSession session; + if (!TryGetSession(id, out session)) + { + var msg = "The session could not be found."; + throw new InvalidOperationException(msg); + } + + session.Context.WebSocket.Send(stream, length); + } - IWebSocketSession session; - if (_sessions.TryGetValue (id, out session)) { - var state = session.ConnectionState; - if (state == WebSocketState.Open) - session.Context.WebSocket.Close (CloseStatusCode.Abnormal); - else if (state == WebSocketState.Closing) - continue; - else - _sessions.Remove (id); - } + /// + /// Sends asynchronously to the client using + /// the specified session. + /// + /// + /// This method does not wait for the send to be complete. + /// + /// + /// An array of that represents the binary data to send. + /// + /// + /// A that represents the ID of the session. + /// + /// + /// + /// An Action<bool> delegate or + /// if not needed. + /// + /// + /// The delegate invokes the method called when the send is complete. + /// + /// + /// true is passed to the method if the send has done with + /// no error; otherwise, false. + /// + /// + /// + /// + /// is . + /// + /// + /// -or- + /// + /// + /// is . + /// + /// + /// + /// is an empty string. + /// + /// + /// + /// The session could not be found. + /// + /// + /// -or- + /// + /// + /// The current state of the WebSocket connection is not Open. + /// + /// + public void SendToAsync(byte[] data, string id, Action completed) + { + IWebSocketSession session; + if (!TryGetSession(id, out session)) + { + var msg = "The session could not be found."; + throw new InvalidOperationException(msg); + } + + session.Context.WebSocket.SendAsync(data, completed); } - } - _sweeping = false; - } + /// + /// Sends asynchronously to the client using + /// the specified session. + /// + /// + /// This method does not wait for the send to be complete. + /// + /// + /// A that represents the text data to send. + /// + /// + /// A that represents the ID of the session. + /// + /// + /// + /// An Action<bool> delegate or + /// if not needed. + /// + /// + /// The delegate invokes the method called when the send is complete. + /// + /// + /// true is passed to the method if the send has done with + /// no error; otherwise, false. + /// + /// + /// + /// + /// is . + /// + /// + /// -or- + /// + /// + /// is . + /// + /// + /// + /// + /// is an empty string. + /// + /// + /// -or- + /// + /// + /// could not be UTF-8-encoded. + /// + /// + /// + /// + /// The session could not be found. + /// + /// + /// -or- + /// + /// + /// The current state of the WebSocket connection is not Open. + /// + /// + public void SendToAsync(string data, string id, Action completed) + { + IWebSocketSession session; + if (!TryGetSession(id, out session)) + { + var msg = "The session could not be found."; + throw new InvalidOperationException(msg); + } + + session.Context.WebSocket.SendAsync(data, completed); + } - /// - /// Tries to get the session instance with . - /// - /// - /// true if the session is successfully found; otherwise, - /// false. - /// - /// - /// A that represents the ID of the session to find. - /// - /// - /// - /// When this method returns, a - /// instance or if not found. - /// - /// - /// The session instance provides the function to access - /// the information in the session. - /// - /// - /// - /// is . - /// - /// - /// is an empty string. - /// - public bool TryGetSession (string id, out IWebSocketSession session) - { - if (id == null) - throw new ArgumentNullException ("id"); + /// + /// Sends the data from asynchronously to + /// the client using the specified session. + /// + /// + /// + /// The data is sent as the binary data. + /// + /// + /// This method does not wait for the send to be complete. + /// + /// + /// + /// A instance from which to read the data to send. + /// + /// + /// An that specifies the number of bytes to send. + /// + /// + /// A that represents the ID of the session. + /// + /// + /// + /// An Action<bool> delegate or + /// if not needed. + /// + /// + /// The delegate invokes the method called when the send is complete. + /// + /// + /// true is passed to the method if the send has done with + /// no error; otherwise, false. + /// + /// + /// + /// + /// is . + /// + /// + /// -or- + /// + /// + /// is . + /// + /// + /// + /// + /// is an empty string. + /// + /// + /// -or- + /// + /// + /// cannot be read. + /// + /// + /// -or- + /// + /// + /// is less than 1. + /// + /// + /// -or- + /// + /// + /// No data could be read from . + /// + /// + /// + /// + /// The session could not be found. + /// + /// + /// -or- + /// + /// + /// The current state of the WebSocket connection is not Open. + /// + /// + public void SendToAsync( + Stream stream, int length, string id, Action completed + ) + { + IWebSocketSession session; + if (!TryGetSession(id, out session)) + { + var msg = "The session could not be found."; + throw new InvalidOperationException(msg); + } + + session.Context.WebSocket.SendAsync(stream, length, completed); + } - if (id.Length == 0) - throw new ArgumentException ("An empty string.", "id"); + /// + /// Cleans up the inactive sessions in the WebSocket service. + /// + public void Sweep() + { + if (_sweeping) + { + _log.Info("The sweeping is already in progress."); + return; + } + + lock (_forSweep) + { + if (_sweeping) + { + _log.Info("The sweeping is already in progress."); + return; + } + + _sweeping = true; + } + + foreach (var id in InactiveIDs) + { + if (_state != ServerState.Start) + break; + + lock (_sync) + { + if (_state != ServerState.Start) + break; + + IWebSocketSession session; + if (_sessions.TryGetValue(id, out session)) + { + var state = session.ConnectionState; + if (state == WebSocketState.Open) + session.Context.WebSocket.Close(CloseStatusCode.Abnormal); + else if (state == WebSocketState.Closing) + continue; + else + _sessions.Remove(id); + } + } + } + + _sweeping = false; + } - return tryGetSession (id, out session); + /// + /// Tries to get the session instance with . + /// + /// + /// true if the session is successfully found; otherwise, + /// false. + /// + /// + /// A that represents the ID of the session to find. + /// + /// + /// + /// When this method returns, a + /// instance or if not found. + /// + /// + /// The session instance provides the function to access + /// the information in the session. + /// + /// + /// + /// is . + /// + /// + /// is an empty string. + /// + public bool TryGetSession(string id, out IWebSocketSession session) + { + if (id == null) + throw new ArgumentNullException("id"); + + if (id.Length == 0) + throw new ArgumentException("An empty string.", "id"); + + return tryGetSession(id, out session); + } } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/WebSocket.cs b/src/WebSocket/WebSocketCore/WebSocket.cs index 618e23b3f..db9aeb84f 100644 --- a/src/WebSocket/WebSocketCore/WebSocket.cs +++ b/src/WebSocket/WebSocketCore/WebSocket.cs @@ -1,4 +1,3 @@ -#region License /* * WebSocket.cs * @@ -28,16 +27,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Contributors /* * Contributors: * - Frank Razenberg * - David Wood * - Liryna */ -#endregion using System; using System.Collections; @@ -55,4050 +51,3866 @@ namespace WebSocketCore { - /// - /// Implements the WebSocket interface. - /// - /// - /// - /// This class provides a set of methods and properties for two-way - /// communication using the WebSocket protocol. - /// - /// - /// The WebSocket protocol is defined in - /// RFC 6455. - /// - /// - public class WebSocket : IDisposable - { - #region Private Fields - - private AuthenticationChallenge _authChallenge; - private string _base64Key; - private bool _client; - private Action _closeContext; - private CompressionMethod _compression; - private WebSocketContext _context; - private CookieCollection _cookies; - private NetworkCredential _credentials; - private bool _emitOnPing; - private bool _enableRedirection; - private string _extensions; - private bool _extensionsRequested; - private object _forMessageEventQueue; - private object _forPing; - private object _forSend; - private object _forState; - private MemoryStream _fragmentsBuffer; - private bool _fragmentsCompressed; - private Opcode _fragmentsOpcode; - private const string _guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - private Func _handshakeRequestChecker; - private bool _ignoreExtensions; - private bool _inContinuation; - private volatile bool _inMessage; - private volatile Logger _logger; - private static readonly int _maxRetryCountForConnect; - private Action _message; - private Queue _messageEventQueue; - private uint _nonceCount; - private string _origin; - private ManualResetEvent _pongReceived; - private bool _preAuth; - private string _protocol; - private string[] _protocols; - private bool _protocolsRequested; - private NetworkCredential _proxyCredentials; - private Uri _proxyUri; - private volatile WebSocketState _readyState; - private ManualResetEvent _receivingExited; - private int _retryCountForConnect; - private bool _secure; - private ClientSslConfiguration _sslConfig; - private Stream _stream; - private TcpClient _tcpClient; - private Uri _uri; - private const string _version = "13"; - private TimeSpan _waitTime; - - #endregion - - #region Internal Fields - - /// - /// Represents the empty array of used internally. - /// - internal static readonly byte[] EmptyBytes; - - /// - /// Represents the length used to determine whether the data should be fragmented in sending. - /// - /// - /// - /// The data will be fragmented if that length is greater than the value of this field. - /// - /// - /// If you would like to change the value, you must set it to a value between 125 and - /// Int32.MaxValue - 14 inclusive. - /// - /// - internal static readonly int FragmentLength; - - /// - /// Represents the random number generator used internally. - /// - internal static readonly RandomNumberGenerator RandomNumber; - - #endregion - - #region Static Constructor - - static WebSocket () - { - _maxRetryCountForConnect = 10; - EmptyBytes = new byte[0]; - FragmentLength = 1016; - RandomNumber = new RNGCryptoServiceProvider (); - } - - #endregion - - #region Internal Constructors - - // As server - internal WebSocket (HttpListenerWebSocketContext context, string protocol) - { - _context = context; - _protocol = protocol; - - _closeContext = context.Close; - _logger = context.Log; - _message = messages; - _secure = context.IsSecureConnection; - _stream = context.Stream; - _waitTime = TimeSpan.FromSeconds (1); - - init (); - } - - // As server - internal WebSocket (TcpListenerWebSocketContext context, string protocol) - { - _context = context; - _protocol = protocol; - - _closeContext = context.Close; - _logger = context.Log; - _message = messages; - _secure = context.IsSecureConnection; - _stream = context.Stream; - _waitTime = TimeSpan.FromSeconds (1); - - init (); - } - - #endregion - - #region Public Constructors - /// - /// Initializes a new instance of the class with - /// and optionally . + /// Implements the WebSocket interface. /// - /// - /// - /// A that specifies the URL to which to connect. - /// - /// - /// The scheme of the URL must be ws or wss. - /// - /// - /// The new instance uses a secure connection if the scheme is wss. - /// - /// - /// - /// - /// An array of that specifies the names of - /// the subprotocols if necessary. - /// - /// - /// Each value of the array must be a token defined in - /// - /// RFC 2616. - /// - /// - /// - /// is . - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is an invalid WebSocket URL string. - /// - /// - /// -or- - /// - /// - /// contains a value that is not a token. - /// - /// - /// -or- - /// - /// - /// contains a value twice. - /// - /// - public WebSocket (string url, params string[] protocols) - { - if (url == null) - throw new ArgumentNullException ("url"); - - if (url.Length == 0) - throw new ArgumentException ("An empty string.", "url"); + public class WebSocket : IDisposable + { + #region + + /// + /// Defines the _guid + /// + private const string _guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + + /// + /// Defines the _version + /// + private const string _version = "13"; + + #endregion + + #region ֶ + + /// + /// Represents the empty array of used internally. + /// + internal static readonly byte[] EmptyBytes; + + /// + /// Represents the length used to determine whether the data should be fragmented in sending. + /// + internal static readonly int FragmentLength; + + /// + /// Represents the random number generator used internally. + /// + internal static readonly RandomNumberGenerator RandomNumber; + + /// + /// Defines the _maxRetryCountForConnect + /// + private static readonly int _maxRetryCountForConnect; + + /// + /// Defines the _authChallenge + /// + private AuthenticationChallenge _authChallenge; + + /// + /// Defines the _base64Key + /// + private string _base64Key; + + /// + /// Defines the _client + /// + private bool _client; + + /// + /// Defines the _closeContext + /// + private Action _closeContext; + + /// + /// Defines the _compression + /// + private CompressionMethod _compression; + + /// + /// Defines the _context + /// + private WebSocketContext _context; + + /// + /// Defines the _cookies + /// + private CookieCollection _cookies; + + /// + /// Defines the _credentials + /// + private NetworkCredential _credentials; + + /// + /// Defines the _emitOnPing + /// + private bool _emitOnPing; + + /// + /// Defines the _enableRedirection + /// + private bool _enableRedirection; + + /// + /// Defines the _extensions + /// + private string _extensions; + + /// + /// Defines the _extensionsRequested + /// + private bool _extensionsRequested; + + /// + /// Defines the _forMessageEventQueue + /// + private object _forMessageEventQueue; + + /// + /// Defines the _forPing + /// + private object _forPing; + + /// + /// Defines the _forSend + /// + private object _forSend; + + /// + /// Defines the _forState + /// + private object _forState; + + /// + /// Defines the _fragmentsBuffer + /// + private MemoryStream _fragmentsBuffer; + + /// + /// Defines the _fragmentsCompressed + /// + private bool _fragmentsCompressed; + + /// + /// Defines the _fragmentsOpcode + /// + private Opcode _fragmentsOpcode; + + /// + /// Defines the _handshakeRequestChecker + /// + private Func _handshakeRequestChecker; + + /// + /// Defines the _ignoreExtensions + /// + private bool _ignoreExtensions; + + /// + /// Defines the _inContinuation + /// + private bool _inContinuation; + + /// + /// Defines the _inMessage + /// + private volatile bool _inMessage; + + /// + /// Defines the _logger + /// + private volatile Logger _logger; + + /// + /// Defines the _message + /// + private Action _message; + + /// + /// Defines the _messageEventQueue + /// + private Queue _messageEventQueue; + + /// + /// Defines the _nonceCount + /// + private uint _nonceCount; + + /// + /// Defines the _origin + /// + private string _origin; + + /// + /// Defines the _pongReceived + /// + private ManualResetEvent _pongReceived; + + /// + /// Defines the _preAuth + /// + private bool _preAuth; + + /// + /// Defines the _protocol + /// + private string _protocol; + + /// + /// Defines the _protocols + /// + private string[] _protocols; + + /// + /// Defines the _protocolsRequested + /// + private bool _protocolsRequested; + + /// + /// Defines the _proxyCredentials + /// + private NetworkCredential _proxyCredentials; + + /// + /// Defines the _proxyUri + /// + private Uri _proxyUri; + + /// + /// Defines the _readyState + /// + private volatile WebSocketState _readyState; + + /// + /// Defines the _receivingExited + /// + private ManualResetEvent _receivingExited; + + /// + /// Defines the _retryCountForConnect + /// + private int _retryCountForConnect; + + /// + /// Defines the _secure + /// + private bool _secure; + + /// + /// Defines the _sslConfig + /// + private ClientSslConfiguration _sslConfig; + + /// + /// Defines the _stream + /// + private Stream _stream; + + /// + /// Defines the _tcpClient + /// + private TcpClient _tcpClient; + + /// + /// Defines the _uri + /// + private Uri _uri; + + /// + /// Defines the _waitTime + /// + private TimeSpan _waitTime; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The url + /// The protocols + public WebSocket(string url, params string[] protocols) + { + if (url == null) + throw new ArgumentNullException("url"); + + if (url.Length == 0) + throw new ArgumentException("An empty string.", "url"); + + string msg; + if (!url.TryCreateWebSocketUri(out _uri, out msg)) + throw new ArgumentException(msg, "url"); + + if (protocols != null && protocols.Length > 0) + { + if (!checkProtocols(protocols, out msg)) + throw new ArgumentException(msg, "protocols"); + + _protocols = protocols; + } - string msg; - if (!url.TryCreateWebSocketUri (out _uri, out msg)) - throw new ArgumentException (msg, "url"); + _base64Key = CreateBase64Key(); + _client = true; + _logger = new Logger(); + _message = messagec; + _secure = _uri.Scheme == "wss"; + _waitTime = TimeSpan.FromSeconds(5); + + init(); + } + + // As server + /// + /// Initializes a new instance of the class. + /// + /// The context + /// The protocol + internal WebSocket(HttpListenerWebSocketContext context, string protocol) + { + _context = context; + _protocol = protocol; + + _closeContext = context.Close; + _logger = context.Log; + _message = messages; + _secure = context.IsSecureConnection; + _stream = context.Stream; + _waitTime = TimeSpan.FromSeconds(1); + + init(); + } + + // As server + /// + /// Initializes a new instance of the class. + /// + /// The context + /// The protocol + internal WebSocket(TcpListenerWebSocketContext context, string protocol) + { + _context = context; + _protocol = protocol; + + _closeContext = context.Close; + _logger = context.Log; + _message = messages; + _secure = context.IsSecureConnection; + _stream = context.Stream; + _waitTime = TimeSpan.FromSeconds(1); + + init(); + } + + /// + /// Initializes static members of the class. + /// + static WebSocket() + { + _maxRetryCountForConnect = 10; + EmptyBytes = new byte[0]; + FragmentLength = 1016; + RandomNumber = new RNGCryptoServiceProvider(); + } + + #endregion 캯 + + #region ¼ + + /// + /// Occurs when the WebSocket connection has been closed. + /// + public event EventHandler OnClose; + + /// + /// Occurs when the gets an error. + /// + public event EventHandler OnError; + + /// + /// Occurs when the receives a message. + /// + public event EventHandler OnMessage; + + /// + /// Occurs when the WebSocket connection has been established. + /// + public event EventHandler OnOpen; + + #endregion ¼ + + #region + + /// + /// Gets or sets the compression method used to compress a message. + /// + public CompressionMethod Compression + { + get + { + return _compression; + } - if (protocols != null && protocols.Length > 0) { - if (!checkProtocols (protocols, out msg)) - throw new ArgumentException (msg, "protocols"); + set + { + string msg = null; + + if (!_client) + { + msg = "This instance is not a client."; + throw new InvalidOperationException(msg); + } + + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } + + lock (_forState) + { + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } + + _compression = value; + } + } + } - _protocols = protocols; - } + /// + /// Gets the HTTP cookies included in the handshake request/response. + /// + public IEnumerable Cookies + { + get + { + lock (_cookies.SyncRoot) + { + foreach (Cookie cookie in _cookies) + yield return cookie; + } + } + } - _base64Key = CreateBase64Key (); - _client = true; - _logger = new Logger (); - _message = messagec; - _secure = _uri.Scheme == "wss"; - _waitTime = TimeSpan.FromSeconds (5); + /// + /// Gets the credentials for the HTTP authentication (Basic/Digest). + /// + public NetworkCredential Credentials + { + get + { + return _credentials; + } + } - init (); - } + /// + /// Gets or sets a value indicating whether a event + /// is emitted when a ping is received. + /// + public bool EmitOnPing + { + get + { + return _emitOnPing; + } - #endregion + set + { + _emitOnPing = value; + } + } - #region Internal Properties + /// + /// Gets or sets a value indicating whether the URL redirection for + /// the handshake request is allowed. + /// + public bool EnableRedirection + { + get + { + return _enableRedirection; + } - internal CookieCollection CookieCollection { - get { - return _cookies; - } - } + set + { + string msg = null; + + if (!_client) + { + msg = "This instance is not a client."; + throw new InvalidOperationException(msg); + } + + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } + + lock (_forState) + { + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } + + _enableRedirection = value; + } + } + } - // As server - internal Func CustomHandshakeRequestChecker { - get { - return _handshakeRequestChecker; - } + /// + /// Gets the extensions selected by server. + /// + public string Extensions + { + get + { + return _extensions ?? String.Empty; + } + } - set { - _handshakeRequestChecker = value; - } - } + /// + /// Gets a value indicating whether the connection is alive. + /// + public bool IsAlive + { + get + { + return ping(EmptyBytes); + } + } - internal bool HasMessage { - get { - lock (_forMessageEventQueue) - return _messageEventQueue.Count > 0; - } - } + /// + /// Gets a value indicating whether a secure connection is used. + /// + public bool IsSecure + { + get + { + return _secure; + } + } - // As server - internal bool IgnoreExtensions { - get { - return _ignoreExtensions; - } + /// + /// Gets or sets the Log + /// Gets the logging function. + /// + public Logger Log + { + get + { + return _logger; + } - set { - _ignoreExtensions = value; - } - } + internal set + { + _logger = value; + } + } - internal bool IsConnected { - get { - return _readyState == WebSocketState.Open || _readyState == WebSocketState.Closing; - } - } + /// + /// Gets or sets the value of the HTTP Origin header to send with + /// the handshake request. + /// + public string Origin + { + get + { + return _origin; + } - #endregion + set + { + string msg = null; + + if (!_client) + { + msg = "This instance is not a client."; + throw new InvalidOperationException(msg); + } + + if (!value.IsNullOrEmpty()) + { + Uri uri; + if (!Uri.TryCreate(value, UriKind.Absolute, out uri)) + { + msg = "Not an absolute URI string."; + throw new ArgumentException(msg, "value"); + } + + if (uri.Segments.Length > 1) + { + msg = "It includes the path segments."; + throw new ArgumentException(msg, "value"); + } + } + + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } + + lock (_forState) + { + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } + + _origin = !value.IsNullOrEmpty() ? value.TrimEnd('/') : value; + } + } + } - #region Public Properties + /// + /// Gets or sets the Protocol + /// Gets the name of subprotocol selected by the server. + /// + public string Protocol + { + get + { + return _protocol ?? String.Empty; + } - /// - /// Gets or sets the compression method used to compress a message. - /// - /// - /// The set operation does nothing if the connection has already been - /// established or it is closing. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It specifies the compression method used to compress a message. - /// - /// - /// The default value is . - /// - /// - /// - /// The set operation is not available if this instance is not a client. - /// - public CompressionMethod Compression { - get { - return _compression; - } - - set { - string msg = null; - - if (!_client) { - msg = "This instance is not a client."; - throw new InvalidOperationException (msg); + internal set + { + _protocol = value; + } } - if (!canSet (out msg)) { - _logger.Warn (msg); - return; + /// + /// Gets the current state of the connection. + /// + public WebSocketState ReadyState + { + get + { + return _readyState; + } } - lock (_forState) { - if (!canSet (out msg)) { - _logger.Warn (msg); - return; - } - - _compression = value; + /// + /// Gets the configuration for secure connection. + /// + public ClientSslConfiguration SslConfiguration + { + get + { + if (!_client) + { + var msg = "This instance is not a client."; + throw new InvalidOperationException(msg); + } + + if (!_secure) + { + var msg = "This instance does not use a secure connection."; + throw new InvalidOperationException(msg); + } + + return getSslConfiguration(); + } } - } - } - /// - /// Gets the HTTP cookies included in the handshake request/response. - /// - /// - /// - /// An - /// instance. - /// - /// - /// It provides an enumerator which supports the iteration over - /// the collection of the cookies. - /// - /// - public IEnumerable Cookies { - get { - lock (_cookies.SyncRoot) { - foreach (Cookie cookie in _cookies) - yield return cookie; + /// + /// Gets the URL to which to connect. + /// + public Uri Url + { + get + { + return _client ? _uri : _context.RequestUri; + } } - } - } - - /// - /// Gets the credentials for the HTTP authentication (Basic/Digest). - /// - /// - /// - /// A that represents the credentials - /// used to authenticate the client. - /// - /// - /// The default value is . - /// - /// - public NetworkCredential Credentials { - get { - return _credentials; - } - } - /// - /// Gets or sets a value indicating whether a event - /// is emitted when a ping is received. - /// - /// - /// - /// true if this instance emits a event - /// when receives a ping; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - public bool EmitOnPing { - get { - return _emitOnPing; - } - - set { - _emitOnPing = value; - } - } + /// + /// Gets or sets the time to wait for the response to the ping or close. + /// + public TimeSpan WaitTime + { + get + { + return _waitTime; + } - /// - /// Gets or sets a value indicating whether the URL redirection for - /// the handshake request is allowed. - /// - /// - /// The set operation does nothing if the connection has already been - /// established or it is closing. - /// - /// - /// - /// true if this instance allows the URL redirection for - /// the handshake request; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - /// - /// The set operation is not available if this instance is not a client. - /// - public bool EnableRedirection { - get { - return _enableRedirection; - } - - set { - string msg = null; - - if (!_client) { - msg = "This instance is not a client."; - throw new InvalidOperationException (msg); + set + { + if (value <= TimeSpan.Zero) + throw new ArgumentOutOfRangeException("value", "Zero or less."); + + string msg; + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } + + lock (_forState) + { + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } + + _waitTime = value; + } + } } - if (!canSet (out msg)) { - _logger.Warn (msg); - return; + /// + /// Gets the CookieCollection + /// + internal CookieCollection CookieCollection + { + get + { + return _cookies; + } } - lock (_forState) { - if (!canSet (out msg)) { - _logger.Warn (msg); - return; - } + // As server + /// + /// Gets or sets the CustomHandshakeRequestChecker + /// + internal Func CustomHandshakeRequestChecker + { + get + { + return _handshakeRequestChecker; + } - _enableRedirection = value; + set + { + _handshakeRequestChecker = value; + } } - } - } - - /// - /// Gets the extensions selected by server. - /// - /// - /// A that will be a list of the extensions - /// negotiated between client and server, or an empty string if - /// not specified or selected. - /// - public string Extensions { - get { - return _extensions ?? String.Empty; - } - } - - /// - /// Gets a value indicating whether the connection is alive. - /// - /// - /// The get operation returns the value by using a ping/pong - /// if the current state of the connection is Open. - /// - /// - /// true if the connection is alive; otherwise, false. - /// - public bool IsAlive { - get { - return ping (EmptyBytes); - } - } - /// - /// Gets a value indicating whether a secure connection is used. - /// - /// - /// true if this instance uses a secure connection; otherwise, - /// false. - /// - public bool IsSecure { - get { - return _secure; - } - } + /// + /// Gets a value indicating whether HasMessage + /// + internal bool HasMessage + { + get + { + lock (_forMessageEventQueue) + return _messageEventQueue.Count > 0; + } + } - /// - /// Gets the logging function. - /// - /// - /// The default logging level is . - /// - /// - /// A that provides the logging function. - /// - public Logger Log { - get { - return _logger; - } - - internal set { - _logger = value; - } - } + // As server + /// + /// Gets or sets a value indicating whether IgnoreExtensions + /// + internal bool IgnoreExtensions + { + get + { + return _ignoreExtensions; + } - /// - /// Gets or sets the value of the HTTP Origin header to send with - /// the handshake request. - /// - /// - /// - /// The HTTP Origin header is defined in - /// - /// Section 7 of RFC 6454. - /// - /// - /// This instance sends the Origin header if this property has any. - /// - /// - /// The set operation does nothing if the connection has already been - /// established or it is closing. - /// - /// - /// - /// - /// A that represents the value of the Origin - /// header to send. - /// - /// - /// The syntax is <scheme>://<host>[:<port>]. - /// - /// - /// The default value is . - /// - /// - /// - /// The set operation is not available if this instance is not a client. - /// - /// - /// - /// The value specified for a set operation is not an absolute URI string. - /// - /// - /// -or- - /// - /// - /// The value specified for a set operation includes the path segments. - /// - /// - public string Origin { - get { - return _origin; - } - - set { - string msg = null; - - if (!_client) { - msg = "This instance is not a client."; - throw new InvalidOperationException (msg); + set + { + _ignoreExtensions = value; + } } - if (!value.IsNullOrEmpty ()) { - Uri uri; - if (!Uri.TryCreate (value, UriKind.Absolute, out uri)) { - msg = "Not an absolute URI string."; - throw new ArgumentException (msg, "value"); - } - - if (uri.Segments.Length > 1) { - msg = "It includes the path segments."; - throw new ArgumentException (msg, "value"); - } + /// + /// Gets a value indicating whether IsConnected + /// + internal bool IsConnected + { + get + { + return _readyState == WebSocketState.Open || _readyState == WebSocketState.Closing; + } } - if (!canSet (out msg)) { - _logger.Warn (msg); - return; - } + #endregion - lock (_forState) { - if (!canSet (out msg)) { - _logger.Warn (msg); - return; - } + #region - _origin = !value.IsNullOrEmpty () ? value.TrimEnd ('/') : value; - } - } - } + /// + /// Accepts the handshake request. + /// + public void Accept() + { + if (_client) + { + var msg = "This instance is a client."; + throw new InvalidOperationException(msg); + } - /// - /// Gets the name of subprotocol selected by the server. - /// - /// - /// - /// A that will be one of the names of - /// subprotocols specified by client. - /// - /// - /// An empty string if not specified or selected. - /// - /// - public string Protocol { - get { - return _protocol ?? String.Empty; - } - - internal set { - _protocol = value; - } - } + if (_readyState == WebSocketState.Closing) + { + var msg = "The close process is in progress."; + throw new InvalidOperationException(msg); + } - /// - /// Gets the current state of the connection. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It indicates the current state of the connection. - /// - /// - /// The default value is . - /// - /// - public WebSocketState ReadyState { - get { - return _readyState; - } - } + if (_readyState == WebSocketState.Closed) + { + var msg = "The connection has already been closed."; + throw new InvalidOperationException(msg); + } - /// - /// Gets the configuration for secure connection. - /// - /// - /// This configuration will be referenced when attempts to connect, - /// so it must be configured before any connect method is called. - /// - /// - /// A that represents - /// the configuration used to establish a secure connection. - /// - /// - /// - /// This instance is not a client. - /// - /// - /// This instance does not use a secure connection. - /// - /// - public ClientSslConfiguration SslConfiguration { - get { - if (!_client) { - var msg = "This instance is not a client."; - throw new InvalidOperationException (msg); + if (accept()) + open(); } - if (!_secure) { - var msg = "This instance does not use a secure connection."; - throw new InvalidOperationException (msg); - } + /// + /// Accepts the handshake request asynchronously. + /// + public void AcceptAsync() + { + if (_client) + { + var msg = "This instance is a client."; + throw new InvalidOperationException(msg); + } - return getSslConfiguration (); - } - } + if (_readyState == WebSocketState.Closing) + { + var msg = "The close process is in progress."; + throw new InvalidOperationException(msg); + } - /// - /// Gets the URL to which to connect. - /// - /// - /// A that represents the URL to which to connect. - /// - public Uri Url { - get { - return _client ? _uri : _context.RequestUri; - } - } + if (_readyState == WebSocketState.Closed) + { + var msg = "The connection has already been closed."; + throw new InvalidOperationException(msg); + } - /// - /// Gets or sets the time to wait for the response to the ping or close. - /// - /// - /// The set operation does nothing if the connection has already been - /// established or it is closing. - /// - /// - /// - /// A to wait for the response. - /// - /// - /// The default value is the same as 5 seconds if this instance is - /// a client. - /// - /// - /// - /// The value specified for a set operation is zero or less. - /// - public TimeSpan WaitTime { - get { - return _waitTime; - } - - set { - if (value <= TimeSpan.Zero) - throw new ArgumentOutOfRangeException ("value", "Zero or less."); - - string msg; - if (!canSet (out msg)) { - _logger.Warn (msg); - return; + Func acceptor = accept; + acceptor.BeginInvoke( + ar => + { + if (acceptor.EndInvoke(ar)) + open(); + }, + null + ); } - lock (_forState) { - if (!canSet (out msg)) { - _logger.Warn (msg); - return; - } - - _waitTime = value; - } - } - } + /// + /// Closes the connection. + /// + public void Close() + { + close(1005, String.Empty); + } + + /// + /// Closes the connection with the specified . + /// + /// The code + public void Close(CloseStatusCode code) + { + if (_client && code == CloseStatusCode.ServerError) + { + var msg = "ServerError cannot be used."; + throw new ArgumentException(msg, "code"); + } - #endregion + if (!_client && code == CloseStatusCode.MandatoryExtension) + { + var msg = "MandatoryExtension cannot be used."; + throw new ArgumentException(msg, "code"); + } - #region Public Events + close((ushort)code, String.Empty); + } + + /// + /// Closes the connection with the specified and + /// . + /// + /// The code + /// The reason + public void Close(CloseStatusCode code, string reason) + { + if (_client && code == CloseStatusCode.ServerError) + { + var msg = "ServerError cannot be used."; + throw new ArgumentException(msg, "code"); + } - /// - /// Occurs when the WebSocket connection has been closed. - /// - public event EventHandler OnClose; + if (!_client && code == CloseStatusCode.MandatoryExtension) + { + var msg = "MandatoryExtension cannot be used."; + throw new ArgumentException(msg, "code"); + } - /// - /// Occurs when the gets an error. - /// - public event EventHandler OnError; + if (reason.IsNullOrEmpty()) + { + close((ushort)code, String.Empty); + return; + } - /// - /// Occurs when the receives a message. - /// - public event EventHandler OnMessage; + if (code == CloseStatusCode.NoStatus) + { + var msg = "NoStatus cannot be used."; + throw new ArgumentException(msg, "code"); + } - /// - /// Occurs when the WebSocket connection has been established. - /// - public event EventHandler OnOpen; + byte[] bytes; + if (!reason.TryGetUTF8EncodedBytes(out bytes)) + { + var msg = "It could not be UTF-8-encoded."; + throw new ArgumentException(msg, "reason"); + } - #endregion + if (bytes.Length > 123) + { + var msg = "Its size is greater than 123 bytes."; + throw new ArgumentOutOfRangeException("reason", msg); + } - #region Private Methods + close((ushort)code, reason); + } - // As server - private bool accept () - { - if (_readyState == WebSocketState.Open) { - var msg = "The handshake request has already been accepted."; - _logger.Warn (msg); + /// + /// Closes the connection with the specified . + /// + /// The code + public void Close(ushort code) + { + if (!code.IsCloseStatusCode()) + { + var msg = "Less than 1000 or greater than 4999."; + throw new ArgumentOutOfRangeException("code", msg); + } - return false; - } + if (_client && code == 1011) + { + var msg = "1011 cannot be used."; + throw new ArgumentException(msg, "code"); + } - lock (_forState) { - if (_readyState == WebSocketState.Open) { - var msg = "The handshake request has already been accepted."; - _logger.Warn (msg); + if (!_client && code == 1010) + { + var msg = "1010 cannot be used."; + throw new ArgumentException(msg, "code"); + } - return false; - } + close(code, String.Empty); + } + + /// + /// Closes the connection with the specified and + /// . + /// + /// The code + /// The reason + public void Close(ushort code, string reason) + { + if (!code.IsCloseStatusCode()) + { + var msg = "Less than 1000 or greater than 4999."; + throw new ArgumentOutOfRangeException("code", msg); + } - if (_readyState == WebSocketState.Closing) { - var msg = "The close process has set in."; - _logger.Error (msg); + if (_client && code == 1011) + { + var msg = "1011 cannot be used."; + throw new ArgumentException(msg, "code"); + } - msg = "An interruption has occurred while attempting to accept."; - error (msg, null); + if (!_client && code == 1010) + { + var msg = "1010 cannot be used."; + throw new ArgumentException(msg, "code"); + } - return false; - } + if (reason.IsNullOrEmpty()) + { + close(code, String.Empty); + return; + } - if (_readyState == WebSocketState.Closed) { - var msg = "The connection has been closed."; - _logger.Error (msg); + if (code == 1005) + { + var msg = "1005 cannot be used."; + throw new ArgumentException(msg, "code"); + } - msg = "An interruption has occurred while attempting to accept."; - error (msg, null); + byte[] bytes; + if (!reason.TryGetUTF8EncodedBytes(out bytes)) + { + var msg = "It could not be UTF-8-encoded."; + throw new ArgumentException(msg, "reason"); + } - return false; - } + if (bytes.Length > 123) + { + var msg = "Its size is greater than 123 bytes."; + throw new ArgumentOutOfRangeException("reason", msg); + } - try { - if (!acceptHandshake ()) - return false; + close(code, reason); } - catch (Exception ex) { - _logger.Fatal (ex.Message); - _logger.Debug (ex.ToString ()); - - var msg = "An exception has occurred while attempting to accept."; - fatal (msg, ex); - return false; + /// + /// Closes the connection asynchronously. + /// + public void CloseAsync() + { + closeAsync(1005, String.Empty); } - _readyState = WebSocketState.Open; - return true; - } - } - - // As server - private bool acceptHandshake () - { - _logger.Debug ( - String.Format ( - "A handshake request from {0}:\n{1}", _context.UserEndPoint, _context - ) - ); - - string msg; - if (!checkHandshakeRequest (_context, out msg)) { - _logger.Error (msg); - - refuseHandshake ( - CloseStatusCode.ProtocolError, - "A handshake error has occurred while attempting to accept." - ); - - return false; - } + /// + /// Closes the connection asynchronously with the specified + /// . + /// + /// The code + public void CloseAsync(CloseStatusCode code) + { + if (_client && code == CloseStatusCode.ServerError) + { + var msg = "ServerError cannot be used."; + throw new ArgumentException(msg, "code"); + } - if (!customCheckHandshakeRequest (_context, out msg)) { - _logger.Error (msg); + if (!_client && code == CloseStatusCode.MandatoryExtension) + { + var msg = "MandatoryExtension cannot be used."; + throw new ArgumentException(msg, "code"); + } - refuseHandshake ( - CloseStatusCode.PolicyViolation, - "A handshake error has occurred while attempting to accept." - ); + closeAsync((ushort)code, String.Empty); + } + + /// + /// Closes the connection asynchronously with the specified + /// and . + /// + /// The code + /// The reason + public void CloseAsync(CloseStatusCode code, string reason) + { + if (_client && code == CloseStatusCode.ServerError) + { + var msg = "ServerError cannot be used."; + throw new ArgumentException(msg, "code"); + } - return false; - } + if (!_client && code == CloseStatusCode.MandatoryExtension) + { + var msg = "MandatoryExtension cannot be used."; + throw new ArgumentException(msg, "code"); + } - _base64Key = _context.Headers["Sec-WebSocket-Key"]; + if (reason.IsNullOrEmpty()) + { + closeAsync((ushort)code, String.Empty); + return; + } - if (_protocol != null) { - var vals = _context.SecWebSocketProtocols; - processSecWebSocketProtocolClientHeader (vals); - } + if (code == CloseStatusCode.NoStatus) + { + var msg = "NoStatus cannot be used."; + throw new ArgumentException(msg, "code"); + } - if (!_ignoreExtensions) { - var val = _context.Headers["Sec-WebSocket-Extensions"]; - processSecWebSocketExtensionsClientHeader (val); - } + byte[] bytes; + if (!reason.TryGetUTF8EncodedBytes(out bytes)) + { + var msg = "It could not be UTF-8-encoded."; + throw new ArgumentException(msg, "reason"); + } - return sendHttpResponse (createHandshakeResponse ()); - } + if (bytes.Length > 123) + { + var msg = "Its size is greater than 123 bytes."; + throw new ArgumentOutOfRangeException("reason", msg); + } - private bool canSet (out string message) - { - message = null; + closeAsync((ushort)code, reason); + } - if (_readyState == WebSocketState.Open) { - message = "The connection has already been established."; - return false; - } + /// + /// Closes the connection asynchronously with the specified + /// . + /// + /// The code + public void CloseAsync(ushort code) + { + if (!code.IsCloseStatusCode()) + { + var msg = "Less than 1000 or greater than 4999."; + throw new ArgumentOutOfRangeException("code", msg); + } - if (_readyState == WebSocketState.Closing) { - message = "The connection is closing."; - return false; - } + if (_client && code == 1011) + { + var msg = "1011 cannot be used."; + throw new ArgumentException(msg, "code"); + } - return true; - } + if (!_client && code == 1010) + { + var msg = "1010 cannot be used."; + throw new ArgumentException(msg, "code"); + } - // As server - private bool checkHandshakeRequest ( - WebSocketContext context, out string message - ) - { - message = null; - - if (!context.IsWebSocketRequest) { - message = "Not a handshake request."; - return false; - } - - if (context.RequestUri == null) { - message = "It specifies an invalid Request-URI."; - return false; - } - - var headers = context.Headers; - - var key = headers["Sec-WebSocket-Key"]; - if (key == null) { - message = "It includes no Sec-WebSocket-Key header."; - return false; - } - - if (key.Length == 0) { - message = "It includes an invalid Sec-WebSocket-Key header."; - return false; - } - - var version = headers["Sec-WebSocket-Version"]; - if (version == null) { - message = "It includes no Sec-WebSocket-Version header."; - return false; - } - - if (version != _version) { - message = "It includes an invalid Sec-WebSocket-Version header."; - return false; - } - - var protocol = headers["Sec-WebSocket-Protocol"]; - if (protocol != null && protocol.Length == 0) { - message = "It includes an invalid Sec-WebSocket-Protocol header."; - return false; - } - - if (!_ignoreExtensions) { - var extensions = headers["Sec-WebSocket-Extensions"]; - if (extensions != null && extensions.Length == 0) { - message = "It includes an invalid Sec-WebSocket-Extensions header."; - return false; - } - } + closeAsync(code, String.Empty); + } + + /// + /// Closes the connection asynchronously with the specified + /// and . + /// + /// The code + /// The reason + public void CloseAsync(ushort code, string reason) + { + if (!code.IsCloseStatusCode()) + { + var msg = "Less than 1000 or greater than 4999."; + throw new ArgumentOutOfRangeException("code", msg); + } - return true; - } + if (_client && code == 1011) + { + var msg = "1011 cannot be used."; + throw new ArgumentException(msg, "code"); + } - // As client - private bool checkHandshakeResponse (HttpResponse response, out string message) - { - message = null; - - if (response.IsRedirect) { - message = "Indicates the redirection."; - return false; - } - - if (response.IsUnauthorized) { - message = "Requires the authentication."; - return false; - } - - if (!response.IsWebSocketResponse) { - message = "Not a WebSocket handshake response."; - return false; - } - - var headers = response.Headers; - if (!validateSecWebSocketAcceptHeader (headers["Sec-WebSocket-Accept"])) { - message = "Includes no Sec-WebSocket-Accept header, or it has an invalid value."; - return false; - } - - if (!validateSecWebSocketProtocolServerHeader (headers["Sec-WebSocket-Protocol"])) { - message = "Includes no Sec-WebSocket-Protocol header, or it has an invalid value."; - return false; - } - - if (!validateSecWebSocketExtensionsServerHeader (headers["Sec-WebSocket-Extensions"])) { - message = "Includes an invalid Sec-WebSocket-Extensions header."; - return false; - } - - if (!validateSecWebSocketVersionServerHeader (headers["Sec-WebSocket-Version"])) { - message = "Includes an invalid Sec-WebSocket-Version header."; - return false; - } - - return true; - } + if (!_client && code == 1010) + { + var msg = "1010 cannot be used."; + throw new ArgumentException(msg, "code"); + } - private static bool checkProtocols (string[] protocols, out string message) - { - message = null; + if (reason.IsNullOrEmpty()) + { + closeAsync(code, String.Empty); + return; + } - Func cond = protocol => protocol.IsNullOrEmpty () - || !protocol.IsToken (); + if (code == 1005) + { + var msg = "1005 cannot be used."; + throw new ArgumentException(msg, "code"); + } - if (protocols.Contains (cond)) { - message = "It contains a value that is not a token."; - return false; - } + byte[] bytes; + if (!reason.TryGetUTF8EncodedBytes(out bytes)) + { + var msg = "It could not be UTF-8-encoded."; + throw new ArgumentException(msg, "reason"); + } - if (protocols.ContainsTwice ()) { - message = "It contains a value twice."; - return false; - } + if (bytes.Length > 123) + { + var msg = "Its size is greater than 123 bytes."; + throw new ArgumentOutOfRangeException("reason", msg); + } - return true; - } + closeAsync(code, reason); + } - private bool checkReceivedFrame (WebSocketFrame frame, out string message) - { - message = null; - - var masked = frame.IsMasked; - if (_client && masked) { - message = "A frame from the server is masked."; - return false; - } - - if (!_client && !masked) { - message = "A frame from a client is not masked."; - return false; - } - - if (_inContinuation && frame.IsData) { - message = "A data frame has been received while receiving continuation frames."; - return false; - } - - if (frame.IsCompressed && _compression == CompressionMethod.None) { - message = "A compressed frame has been received without any agreement for it."; - return false; - } - - if (frame.Rsv2 == Rsv.On) { - message = "The RSV2 of a frame is non-zero without any negotiation for it."; - return false; - } - - if (frame.Rsv3 == Rsv.On) { - message = "The RSV3 of a frame is non-zero without any negotiation for it."; - return false; - } - - return true; - } + /// + /// Establishes a connection. + /// + public void Connect() + { + if (!_client) + { + var msg = "This instance is not a client."; + throw new InvalidOperationException(msg); + } - private void close (ushort code, string reason) - { - if (_readyState == WebSocketState.Closing) { - _logger.Info ("The closing is already in progress."); - return; - } - - if (_readyState == WebSocketState.Closed) { - _logger.Info ("The connection has already been closed."); - return; - } - - if (code == 1005) { // == no status - close (PayloadData.Empty, true, true, false); - return; - } - - var send = !code.IsReserved (); - close (new PayloadData (code, reason), send, send, false); - } + if (_readyState == WebSocketState.Closing) + { + var msg = "The close process is in progress."; + throw new InvalidOperationException(msg); + } - private void close ( - PayloadData payloadData, bool send, bool receive, bool received - ) - { - lock (_forState) { - if (_readyState == WebSocketState.Closing) { - _logger.Info ("The closing is already in progress."); - return; - } + if (_retryCountForConnect > _maxRetryCountForConnect) + { + var msg = "A series of reconnecting has failed."; + throw new InvalidOperationException(msg); + } - if (_readyState == WebSocketState.Closed) { - _logger.Info ("The connection has already been closed."); - return; + if (connect()) + open(); } - send = send && _readyState == WebSocketState.Open; - receive = send && receive; - - _readyState = WebSocketState.Closing; - } - - _logger.Trace ("Begin closing the connection."); + /// + /// Establishes a connection asynchronously. + /// + public void ConnectAsync() + { + if (!_client) + { + var msg = "This instance is not a client."; + throw new InvalidOperationException(msg); + } - var res = closeHandshake (payloadData, send, receive, received); - releaseResources (); + if (_readyState == WebSocketState.Closing) + { + var msg = "The close process is in progress."; + throw new InvalidOperationException(msg); + } - _logger.Trace ("End closing the connection."); + if (_retryCountForConnect > _maxRetryCountForConnect) + { + var msg = "A series of reconnecting has failed."; + throw new InvalidOperationException(msg); + } - _readyState = WebSocketState.Closed; + Func connector = connect; + connector.BeginInvoke( + ar => + { + if (connector.EndInvoke(ar)) + open(); + }, + null + ); + } - var e = new CloseEventArgs (payloadData); - e.WasClean = res; + /// + /// Sends a ping using the WebSocket connection. + /// + /// The + public bool Ping() + { + return ping(EmptyBytes); + } + + /// + /// Sends a ping with using the WebSocket + /// connection. + /// + /// The message + /// The + public bool Ping(string message) + { + if (message.IsNullOrEmpty()) + return ping(EmptyBytes); + + byte[] bytes; + if (!message.TryGetUTF8EncodedBytes(out bytes)) + { + var msg = "It could not be UTF-8-encoded."; + throw new ArgumentException(msg, "message"); + } - try { - OnClose.Emit (this, e); - } - catch (Exception ex) { - _logger.Error (ex.ToString ()); - error ("An error has occurred during the OnClose event.", ex); - } - } + if (bytes.Length > 125) + { + var msg = "Its size is greater than 125 bytes."; + throw new ArgumentOutOfRangeException("message", msg); + } - private void closeAsync (ushort code, string reason) - { - if (_readyState == WebSocketState.Closing) { - _logger.Info ("The closing is already in progress."); - return; - } - - if (_readyState == WebSocketState.Closed) { - _logger.Info ("The connection has already been closed."); - return; - } - - if (code == 1005) { // == no status - closeAsync (PayloadData.Empty, true, true, false); - return; - } - - var send = !code.IsReserved (); - closeAsync (new PayloadData (code, reason), send, send, false); - } + return ping(bytes); + } - private void closeAsync ( - PayloadData payloadData, bool send, bool receive, bool received - ) - { - Action closer = close; - closer.BeginInvoke ( - payloadData, send, receive, received, ar => closer.EndInvoke (ar), null - ); - } + /// + /// Sends the specified data using the WebSocket connection. + /// + /// The data + public void Send(byte[] data) + { + if (_readyState != WebSocketState.Open) + { + var msg = "The current state of the connection is not Open."; + throw new InvalidOperationException(msg); + } - private bool closeHandshake (byte[] frameAsBytes, bool receive, bool received) - { - var sent = frameAsBytes != null && sendBytes (frameAsBytes); + if (data == null) + throw new ArgumentNullException("data"); - var wait = !received && sent && receive && _receivingExited != null; - if (wait) - received = _receivingExited.WaitOne (_waitTime); + send(Opcode.Binary, new MemoryStream(data)); + } - var ret = sent && received; + /// + /// Sends the specified file using the WebSocket connection. + /// + /// The fileInfo + public void Send(FileInfo fileInfo) + { + if (_readyState != WebSocketState.Open) + { + var msg = "The current state of the connection is not Open."; + throw new InvalidOperationException(msg); + } - _logger.Debug ( - String.Format ( - "Was clean?: {0}\n sent: {1}\n received: {2}", ret, sent, received - ) - ); + if (fileInfo == null) + throw new ArgumentNullException("fileInfo"); - return ret; - } + if (!fileInfo.Exists) + { + var msg = "The file does not exist."; + throw new ArgumentException(msg, "fileInfo"); + } - private bool closeHandshake ( - PayloadData payloadData, bool send, bool receive, bool received - ) - { - var sent = false; - if (send) { - var frame = WebSocketFrame.CreateCloseFrame (payloadData, _client); - sent = sendBytes (frame.ToArray ()); + FileStream stream; + if (!fileInfo.TryOpenRead(out stream)) + { + var msg = "The file could not be opened."; + throw new ArgumentException(msg, "fileInfo"); + } - if (_client) - frame.Unmask (); - } + send(Opcode.Binary, stream); + } - var wait = !received && sent && receive && _receivingExited != null; - if (wait) - received = _receivingExited.WaitOne (_waitTime); + /// + /// Sends the data from the specified stream using the WebSocket connection. + /// + /// The stream + /// The length + public void Send(Stream stream, int length) + { + if (_readyState != WebSocketState.Open) + { + var msg = "The current state of the connection is not Open."; + throw new InvalidOperationException(msg); + } - var ret = sent && received; + if (stream == null) + throw new ArgumentNullException("stream"); - _logger.Debug ( - String.Format ( - "Was clean?: {0}\n sent: {1}\n received: {2}", ret, sent, received - ) - ); + if (!stream.CanRead) + { + var msg = "It cannot be read."; + throw new ArgumentException(msg, "stream"); + } - return ret; - } + if (length < 1) + { + var msg = "Less than 1."; + throw new ArgumentException(msg, "length"); + } - // As client - private bool connect () - { - if (_readyState == WebSocketState.Open) { - var msg = "The connection has already been established."; - _logger.Warn (msg); + var bytes = stream.ReadBytes(length); - return false; - } + var len = bytes.Length; + if (len == 0) + { + var msg = "No data could be read from it."; + throw new ArgumentException(msg, "stream"); + } - lock (_forState) { - if (_readyState == WebSocketState.Open) { - var msg = "The connection has already been established."; - _logger.Warn (msg); + if (len < length) + { + _logger.Warn( + String.Format( + "Only {0} byte(s) of data could be read from the stream.", + len + ) + ); + } - return false; + send(Opcode.Binary, new MemoryStream(bytes)); } - if (_readyState == WebSocketState.Closing) { - var msg = "The close process has set in."; - _logger.Error (msg); + /// + /// Sends the specified data using the WebSocket connection. + /// + /// The data + public void Send(string data) + { + if (_readyState != WebSocketState.Open) + { + var msg = "The current state of the connection is not Open."; + throw new InvalidOperationException(msg); + } - msg = "An interruption has occurred while attempting to connect."; - error (msg, null); + if (data == null) + throw new ArgumentNullException("data"); + + byte[] bytes; + if (!data.TryGetUTF8EncodedBytes(out bytes)) + { + var msg = "It could not be UTF-8-encoded."; + throw new ArgumentException(msg, "data"); + } - return false; + send(Opcode.Text, new MemoryStream(bytes)); } - if (_retryCountForConnect > _maxRetryCountForConnect) { - var msg = "An opportunity for reconnecting has been lost."; - _logger.Error (msg); + /// + /// Sends the specified data asynchronously using the WebSocket connection. + /// + /// The data + /// The completed + public void SendAsync(byte[] data, Action completed) + { + if (_readyState != WebSocketState.Open) + { + var msg = "The current state of the connection is not Open."; + throw new InvalidOperationException(msg); + } - msg = "An interruption has occurred while attempting to connect."; - error (msg, null); + if (data == null) + throw new ArgumentNullException("data"); - return false; + sendAsync(Opcode.Binary, new MemoryStream(data), completed); } - _readyState = WebSocketState.Connecting; + /// + /// Sends the specified file asynchronously using the WebSocket connection. + /// + /// The fileInfo + /// The completed + public void SendAsync(FileInfo fileInfo, Action completed) + { + if (_readyState != WebSocketState.Open) + { + var msg = "The current state of the connection is not Open."; + throw new InvalidOperationException(msg); + } - try { - doHandshake (); - } - catch (Exception ex) { - _retryCountForConnect++; + if (fileInfo == null) + throw new ArgumentNullException("fileInfo"); - _logger.Fatal (ex.Message); - _logger.Debug (ex.ToString ()); + if (!fileInfo.Exists) + { + var msg = "The file does not exist."; + throw new ArgumentException(msg, "fileInfo"); + } - var msg = "An exception has occurred while attempting to connect."; - fatal (msg, ex); + FileStream stream; + if (!fileInfo.TryOpenRead(out stream)) + { + var msg = "The file could not be opened."; + throw new ArgumentException(msg, "fileInfo"); + } - return false; + sendAsync(Opcode.Binary, stream, completed); } - _retryCountForConnect = 1; - _readyState = WebSocketState.Open; - - return true; - } - } + /// + /// Sends the data from the specified stream asynchronously using + /// the WebSocket connection. + /// + /// The stream + /// The length + /// The completed + public void SendAsync(Stream stream, int length, Action completed) + { + if (_readyState != WebSocketState.Open) + { + var msg = "The current state of the connection is not Open."; + throw new InvalidOperationException(msg); + } - // As client - private string createExtensions () - { - var buff = new StringBuilder (80); + if (stream == null) + throw new ArgumentNullException("stream"); - if (_compression != CompressionMethod.None) { - var str = _compression.ToExtensionString ( - "server_no_context_takeover", "client_no_context_takeover"); + if (!stream.CanRead) + { + var msg = "It cannot be read."; + throw new ArgumentException(msg, "stream"); + } - buff.AppendFormat ("{0}, ", str); - } + if (length < 1) + { + var msg = "Less than 1."; + throw new ArgumentException(msg, "length"); + } - var len = buff.Length; - if (len > 2) { - buff.Length = len - 2; - return buff.ToString (); - } + var bytes = stream.ReadBytes(length); - return null; - } + var len = bytes.Length; + if (len == 0) + { + var msg = "No data could be read from it."; + throw new ArgumentException(msg, "stream"); + } - // As server - private HttpResponse createHandshakeFailureResponse (HttpStatusCode code) - { - var ret = HttpResponse.CreateCloseResponse (code); - ret.Headers["Sec-WebSocket-Version"] = _version; + if (len < length) + { + _logger.Warn( + String.Format( + "Only {0} byte(s) of data could be read from the stream.", + len + ) + ); + } - return ret; - } + sendAsync(Opcode.Binary, new MemoryStream(bytes), completed); + } - // As client - private HttpRequest createHandshakeRequest () - { - var ret = HttpRequest.CreateWebSocketRequest (_uri); + /// + /// Sends the specified data asynchronously using the WebSocket connection. + /// + /// The data + /// The completed + public void SendAsync(string data, Action completed) + { + if (_readyState != WebSocketState.Open) + { + var msg = "The current state of the connection is not Open."; + throw new InvalidOperationException(msg); + } - var headers = ret.Headers; - if (!_origin.IsNullOrEmpty ()) - headers["Origin"] = _origin; + if (data == null) + throw new ArgumentNullException("data"); - headers["Sec-WebSocket-Key"] = _base64Key; + byte[] bytes; + if (!data.TryGetUTF8EncodedBytes(out bytes)) + { + var msg = "It could not be UTF-8-encoded."; + throw new ArgumentException(msg, "data"); + } - _protocolsRequested = _protocols != null; - if (_protocolsRequested) - headers["Sec-WebSocket-Protocol"] = _protocols.ToString (", "); + sendAsync(Opcode.Text, new MemoryStream(bytes), completed); + } - _extensionsRequested = _compression != CompressionMethod.None; - if (_extensionsRequested) - headers["Sec-WebSocket-Extensions"] = createExtensions (); + /// + /// Sets an HTTP cookie to send with the handshake request. + /// + /// The cookie + public void SetCookie(Cookie cookie) + { + string msg = null; - headers["Sec-WebSocket-Version"] = _version; + if (!_client) + { + msg = "This instance is not a client."; + throw new InvalidOperationException(msg); + } - AuthenticationResponse authRes = null; - if (_authChallenge != null && _credentials != null) { - authRes = new AuthenticationResponse (_authChallenge, _credentials, _nonceCount); - _nonceCount = authRes.NonceCount; - } - else if (_preAuth) { - authRes = new AuthenticationResponse (_credentials); - } + if (cookie == null) + throw new ArgumentNullException("cookie"); - if (authRes != null) - headers["Authorization"] = authRes.ToString (); + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } - if (_cookies.Count > 0) - ret.SetCookies (_cookies); + lock (_forState) + { + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } - return ret; - } + lock (_cookies.SyncRoot) + _cookies.SetOrRemove(cookie); + } + } - // As server - private HttpResponse createHandshakeResponse () - { - var ret = HttpResponse.CreateWebSocketResponse (); + /// + /// Sets the credentials for the HTTP authentication (Basic/Digest). + /// + /// The username + /// The password + /// The preAuth + public void SetCredentials(string username, string password, bool preAuth) + { + string msg = null; + + if (!_client) + { + msg = "This instance is not a client."; + throw new InvalidOperationException(msg); + } - var headers = ret.Headers; - headers["Sec-WebSocket-Accept"] = CreateResponseKey (_base64Key); + if (!username.IsNullOrEmpty()) + { + if (username.Contains(':') || !username.IsText()) + { + msg = "It contains an invalid character."; + throw new ArgumentException(msg, "username"); + } + } - if (_protocol != null) - headers["Sec-WebSocket-Protocol"] = _protocol; + if (!password.IsNullOrEmpty()) + { + if (!password.IsText()) + { + msg = "It contains an invalid character."; + throw new ArgumentException(msg, "password"); + } + } - if (_extensions != null) - headers["Sec-WebSocket-Extensions"] = _extensions; + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } - if (_cookies.Count > 0) - ret.SetCookies (_cookies); + lock (_forState) + { + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } - return ret; - } + if (username.IsNullOrEmpty()) + { + _credentials = null; + _preAuth = false; - // As server - private bool customCheckHandshakeRequest ( - WebSocketContext context, out string message - ) - { - message = null; + return; + } - if (_handshakeRequestChecker == null) - return true; + _credentials = new NetworkCredential( + username, password, _uri.PathAndQuery + ); - message = _handshakeRequestChecker (context); - return message == null; - } + _preAuth = preAuth; + } + } - private MessageEventArgs dequeueFromMessageEventQueue () - { - lock (_forMessageEventQueue) - return _messageEventQueue.Count > 0 ? _messageEventQueue.Dequeue () : null; - } + /// + /// Sets the URL of the HTTP proxy server through which to connect and + /// the credentials for the HTTP proxy authentication (Basic/Digest). + /// + /// The url + /// The username + /// The password + public void SetProxy(string url, string username, string password) + { + string msg = null; + + if (!_client) + { + msg = "This instance is not a client."; + throw new InvalidOperationException(msg); + } - // As client - private void doHandshake () - { - setClientStream (); - var res = sendHandshakeRequest (); + Uri uri = null; + + if (!url.IsNullOrEmpty()) + { + if (!Uri.TryCreate(url, UriKind.Absolute, out uri)) + { + msg = "Not an absolute URI string."; + throw new ArgumentException(msg, "url"); + } + + if (uri.Scheme != "http") + { + msg = "The scheme part is not http."; + throw new ArgumentException(msg, "url"); + } + + if (uri.Segments.Length > 1) + { + msg = "It includes the path segments."; + throw new ArgumentException(msg, "url"); + } + } - string msg; - if (!checkHandshakeResponse (res, out msg)) - throw new WebSocketException (CloseStatusCode.ProtocolError, msg); + if (!username.IsNullOrEmpty()) + { + if (username.Contains(':') || !username.IsText()) + { + msg = "It contains an invalid character."; + throw new ArgumentException(msg, "username"); + } + } - if (_protocolsRequested) - _protocol = res.Headers["Sec-WebSocket-Protocol"]; + if (!password.IsNullOrEmpty()) + { + if (!password.IsText()) + { + msg = "It contains an invalid character."; + throw new ArgumentException(msg, "password"); + } + } - if (_extensionsRequested) - processSecWebSocketExtensionsServerHeader (res.Headers["Sec-WebSocket-Extensions"]); + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } - processCookies (res.Cookies); - } + lock (_forState) + { + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } + + if (url.IsNullOrEmpty()) + { + _proxyUri = null; + _proxyCredentials = null; + + return; + } + + _proxyUri = uri; + _proxyCredentials = !username.IsNullOrEmpty() + ? new NetworkCredential( + username, + password, + String.Format( + "{0}:{1}", _uri.DnsSafeHost, _uri.Port + ) + ) + : null; + } + } - private void enqueueToMessageEventQueue (MessageEventArgs e) - { - lock (_forMessageEventQueue) - _messageEventQueue.Enqueue (e); - } + // As client + /// + /// The CreateBase64Key + /// + /// The + internal static string CreateBase64Key() + { + var src = new byte[16]; + RandomNumber.GetBytes(src); + + return Convert.ToBase64String(src); + } + + /// + /// The CreateResponseKey + /// + /// The base64Key + /// The + internal static string CreateResponseKey(string base64Key) + { + var buff = new StringBuilder(base64Key, 64); + buff.Append(_guid); + SHA1 sha1 = new SHA1CryptoServiceProvider(); + var src = sha1.ComputeHash(buff.ToString().UTF8Encode()); + + return Convert.ToBase64String(src); + } + + // As server + /// + /// The Close + /// + /// The response + internal void Close(HttpResponse response) + { + _readyState = WebSocketState.Closing; + + sendHttpResponse(response); + releaseServerResources(); + + _readyState = WebSocketState.Closed; + } + + // As server + /// + /// The Close + /// + /// The code + internal void Close(HttpStatusCode code) + { + Close(createHandshakeFailureResponse(code)); + } + + // As server + /// + /// The Close + /// + /// The payloadData + /// The frameAsBytes + internal void Close(PayloadData payloadData, byte[] frameAsBytes) + { + lock (_forState) + { + if (_readyState == WebSocketState.Closing) + { + _logger.Info("The closing is already in progress."); + return; + } + + if (_readyState == WebSocketState.Closed) + { + _logger.Info("The connection has already been closed."); + return; + } + + _readyState = WebSocketState.Closing; + } - private void error (string message, Exception exception) - { - try { - OnError.Emit (this, new ErrorEventArgs (message, exception)); - } - catch (Exception ex) { - _logger.Error (ex.Message); - _logger.Debug (ex.ToString ()); - } - } + _logger.Trace("Begin closing the connection."); - private void fatal (string message, Exception exception) - { - var code = exception is WebSocketException - ? ((WebSocketException) exception).Code - : CloseStatusCode.Abnormal; + var sent = frameAsBytes != null && sendBytes(frameAsBytes); + var received = sent && _receivingExited != null + ? _receivingExited.WaitOne(_waitTime) + : false; - fatal (message, (ushort) code); - } + var res = sent && received; - private void fatal (string message, ushort code) - { - var payload = new PayloadData (code, message); - close (payload, !code.IsReserved (), false, false); - } + _logger.Debug( + String.Format( + "Was clean?: {0}\n sent: {1}\n received: {2}", res, sent, received + ) + ); - private void fatal (string message, CloseStatusCode code) - { - fatal (message, (ushort) code); - } + releaseServerResources(); + releaseCommonResources(); - private ClientSslConfiguration getSslConfiguration () - { - if (_sslConfig == null) - _sslConfig = new ClientSslConfiguration (_uri.DnsSafeHost); + _logger.Trace("End closing the connection."); - return _sslConfig; - } + _readyState = WebSocketState.Closed; - private void init () - { - _compression = CompressionMethod.None; - _cookies = new CookieCollection (); - _forPing = new object (); - _forSend = new object (); - _forState = new object (); - _messageEventQueue = new Queue (); - _forMessageEventQueue = ((ICollection) _messageEventQueue).SyncRoot; - _readyState = WebSocketState.Connecting; - } + var e = new CloseEventArgs(payloadData); + e.WasClean = res; - private void message () - { - MessageEventArgs e = null; - lock (_forMessageEventQueue) { - if (_inMessage || _messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) - return; + try + { + OnClose.Emit(this, e); + } + catch (Exception ex) + { + _logger.Error(ex.ToString()); + } + } - _inMessage = true; - e = _messageEventQueue.Dequeue (); - } + // As server + /// + /// The InternalAccept + /// + internal void InternalAccept() + { + try + { + if (!acceptHandshake()) + return; + } + catch (Exception ex) + { + _logger.Fatal(ex.Message); + _logger.Debug(ex.ToString()); - _message (e); - } + var msg = "An exception has occurred while attempting to accept."; + fatal(msg, ex); - private void messagec (MessageEventArgs e) - { - do { - try { - OnMessage.Emit (this, e); - } - catch (Exception ex) { - _logger.Error (ex.ToString ()); - error ("An error has occurred during an OnMessage event.", ex); - } + return; + } - lock (_forMessageEventQueue) { - if (_messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) { - _inMessage = false; - break; - } + _readyState = WebSocketState.Open; - e = _messageEventQueue.Dequeue (); + open(); } - } - while (true); - } - private void messages (MessageEventArgs e) - { - try { - OnMessage.Emit (this, e); - } - catch (Exception ex) { - _logger.Error (ex.ToString ()); - error ("An error has occurred during an OnMessage event.", ex); - } - - lock (_forMessageEventQueue) { - if (_messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) { - _inMessage = false; - return; + // As server + /// + /// The Ping + /// + /// The frameAsBytes + /// The timeout + /// The + internal bool Ping(byte[] frameAsBytes, TimeSpan timeout) + { + if (_readyState != WebSocketState.Open) + return false; + + var pongReceived = _pongReceived; + if (pongReceived == null) + return false; + + lock (_forPing) + { + try + { + pongReceived.Reset(); + + lock (_forState) + { + if (_readyState != WebSocketState.Open) + return false; + + if (!sendBytes(frameAsBytes)) + return false; + } + + return pongReceived.WaitOne(timeout); + } + catch (ObjectDisposedException) + { + return false; + } + } } - e = _messageEventQueue.Dequeue (); - } + // As server + /// + /// The Send + /// + /// The opcode + /// The data + /// The cache + internal void Send( + Opcode opcode, byte[] data, Dictionary cache + ) + { + lock (_forSend) + { + lock (_forState) + { + if (_readyState != WebSocketState.Open) + { + _logger.Error("The connection is closing."); + return; + } + + byte[] found; + if (!cache.TryGetValue(_compression, out found)) + { + found = new WebSocketFrame( + Fin.Final, + opcode, + data.Compress(_compression), + _compression != CompressionMethod.None, + false + ) + .ToArray(); - ThreadPool.QueueUserWorkItem (state => messages (e)); - } + cache.Add(_compression, found); + } - private void open () - { - _inMessage = true; - startReceiving (); - try { - OnOpen.Emit (this, EventArgs.Empty); - } - catch (Exception ex) { - _logger.Error (ex.ToString ()); - error ("An error has occurred during the OnOpen event.", ex); - } - - MessageEventArgs e = null; - lock (_forMessageEventQueue) { - if (_messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) { - _inMessage = false; - return; + sendBytes(found); + } + } } - e = _messageEventQueue.Dequeue (); - } - - _message.BeginInvoke (e, ar => _message.EndInvoke (ar), null); - } - - private bool ping (byte[] data) - { - if (_readyState != WebSocketState.Open) - return false; - - var pongReceived = _pongReceived; - if (pongReceived == null) - return false; - - lock (_forPing) { - try { - pongReceived.Reset (); - if (!send (Fin.Final, Opcode.Ping, data, false)) - return false; - - return pongReceived.WaitOne (_waitTime); - } - catch (ObjectDisposedException) { - return false; + // As server + /// + /// The Send + /// + /// The opcode + /// The stream + /// The cache + internal void Send( + Opcode opcode, Stream stream, Dictionary cache + ) + { + lock (_forSend) + { + Stream found; + if (!cache.TryGetValue(_compression, out found)) + { + found = stream.Compress(_compression); + cache.Add(_compression, found); + } + else + { + found.Position = 0; + } + + send(opcode, found, _compression != CompressionMethod.None); + } } - } - } - private bool processCloseFrame (WebSocketFrame frame) - { - var payload = frame.PayloadData; - close (payload, !payload.HasReservedCode, false, true); + /// + /// The checkProtocols + /// + /// The protocols + /// The message + /// The + private static bool checkProtocols(string[] protocols, out string message) + { + message = null; - return false; - } + Func cond = protocol => protocol.IsNullOrEmpty() + || !protocol.IsToken(); - // As client - private void processCookies (CookieCollection cookies) - { - if (cookies.Count == 0) - return; + if (protocols.Contains(cond)) + { + message = "It contains a value that is not a token."; + return false; + } - _cookies.SetOrRemove (cookies); - } + if (protocols.ContainsTwice()) + { + message = "It contains a value twice."; + return false; + } - private bool processDataFrame (WebSocketFrame frame) - { - enqueueToMessageEventQueue ( - frame.IsCompressed - ? new MessageEventArgs ( - frame.Opcode, frame.PayloadData.ApplicationData.Decompress (_compression)) - : new MessageEventArgs (frame)); + return true; + } - return true; - } + // As server + /// + /// The accept + /// + /// The + private bool accept() + { + if (_readyState == WebSocketState.Open) + { + var msg = "The handshake request has already been accepted."; + _logger.Warn(msg); - private bool processFragmentFrame (WebSocketFrame frame) - { - if (!_inContinuation) { - // Must process first fragment. - if (frame.IsContinuation) - return true; - - _fragmentsOpcode = frame.Opcode; - _fragmentsCompressed = frame.IsCompressed; - _fragmentsBuffer = new MemoryStream (); - _inContinuation = true; - } - - _fragmentsBuffer.WriteBytes (frame.PayloadData.ApplicationData, 1024); - if (frame.IsFinal) { - using (_fragmentsBuffer) { - var data = _fragmentsCompressed - ? _fragmentsBuffer.DecompressToArray (_compression) - : _fragmentsBuffer.ToArray (); - - enqueueToMessageEventQueue (new MessageEventArgs (_fragmentsOpcode, data)); - } + return false; + } - _fragmentsBuffer = null; - _inContinuation = false; - } + lock (_forState) + { + if (_readyState == WebSocketState.Open) + { + var msg = "The handshake request has already been accepted."; + _logger.Warn(msg); - return true; - } + return false; + } - private bool processPingFrame (WebSocketFrame frame) - { - _logger.Trace ("A ping was received."); + if (_readyState == WebSocketState.Closing) + { + var msg = "The close process has set in."; + _logger.Error(msg); - var pong = WebSocketFrame.CreatePongFrame (frame.PayloadData, _client); + msg = "An interruption has occurred while attempting to accept."; + error(msg, null); - lock (_forState) { - if (_readyState != WebSocketState.Open) { - _logger.Error ("The connection is closing."); - return true; - } + return false; + } - if (!sendBytes (pong.ToArray ())) - return false; - } + if (_readyState == WebSocketState.Closed) + { + var msg = "The connection has been closed."; + _logger.Error(msg); - _logger.Trace ("A pong to this ping has been sent."); + msg = "An interruption has occurred while attempting to accept."; + error(msg, null); - if (_emitOnPing) { - if (_client) - pong.Unmask (); + return false; + } - enqueueToMessageEventQueue (new MessageEventArgs (frame)); - } + try + { + if (!acceptHandshake()) + return false; + } + catch (Exception ex) + { + _logger.Fatal(ex.Message); + _logger.Debug(ex.ToString()); - return true; - } + var msg = "An exception has occurred while attempting to accept."; + fatal(msg, ex); - private bool processPongFrame (WebSocketFrame frame) - { - _logger.Trace ("A pong was received."); + return false; + } - try { - _pongReceived.Set (); - } - catch (NullReferenceException ex) { - _logger.Error (ex.Message); - _logger.Debug (ex.ToString ()); + _readyState = WebSocketState.Open; + return true; + } + } - return false; - } - catch (ObjectDisposedException ex) { - _logger.Error (ex.Message); - _logger.Debug (ex.ToString ()); + // As server + /// + /// The acceptHandshake + /// + /// The + private bool acceptHandshake() + { + _logger.Debug( + String.Format( + "A handshake request from {0}:\n{1}", _context.UserEndPoint, _context + ) + ); - return false; - } + string msg; + if (!checkHandshakeRequest(_context, out msg)) + { + _logger.Error(msg); - _logger.Trace ("It has been signaled."); + refuseHandshake( + CloseStatusCode.ProtocolError, + "A handshake error has occurred while attempting to accept." + ); - return true; - } + return false; + } - private bool processReceivedFrame (WebSocketFrame frame) - { - string msg; - if (!checkReceivedFrame (frame, out msg)) - throw new WebSocketException (CloseStatusCode.ProtocolError, msg); - - frame.Unmask (); - return frame.IsFragment - ? processFragmentFrame (frame) - : frame.IsData - ? processDataFrame (frame) - : frame.IsPing - ? processPingFrame (frame) - : frame.IsPong - ? processPongFrame (frame) - : frame.IsClose - ? processCloseFrame (frame) - : processUnsupportedFrame (frame); - } + if (!customCheckHandshakeRequest(_context, out msg)) + { + _logger.Error(msg); - // As server - private void processSecWebSocketExtensionsClientHeader (string value) - { - if (value == null) - return; + refuseHandshake( + CloseStatusCode.PolicyViolation, + "A handshake error has occurred while attempting to accept." + ); - var buff = new StringBuilder (80); - var comp = false; + return false; + } - foreach (var elm in value.SplitHeaderValue (',')) { - var extension = elm.Trim (); - if (extension.Length == 0) - continue; + _base64Key = _context.Headers["Sec-WebSocket-Key"]; - if (!comp) { - if (extension.IsCompressionExtension (CompressionMethod.Deflate)) { - _compression = CompressionMethod.Deflate; + if (_protocol != null) + { + var vals = _context.SecWebSocketProtocols; + processSecWebSocketProtocolClientHeader(vals); + } - buff.AppendFormat ( - "{0}, ", - _compression.ToExtensionString ( - "client_no_context_takeover", "server_no_context_takeover" - ) - ); + if (!_ignoreExtensions) + { + var val = _context.Headers["Sec-WebSocket-Extensions"]; + processSecWebSocketExtensionsClientHeader(val); + } - comp = true; - } + return sendHttpResponse(createHandshakeResponse()); } - } - var len = buff.Length; - if (len <= 2) - return; + /// + /// The canSet + /// + /// The message + /// The + private bool canSet(out string message) + { + message = null; - buff.Length = len - 2; - _extensions = buff.ToString (); - } + if (_readyState == WebSocketState.Open) + { + message = "The connection has already been established."; + return false; + } - // As client - private void processSecWebSocketExtensionsServerHeader (string value) - { - if (value == null) { - _compression = CompressionMethod.None; - return; - } + if (_readyState == WebSocketState.Closing) + { + message = "The connection is closing."; + return false; + } - _extensions = value; - } + return true; + } - // As server - private void processSecWebSocketProtocolClientHeader ( - IEnumerable values + // As server + /// + /// The checkHandshakeRequest + /// + /// The context + /// The message + /// The + private bool checkHandshakeRequest( + WebSocketContext context, out string message ) - { - if (values.Contains (val => val == _protocol)) - return; - - _protocol = null; - } + { + message = null; - private bool processUnsupportedFrame (WebSocketFrame frame) - { - _logger.Fatal ("An unsupported frame:" + frame.PrintToString (false)); - fatal ("There is no way to handle it.", CloseStatusCode.PolicyViolation); + if (!context.IsWebSocketRequest) + { + message = "Not a handshake request."; + return false; + } - return false; - } + if (context.RequestUri == null) + { + message = "It specifies an invalid Request-URI."; + return false; + } - // As server - private void refuseHandshake (CloseStatusCode code, string reason) - { - _readyState = WebSocketState.Closing; + var headers = context.Headers; - var res = createHandshakeFailureResponse (HttpStatusCode.BadRequest); - sendHttpResponse (res); + var key = headers["Sec-WebSocket-Key"]; + if (key == null) + { + message = "It includes no Sec-WebSocket-Key header."; + return false; + } - releaseServerResources (); + if (key.Length == 0) + { + message = "It includes an invalid Sec-WebSocket-Key header."; + return false; + } - _readyState = WebSocketState.Closed; + var version = headers["Sec-WebSocket-Version"]; + if (version == null) + { + message = "It includes no Sec-WebSocket-Version header."; + return false; + } - var e = new CloseEventArgs (code, reason); + if (version != _version) + { + message = "It includes an invalid Sec-WebSocket-Version header."; + return false; + } - try { - OnClose.Emit (this, e); - } - catch (Exception ex) { - _logger.Error (ex.Message); - _logger.Debug (ex.ToString ()); - } - } + var protocol = headers["Sec-WebSocket-Protocol"]; + if (protocol != null && protocol.Length == 0) + { + message = "It includes an invalid Sec-WebSocket-Protocol header."; + return false; + } - // As client - private void releaseClientResources () - { - if (_stream != null) { - _stream.Dispose (); - _stream = null; - } - - if (_tcpClient != null) { - _tcpClient.Close (); - _tcpClient = null; - } - } + if (!_ignoreExtensions) + { + var extensions = headers["Sec-WebSocket-Extensions"]; + if (extensions != null && extensions.Length == 0) + { + message = "It includes an invalid Sec-WebSocket-Extensions header."; + return false; + } + } - private void releaseCommonResources () - { - if (_fragmentsBuffer != null) { - _fragmentsBuffer.Dispose (); - _fragmentsBuffer = null; - _inContinuation = false; - } - - if (_pongReceived != null) { - _pongReceived.Close (); - _pongReceived = null; - } - - if (_receivingExited != null) { - _receivingExited.Close (); - _receivingExited = null; - } - } + return true; + } - private void releaseResources () - { - if (_client) - releaseClientResources (); - else - releaseServerResources (); + // As client + /// + /// The checkHandshakeResponse + /// + /// The response + /// The message + /// The + private bool checkHandshakeResponse(HttpResponse response, out string message) + { + message = null; - releaseCommonResources (); - } + if (response.IsRedirect) + { + message = "Indicates the redirection."; + return false; + } - // As server - private void releaseServerResources () - { - if (_closeContext == null) - return; + if (response.IsUnauthorized) + { + message = "Requires the authentication."; + return false; + } - _closeContext (); - _closeContext = null; - _stream = null; - _context = null; - } + if (!response.IsWebSocketResponse) + { + message = "Not a WebSocket handshake response."; + return false; + } - private bool send (Opcode opcode, Stream stream) - { - lock (_forSend) { - var src = stream; - var compressed = false; - var sent = false; - try { - if (_compression != CompressionMethod.None) { - stream = stream.Compress (_compression); - compressed = true; - } - - sent = send (opcode, stream, compressed); - if (!sent) - error ("A send has been interrupted.", null); - } - catch (Exception ex) { - _logger.Error (ex.ToString ()); - error ("An error has occurred during a send.", ex); - } - finally { - if (compressed) - stream.Dispose (); + var headers = response.Headers; + if (!validateSecWebSocketAcceptHeader(headers["Sec-WebSocket-Accept"])) + { + message = "Includes no Sec-WebSocket-Accept header, or it has an invalid value."; + return false; + } - src.Dispose (); - } + if (!validateSecWebSocketProtocolServerHeader(headers["Sec-WebSocket-Protocol"])) + { + message = "Includes no Sec-WebSocket-Protocol header, or it has an invalid value."; + return false; + } - return sent; - } - } + if (!validateSecWebSocketExtensionsServerHeader(headers["Sec-WebSocket-Extensions"])) + { + message = "Includes an invalid Sec-WebSocket-Extensions header."; + return false; + } - private bool send (Opcode opcode, Stream stream, bool compressed) - { - var len = stream.Length; - if (len == 0) - return send (Fin.Final, opcode, EmptyBytes, false); - - var quo = len / FragmentLength; - var rem = (int) (len % FragmentLength); - - byte[] buff = null; - if (quo == 0) { - buff = new byte[rem]; - return stream.Read (buff, 0, rem) == rem - && send (Fin.Final, opcode, buff, compressed); - } - - if (quo == 1 && rem == 0) { - buff = new byte[FragmentLength]; - return stream.Read (buff, 0, FragmentLength) == FragmentLength - && send (Fin.Final, opcode, buff, compressed); - } - - /* Send fragments */ - - // Begin - buff = new byte[FragmentLength]; - var sent = stream.Read (buff, 0, FragmentLength) == FragmentLength - && send (Fin.More, opcode, buff, compressed); - - if (!sent) - return false; - - var n = rem == 0 ? quo - 2 : quo - 1; - for (long i = 0; i < n; i++) { - sent = stream.Read (buff, 0, FragmentLength) == FragmentLength - && send (Fin.More, Opcode.Cont, buff, false); - - if (!sent) - return false; - } - - // End - if (rem == 0) - rem = FragmentLength; - else - buff = new byte[rem]; - - return stream.Read (buff, 0, rem) == rem - && send (Fin.Final, Opcode.Cont, buff, false); - } + if (!validateSecWebSocketVersionServerHeader(headers["Sec-WebSocket-Version"])) + { + message = "Includes an invalid Sec-WebSocket-Version header."; + return false; + } - private bool send (Fin fin, Opcode opcode, byte[] data, bool compressed) - { - lock (_forState) { - if (_readyState != WebSocketState.Open) { - _logger.Error ("The connection is closing."); - return false; + return true; } - var frame = new WebSocketFrame (fin, opcode, data, compressed, _client); - return sendBytes (frame.ToArray ()); - } - } + /// + /// The checkReceivedFrame + /// + /// The frame + /// The message + /// The + private bool checkReceivedFrame(WebSocketFrame frame, out string message) + { + message = null; - private void sendAsync (Opcode opcode, Stream stream, Action completed) - { - Func sender = send; - sender.BeginInvoke ( - opcode, - stream, - ar => { - try { - var sent = sender.EndInvoke (ar); - if (completed != null) - completed (sent); - } - catch (Exception ex) { - _logger.Error (ex.ToString ()); - error ( - "An error has occurred during the callback for an async send.", - ex - ); - } - }, - null - ); - } + var masked = frame.IsMasked; + if (_client && masked) + { + message = "A frame from the server is masked."; + return false; + } - private bool sendBytes (byte[] bytes) - { - try { - _stream.Write (bytes, 0, bytes.Length); - } - catch (Exception ex) { - _logger.Error (ex.Message); - _logger.Debug (ex.ToString ()); + if (!_client && !masked) + { + message = "A frame from a client is not masked."; + return false; + } - return false; - } + if (_inContinuation && frame.IsData) + { + message = "A data frame has been received while receiving continuation frames."; + return false; + } - return true; - } + if (frame.IsCompressed && _compression == CompressionMethod.None) + { + message = "A compressed frame has been received without any agreement for it."; + return false; + } - // As client - private HttpResponse sendHandshakeRequest () - { - var req = createHandshakeRequest (); - var res = sendHttpRequest (req, 90000); - if (res.IsUnauthorized) { - var chal = res.Headers["WWW-Authenticate"]; - _logger.Warn (String.Format ("Received an authentication requirement for '{0}'.", chal)); - if (chal.IsNullOrEmpty ()) { - _logger.Error ("No authentication challenge is specified."); - return res; - } + if (frame.Rsv2 == Rsv.On) + { + message = "The RSV2 of a frame is non-zero without any negotiation for it."; + return false; + } - _authChallenge = AuthenticationChallenge.Parse (chal); - if (_authChallenge == null) { - _logger.Error ("An invalid authentication challenge is specified."); - return res; - } + if (frame.Rsv3 == Rsv.On) + { + message = "The RSV3 of a frame is non-zero without any negotiation for it."; + return false; + } - if (_credentials != null && - (!_preAuth || _authChallenge.Scheme == AuthenticationSchemes.Digest)) { - if (res.HasConnectionClose) { - releaseClientResources (); - setClientStream (); - } - - var authRes = new AuthenticationResponse (_authChallenge, _credentials, _nonceCount); - _nonceCount = authRes.NonceCount; - req.Headers["Authorization"] = authRes.ToString (); - res = sendHttpRequest (req, 15000); + return true; } - } - - if (res.IsRedirect) { - var url = res.Headers["Location"]; - _logger.Warn (String.Format ("Received a redirection to '{0}'.", url)); - if (_enableRedirection) { - if (url.IsNullOrEmpty ()) { - _logger.Error ("No url to redirect is located."); - return res; - } - - Uri uri; - string msg; - if (!url.TryCreateWebSocketUri (out uri, out msg)) { - _logger.Error ("An invalid url to redirect is located: " + msg); - return res; - } - - releaseClientResources (); - _uri = uri; - _secure = uri.Scheme == "wss"; + /// + /// The close + /// + /// The payloadData + /// The send + /// The receive + /// The received + private void close( + PayloadData payloadData, bool send, bool receive, bool received + ) + { + lock (_forState) + { + if (_readyState == WebSocketState.Closing) + { + _logger.Info("The closing is already in progress."); + return; + } + + if (_readyState == WebSocketState.Closed) + { + _logger.Info("The connection has already been closed."); + return; + } + + send = send && _readyState == WebSocketState.Open; + receive = send && receive; + + _readyState = WebSocketState.Closing; + } - setClientStream (); - return sendHandshakeRequest (); - } - } + _logger.Trace("Begin closing the connection."); - return res; - } + var res = closeHandshake(payloadData, send, receive, received); + releaseResources(); - // As client - private HttpResponse sendHttpRequest (HttpRequest request, int millisecondsTimeout) - { - _logger.Debug ("A request to the server:\n" + request.ToString ()); - var res = request.GetResponse (_stream, millisecondsTimeout); - _logger.Debug ("A response to this request:\n" + res.ToString ()); + _logger.Trace("End closing the connection."); - return res; - } + _readyState = WebSocketState.Closed; - // As server - private bool sendHttpResponse (HttpResponse response) - { - _logger.Debug ( - String.Format ( - "A response to {0}:\n{1}", _context.UserEndPoint, response - ) - ); + var e = new CloseEventArgs(payloadData); + e.WasClean = res; - return sendBytes (response.ToByteArray ()); - } - - // As client - private void sendProxyConnectRequest () - { - var req = HttpRequest.CreateConnectRequest (_uri); - var res = sendHttpRequest (req, 90000); - if (res.IsProxyAuthenticationRequired) { - var chal = res.Headers["Proxy-Authenticate"]; - _logger.Warn ( - String.Format ("Received a proxy authentication requirement for '{0}'.", chal)); - - if (chal.IsNullOrEmpty ()) - throw new WebSocketException ("No proxy authentication challenge is specified."); - - var authChal = AuthenticationChallenge.Parse (chal); - if (authChal == null) - throw new WebSocketException ("An invalid proxy authentication challenge is specified."); - - if (_proxyCredentials != null) { - if (res.HasConnectionClose) { - releaseClientResources (); - _tcpClient = new TcpClient (_proxyUri.DnsSafeHost, _proxyUri.Port); - _stream = _tcpClient.GetStream (); - } - - var authRes = new AuthenticationResponse (authChal, _proxyCredentials, 0); - req.Headers["Proxy-Authorization"] = authRes.ToString (); - res = sendHttpRequest (req, 15000); + try + { + OnClose.Emit(this, e); + } + catch (Exception ex) + { + _logger.Error(ex.ToString()); + error("An error has occurred during the OnClose event.", ex); + } } - if (res.IsProxyAuthenticationRequired) - throw new WebSocketException ("A proxy authentication is required."); - } + /// + /// The close + /// + /// The code + /// The reason + private void close(ushort code, string reason) + { + if (_readyState == WebSocketState.Closing) + { + _logger.Info("The closing is already in progress."); + return; + } - if (res.StatusCode[0] != '2') - throw new WebSocketException ( - "The proxy has failed a connection to the requested host and port."); - } + if (_readyState == WebSocketState.Closed) + { + _logger.Info("The connection has already been closed."); + return; + } - // As client - private void setClientStream () - { - if (_proxyUri != null) { - _tcpClient = new TcpClient (_proxyUri.DnsSafeHost, _proxyUri.Port); - _stream = _tcpClient.GetStream (); - sendProxyConnectRequest (); - } - else { - _tcpClient = new TcpClient (_uri.DnsSafeHost, _uri.Port); - _stream = _tcpClient.GetStream (); - } - - if (_secure) { - var conf = getSslConfiguration (); - var host = conf.TargetHost; - if (host != _uri.DnsSafeHost) - throw new WebSocketException ( - CloseStatusCode.TlsHandshakeFailure, "An invalid host name is specified."); - - try { - var sslStream = new SslStream ( - _stream, - false, - conf.ServerCertificateValidationCallback, - conf.ClientCertificateSelectionCallback); - - sslStream.AuthenticateAsClient ( - host, - conf.ClientCertificates, - conf.EnabledSslProtocols, - conf.CheckCertificateRevocation); - - _stream = sslStream; + if (code == 1005) + { // == no status + close(PayloadData.Empty, true, true, false); + return; + } + + var send = !code.IsReserved(); + close(new PayloadData(code, reason), send, send, false); } - catch (Exception ex) { - throw new WebSocketException (CloseStatusCode.TlsHandshakeFailure, ex); + + /// + /// The closeAsync + /// + /// The payloadData + /// The send + /// The receive + /// The received + private void closeAsync( + PayloadData payloadData, bool send, bool receive, bool received + ) + { + Action closer = close; + closer.BeginInvoke( + payloadData, send, receive, received, ar => closer.EndInvoke(ar), null + ); } - } - } - private void startReceiving () - { - if (_messageEventQueue.Count > 0) - _messageEventQueue.Clear (); - - _pongReceived = new ManualResetEvent (false); - _receivingExited = new ManualResetEvent (false); - - Action receive = null; - receive = - () => - WebSocketFrame.ReadFrameAsync ( - _stream, - false, - frame => { - if (!processReceivedFrame (frame) || _readyState == WebSocketState.Closed) { - var exited = _receivingExited; - if (exited != null) - exited.Set (); + /// + /// The closeAsync + /// + /// The code + /// The reason + private void closeAsync(ushort code, string reason) + { + if (_readyState == WebSocketState.Closing) + { + _logger.Info("The closing is already in progress."); + return; + } + if (_readyState == WebSocketState.Closed) + { + _logger.Info("The connection has already been closed."); return; - } + } - // Receive next asap because the Ping or Close needs a response to it. - receive (); + if (code == 1005) + { // == no status + closeAsync(PayloadData.Empty, true, true, false); + return; + } + + var send = !code.IsReserved(); + closeAsync(new PayloadData(code, reason), send, send, false); + } + + /// + /// The closeHandshake + /// + /// The payloadData + /// The send + /// The receive + /// The received + /// The + private bool closeHandshake( + PayloadData payloadData, bool send, bool receive, bool received + ) + { + var sent = false; + if (send) + { + var frame = WebSocketFrame.CreateCloseFrame(payloadData, _client); + sent = sendBytes(frame.ToArray()); + + if (_client) + frame.Unmask(); + } - if (_inMessage || !HasMessage || _readyState != WebSocketState.Open) - return; + var wait = !received && sent && receive && _receivingExited != null; + if (wait) + received = _receivingExited.WaitOne(_waitTime); - message (); - }, - ex => { - _logger.Fatal (ex.ToString ()); - fatal ("An exception has occurred while receiving.", ex); - } - ); + var ret = sent && received; - receive (); - } + _logger.Debug( + String.Format( + "Was clean?: {0}\n sent: {1}\n received: {2}", ret, sent, received + ) + ); - // As client - private bool validateSecWebSocketAcceptHeader (string value) - { - return value != null && value == CreateResponseKey (_base64Key); - } + return ret; + } - // As client - private bool validateSecWebSocketExtensionsServerHeader (string value) - { - if (value == null) - return true; + /// + /// The closeHandshake + /// + /// The frameAsBytes + /// The receive + /// The received + /// The + private bool closeHandshake(byte[] frameAsBytes, bool receive, bool received) + { + var sent = frameAsBytes != null && sendBytes(frameAsBytes); - if (value.Length == 0) - return false; + var wait = !received && sent && receive && _receivingExited != null; + if (wait) + received = _receivingExited.WaitOne(_waitTime); - if (!_extensionsRequested) - return false; + var ret = sent && received; - var comp = _compression != CompressionMethod.None; - foreach (var e in value.SplitHeaderValue (',')) { - var ext = e.Trim (); - if (comp && ext.IsCompressionExtension (_compression)) { - if (!ext.Contains ("server_no_context_takeover")) { - _logger.Error ("The server hasn't sent back 'server_no_context_takeover'."); - return false; - } - - if (!ext.Contains ("client_no_context_takeover")) - _logger.Warn ("The server hasn't sent back 'client_no_context_takeover'."); - - var method = _compression.ToExtensionString (); - var invalid = - ext.SplitHeaderValue (';').Contains ( - t => { - t = t.Trim (); - return t != method - && t != "server_no_context_takeover" - && t != "client_no_context_takeover"; - } + _logger.Debug( + String.Format( + "Was clean?: {0}\n sent: {1}\n received: {2}", ret, sent, received + ) ); - if (invalid) - return false; + return ret; } - else { - return false; - } - } - return true; - } + // As client + /// + /// The connect + /// + /// The + private bool connect() + { + if (_readyState == WebSocketState.Open) + { + var msg = "The connection has already been established."; + _logger.Warn(msg); - // As client - private bool validateSecWebSocketProtocolServerHeader (string value) - { - if (value == null) - return !_protocolsRequested; + return false; + } - if (value.Length == 0) - return false; + lock (_forState) + { + if (_readyState == WebSocketState.Open) + { + var msg = "The connection has already been established."; + _logger.Warn(msg); - return _protocolsRequested && _protocols.Contains (p => p == value); - } + return false; + } - // As client - private bool validateSecWebSocketVersionServerHeader (string value) - { - return value == null || value == _version; - } + if (_readyState == WebSocketState.Closing) + { + var msg = "The close process has set in."; + _logger.Error(msg); - #endregion + msg = "An interruption has occurred while attempting to connect."; + error(msg, null); - #region Internal Methods + return false; + } - // As server - internal void Close (HttpResponse response) - { - _readyState = WebSocketState.Closing; + if (_retryCountForConnect > _maxRetryCountForConnect) + { + var msg = "An opportunity for reconnecting has been lost."; + _logger.Error(msg); - sendHttpResponse (response); - releaseServerResources (); + msg = "An interruption has occurred while attempting to connect."; + error(msg, null); - _readyState = WebSocketState.Closed; - } + return false; + } - // As server - internal void Close (HttpStatusCode code) - { - Close (createHandshakeFailureResponse (code)); - } + _readyState = WebSocketState.Connecting; - // As server - internal void Close (PayloadData payloadData, byte[] frameAsBytes) - { - lock (_forState) { - if (_readyState == WebSocketState.Closing) { - _logger.Info ("The closing is already in progress."); - return; - } + try + { + doHandshake(); + } + catch (Exception ex) + { + _retryCountForConnect++; - if (_readyState == WebSocketState.Closed) { - _logger.Info ("The connection has already been closed."); - return; - } + _logger.Fatal(ex.Message); + _logger.Debug(ex.ToString()); - _readyState = WebSocketState.Closing; - } + var msg = "An exception has occurred while attempting to connect."; + fatal(msg, ex); - _logger.Trace ("Begin closing the connection."); + return false; + } - var sent = frameAsBytes != null && sendBytes (frameAsBytes); - var received = sent && _receivingExited != null - ? _receivingExited.WaitOne (_waitTime) - : false; + _retryCountForConnect = 1; + _readyState = WebSocketState.Open; - var res = sent && received; + return true; + } + } - _logger.Debug ( - String.Format ( - "Was clean?: {0}\n sent: {1}\n received: {2}", res, sent, received - ) - ); + // As client + /// + /// The createExtensions + /// + /// The + private string createExtensions() + { + var buff = new StringBuilder(80); - releaseServerResources (); - releaseCommonResources (); + if (_compression != CompressionMethod.None) + { + var str = _compression.ToExtensionString( + "server_no_context_takeover", "client_no_context_takeover"); - _logger.Trace ("End closing the connection."); + buff.AppendFormat("{0}, ", str); + } - _readyState = WebSocketState.Closed; + var len = buff.Length; + if (len > 2) + { + buff.Length = len - 2; + return buff.ToString(); + } - var e = new CloseEventArgs (payloadData); - e.WasClean = res; + return null; + } - try { - OnClose.Emit (this, e); - } - catch (Exception ex) { - _logger.Error (ex.ToString ()); - } - } + // As server + /// + /// The createHandshakeFailureResponse + /// + /// The code + /// The + private HttpResponse createHandshakeFailureResponse(HttpStatusCode code) + { + var ret = HttpResponse.CreateCloseResponse(code); + ret.Headers["Sec-WebSocket-Version"] = _version; - // As client - internal static string CreateBase64Key () - { - var src = new byte[16]; - RandomNumber.GetBytes (src); + return ret; + } - return Convert.ToBase64String (src); - } + // As client + /// + /// The createHandshakeRequest + /// + /// The + private HttpRequest createHandshakeRequest() + { + var ret = HttpRequest.CreateWebSocketRequest(_uri); - internal static string CreateResponseKey (string base64Key) - { - var buff = new StringBuilder (base64Key, 64); - buff.Append (_guid); - SHA1 sha1 = new SHA1CryptoServiceProvider (); - var src = sha1.ComputeHash (buff.ToString ().UTF8Encode ()); + var headers = ret.Headers; + if (!_origin.IsNullOrEmpty()) + headers["Origin"] = _origin; - return Convert.ToBase64String (src); - } + headers["Sec-WebSocket-Key"] = _base64Key; - // As server - internal void InternalAccept () - { - try { - if (!acceptHandshake ()) - return; - } - catch (Exception ex) { - _logger.Fatal (ex.Message); - _logger.Debug (ex.ToString ()); + _protocolsRequested = _protocols != null; + if (_protocolsRequested) + headers["Sec-WebSocket-Protocol"] = _protocols.ToString(", "); - var msg = "An exception has occurred while attempting to accept."; - fatal (msg, ex); + _extensionsRequested = _compression != CompressionMethod.None; + if (_extensionsRequested) + headers["Sec-WebSocket-Extensions"] = createExtensions(); - return; - } + headers["Sec-WebSocket-Version"] = _version; - _readyState = WebSocketState.Open; + AuthenticationResponse authRes = null; + if (_authChallenge != null && _credentials != null) + { + authRes = new AuthenticationResponse(_authChallenge, _credentials, _nonceCount); + _nonceCount = authRes.NonceCount; + } + else if (_preAuth) + { + authRes = new AuthenticationResponse(_credentials); + } - open (); - } + if (authRes != null) + headers["Authorization"] = authRes.ToString(); - // As server - internal bool Ping (byte[] frameAsBytes, TimeSpan timeout) - { - if (_readyState != WebSocketState.Open) - return false; + if (_cookies.Count > 0) + ret.SetCookies(_cookies); - var pongReceived = _pongReceived; - if (pongReceived == null) - return false; + return ret; + } - lock (_forPing) { - try { - pongReceived.Reset (); + // As server + /// + /// The createHandshakeResponse + /// + /// The + private HttpResponse createHandshakeResponse() + { + var ret = HttpResponse.CreateWebSocketResponse(); - lock (_forState) { - if (_readyState != WebSocketState.Open) - return false; + var headers = ret.Headers; + headers["Sec-WebSocket-Accept"] = CreateResponseKey(_base64Key); - if (!sendBytes (frameAsBytes)) - return false; - } + if (_protocol != null) + headers["Sec-WebSocket-Protocol"] = _protocol; - return pongReceived.WaitOne (timeout); - } - catch (ObjectDisposedException) { - return false; - } - } - } + if (_extensions != null) + headers["Sec-WebSocket-Extensions"] = _extensions; - // As server - internal void Send ( - Opcode opcode, byte[] data, Dictionary cache - ) - { - lock (_forSend) { - lock (_forState) { - if (_readyState != WebSocketState.Open) { - _logger.Error ("The connection is closing."); - return; - } - - byte[] found; - if (!cache.TryGetValue (_compression, out found)) { - found = new WebSocketFrame ( - Fin.Final, - opcode, - data.Compress (_compression), - _compression != CompressionMethod.None, - false - ) - .ToArray (); - - cache.Add (_compression, found); - } - - sendBytes (found); + if (_cookies.Count > 0) + ret.SetCookies(_cookies); + + return ret; } - } - } - // As server - internal void Send ( - Opcode opcode, Stream stream, Dictionary cache + // As server + /// + /// The customCheckHandshakeRequest + /// + /// The context + /// The message + /// The + private bool customCheckHandshakeRequest( + WebSocketContext context, out string message ) - { - lock (_forSend) { - Stream found; - if (!cache.TryGetValue (_compression, out found)) { - found = stream.Compress (_compression); - cache.Add (_compression, found); - } - else { - found.Position = 0; + { + message = null; + + if (_handshakeRequestChecker == null) + return true; + + message = _handshakeRequestChecker(context); + return message == null; + } + + /// + /// The dequeueFromMessageEventQueue + /// + /// The + private MessageEventArgs dequeueFromMessageEventQueue() + { + lock (_forMessageEventQueue) + return _messageEventQueue.Count > 0 ? _messageEventQueue.Dequeue() : null; + } + + // As client + /// + /// The doHandshake + /// + private void doHandshake() + { + setClientStream(); + var res = sendHandshakeRequest(); + + string msg; + if (!checkHandshakeResponse(res, out msg)) + throw new WebSocketException(CloseStatusCode.ProtocolError, msg); + + if (_protocolsRequested) + _protocol = res.Headers["Sec-WebSocket-Protocol"]; + + if (_extensionsRequested) + processSecWebSocketExtensionsServerHeader(res.Headers["Sec-WebSocket-Extensions"]); + + processCookies(res.Cookies); + } + + /// + /// The enqueueToMessageEventQueue + /// + /// The e + private void enqueueToMessageEventQueue(MessageEventArgs e) + { + lock (_forMessageEventQueue) + _messageEventQueue.Enqueue(e); + } + + /// + /// The error + /// + /// The message + /// The exception + private void error(string message, Exception exception) + { + try + { + OnError.Emit(this, new ErrorEventArgs(message, exception)); + } + catch (Exception ex) + { + _logger.Error(ex.Message); + _logger.Debug(ex.ToString()); + } } - send (opcode, found, _compression != CompressionMethod.None); - } - } - - #endregion + /// + /// The fatal + /// + /// The message + /// The code + private void fatal(string message, CloseStatusCode code) + { + fatal(message, (ushort)code); + } + + /// + /// The fatal + /// + /// The message + /// The exception + private void fatal(string message, Exception exception) + { + var code = exception is WebSocketException + ? ((WebSocketException)exception).Code + : CloseStatusCode.Abnormal; + + fatal(message, (ushort)code); + } + + /// + /// The fatal + /// + /// The message + /// The code + private void fatal(string message, ushort code) + { + var payload = new PayloadData(code, message); + close(payload, !code.IsReserved(), false, false); + } + + /// + /// The getSslConfiguration + /// + /// The + private ClientSslConfiguration getSslConfiguration() + { + if (_sslConfig == null) + _sslConfig = new ClientSslConfiguration(_uri.DnsSafeHost); + + return _sslConfig; + } + + /// + /// The init + /// + private void init() + { + _compression = CompressionMethod.None; + _cookies = new CookieCollection(); + _forPing = new object(); + _forSend = new object(); + _forState = new object(); + _messageEventQueue = new Queue(); + _forMessageEventQueue = ((ICollection)_messageEventQueue).SyncRoot; + _readyState = WebSocketState.Connecting; + } + + /// + /// The message + /// + private void message() + { + MessageEventArgs e = null; + lock (_forMessageEventQueue) + { + if (_inMessage || _messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) + return; + + _inMessage = true; + e = _messageEventQueue.Dequeue(); + } - #region Public Methods + _message(e); + } + + /// + /// The messagec + /// + /// The e + private void messagec(MessageEventArgs e) + { + do + { + try + { + OnMessage.Emit(this, e); + } + catch (Exception ex) + { + _logger.Error(ex.ToString()); + error("An error has occurred during an OnMessage event.", ex); + } + + lock (_forMessageEventQueue) + { + if (_messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) + { + _inMessage = false; + break; + } + + e = _messageEventQueue.Dequeue(); + } + } + while (true); + } + + /// + /// The messages + /// + /// The e + private void messages(MessageEventArgs e) + { + try + { + OnMessage.Emit(this, e); + } + catch (Exception ex) + { + _logger.Error(ex.ToString()); + error("An error has occurred during an OnMessage event.", ex); + } - /// - /// Accepts the handshake request. - /// - /// - /// This method does nothing if the handshake request has already been - /// accepted. - /// - /// - /// - /// This instance is a client. - /// - /// - /// -or- - /// - /// - /// The close process is in progress. - /// - /// - /// -or- - /// - /// - /// The connection has already been closed. - /// - /// - public void Accept () - { - if (_client) { - var msg = "This instance is a client."; - throw new InvalidOperationException (msg); - } - - if (_readyState == WebSocketState.Closing) { - var msg = "The close process is in progress."; - throw new InvalidOperationException (msg); - } - - if (_readyState == WebSocketState.Closed) { - var msg = "The connection has already been closed."; - throw new InvalidOperationException (msg); - } - - if (accept ()) - open (); - } + lock (_forMessageEventQueue) + { + if (_messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) + { + _inMessage = false; + return; + } - /// - /// Accepts the handshake request asynchronously. - /// - /// - /// - /// This method does not wait for the accept process to be complete. - /// - /// - /// This method does nothing if the handshake request has already been - /// accepted. - /// - /// - /// - /// - /// This instance is a client. - /// - /// - /// -or- - /// - /// - /// The close process is in progress. - /// - /// - /// -or- - /// - /// - /// The connection has already been closed. - /// - /// - public void AcceptAsync () - { - if (_client) { - var msg = "This instance is a client."; - throw new InvalidOperationException (msg); - } - - if (_readyState == WebSocketState.Closing) { - var msg = "The close process is in progress."; - throw new InvalidOperationException (msg); - } - - if (_readyState == WebSocketState.Closed) { - var msg = "The connection has already been closed."; - throw new InvalidOperationException (msg); - } - - Func acceptor = accept; - acceptor.BeginInvoke ( - ar => { - if (acceptor.EndInvoke (ar)) - open (); - }, - null - ); - } + e = _messageEventQueue.Dequeue(); + } - /// - /// Closes the connection. - /// - /// - /// This method does nothing if the current state of the connection is - /// Closing or Closed. - /// - public void Close () - { - close (1005, String.Empty); - } + ThreadPool.QueueUserWorkItem(state => messages(e)); + } - /// - /// Closes the connection with the specified . - /// - /// - /// This method does nothing if the current state of the connection is - /// Closing or Closed. - /// - /// - /// - /// A that represents the status code - /// indicating the reason for the close. - /// - /// - /// The status codes are defined in - /// - /// Section 7.4 of RFC 6455. - /// - /// - /// - /// is less than 1000 or greater than 4999. - /// - /// - /// - /// is 1011 (server error). - /// It cannot be used by clients. - /// - /// - /// -or- - /// - /// - /// is 1010 (mandatory extension). - /// It cannot be used by servers. - /// - /// - public void Close (ushort code) - { - if (!code.IsCloseStatusCode ()) { - var msg = "Less than 1000 or greater than 4999."; - throw new ArgumentOutOfRangeException ("code", msg); - } - - if (_client && code == 1011) { - var msg = "1011 cannot be used."; - throw new ArgumentException (msg, "code"); - } - - if (!_client && code == 1010) { - var msg = "1010 cannot be used."; - throw new ArgumentException (msg, "code"); - } - - close (code, String.Empty); - } + /// + /// The open + /// + private void open() + { + _inMessage = true; + startReceiving(); + try + { + OnOpen.Emit(this, EventArgs.Empty); + } + catch (Exception ex) + { + _logger.Error(ex.ToString()); + error("An error has occurred during the OnOpen event.", ex); + } - /// - /// Closes the connection with the specified . - /// - /// - /// This method does nothing if the current state of the connection is - /// Closing or Closed. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It represents the status code indicating the reason for the close. - /// - /// - /// - /// - /// is - /// . - /// It cannot be used by clients. - /// - /// - /// -or- - /// - /// - /// is - /// . - /// It cannot be used by servers. - /// - /// - public void Close (CloseStatusCode code) - { - if (_client && code == CloseStatusCode.ServerError) { - var msg = "ServerError cannot be used."; - throw new ArgumentException (msg, "code"); - } + MessageEventArgs e = null; + lock (_forMessageEventQueue) + { + if (_messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) + { + _inMessage = false; + return; + } - if (!_client && code == CloseStatusCode.MandatoryExtension) { - var msg = "MandatoryExtension cannot be used."; - throw new ArgumentException (msg, "code"); - } + e = _messageEventQueue.Dequeue(); + } - close ((ushort) code, String.Empty); - } + _message.BeginInvoke(e, ar => _message.EndInvoke(ar), null); + } - /// - /// Closes the connection with the specified and - /// . - /// - /// - /// This method does nothing if the current state of the connection is - /// Closing or Closed. - /// - /// - /// - /// A that represents the status code - /// indicating the reason for the close. - /// - /// - /// The status codes are defined in - /// - /// Section 7.4 of RFC 6455. - /// - /// - /// - /// - /// A that represents the reason for the close. - /// - /// - /// The size must be 123 bytes or less in UTF-8. - /// - /// - /// - /// - /// is less than 1000 or greater than 4999. - /// - /// - /// -or- - /// - /// - /// The size of is greater than 123 bytes. - /// - /// - /// - /// - /// is 1011 (server error). - /// It cannot be used by clients. - /// - /// - /// -or- - /// - /// - /// is 1010 (mandatory extension). - /// It cannot be used by servers. - /// - /// - /// -or- - /// - /// - /// is 1005 (no status) and - /// there is . - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - public void Close (ushort code, string reason) - { - if (!code.IsCloseStatusCode ()) { - var msg = "Less than 1000 or greater than 4999."; - throw new ArgumentOutOfRangeException ("code", msg); - } - - if (_client && code == 1011) { - var msg = "1011 cannot be used."; - throw new ArgumentException (msg, "code"); - } - - if (!_client && code == 1010) { - var msg = "1010 cannot be used."; - throw new ArgumentException (msg, "code"); - } - - if (reason.IsNullOrEmpty ()) { - close (code, String.Empty); - return; - } - - if (code == 1005) { - var msg = "1005 cannot be used."; - throw new ArgumentException (msg, "code"); - } - - byte[] bytes; - if (!reason.TryGetUTF8EncodedBytes (out bytes)) { - var msg = "It could not be UTF-8-encoded."; - throw new ArgumentException (msg, "reason"); - } - - if (bytes.Length > 123) { - var msg = "Its size is greater than 123 bytes."; - throw new ArgumentOutOfRangeException ("reason", msg); - } - - close (code, reason); - } + /// + /// The ping + /// + /// The data + /// The + private bool ping(byte[] data) + { + if (_readyState != WebSocketState.Open) + return false; + + var pongReceived = _pongReceived; + if (pongReceived == null) + return false; + + lock (_forPing) + { + try + { + pongReceived.Reset(); + if (!send(Fin.Final, Opcode.Ping, data, false)) + return false; + + return pongReceived.WaitOne(_waitTime); + } + catch (ObjectDisposedException) + { + return false; + } + } + } - /// - /// Closes the connection with the specified and - /// . - /// - /// - /// This method does nothing if the current state of the connection is - /// Closing or Closed. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It represents the status code indicating the reason for the close. - /// - /// - /// - /// - /// A that represents the reason for the close. - /// - /// - /// The size must be 123 bytes or less in UTF-8. - /// - /// - /// - /// - /// is - /// . - /// It cannot be used by clients. - /// - /// - /// -or- - /// - /// - /// is - /// . - /// It cannot be used by servers. - /// - /// - /// -or- - /// - /// - /// is - /// and - /// there is . - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// - /// The size of is greater than 123 bytes. - /// - public void Close (CloseStatusCode code, string reason) - { - if (_client && code == CloseStatusCode.ServerError) { - var msg = "ServerError cannot be used."; - throw new ArgumentException (msg, "code"); - } - - if (!_client && code == CloseStatusCode.MandatoryExtension) { - var msg = "MandatoryExtension cannot be used."; - throw new ArgumentException (msg, "code"); - } - - if (reason.IsNullOrEmpty ()) { - close ((ushort) code, String.Empty); - return; - } - - if (code == CloseStatusCode.NoStatus) { - var msg = "NoStatus cannot be used."; - throw new ArgumentException (msg, "code"); - } - - byte[] bytes; - if (!reason.TryGetUTF8EncodedBytes (out bytes)) { - var msg = "It could not be UTF-8-encoded."; - throw new ArgumentException (msg, "reason"); - } - - if (bytes.Length > 123) { - var msg = "Its size is greater than 123 bytes."; - throw new ArgumentOutOfRangeException ("reason", msg); - } - - close ((ushort) code, reason); - } + /// + /// The processCloseFrame + /// + /// The frame + /// The + private bool processCloseFrame(WebSocketFrame frame) + { + var payload = frame.PayloadData; + close(payload, !payload.HasReservedCode, false, true); - /// - /// Closes the connection asynchronously. - /// - /// - /// - /// This method does not wait for the close to be complete. - /// - /// - /// And this method does nothing if the current state of - /// the connection is Closing or Closed. - /// - /// - public void CloseAsync () - { - closeAsync (1005, String.Empty); - } + return false; + } - /// - /// Closes the connection asynchronously with the specified - /// . - /// - /// - /// - /// This method does not wait for the close to be complete. - /// - /// - /// And this method does nothing if the current state of - /// the connection is Closing or Closed. - /// - /// - /// - /// - /// A that represents the status code - /// indicating the reason for the close. - /// - /// - /// The status codes are defined in - /// - /// Section 7.4 of RFC 6455. - /// - /// - /// - /// is less than 1000 or greater than 4999. - /// - /// - /// - /// is 1011 (server error). - /// It cannot be used by clients. - /// - /// - /// -or- - /// - /// - /// is 1010 (mandatory extension). - /// It cannot be used by servers. - /// - /// - public void CloseAsync (ushort code) - { - if (!code.IsCloseStatusCode ()) { - var msg = "Less than 1000 or greater than 4999."; - throw new ArgumentOutOfRangeException ("code", msg); - } - - if (_client && code == 1011) { - var msg = "1011 cannot be used."; - throw new ArgumentException (msg, "code"); - } - - if (!_client && code == 1010) { - var msg = "1010 cannot be used."; - throw new ArgumentException (msg, "code"); - } - - closeAsync (code, String.Empty); - } + // As client + /// + /// The processCookies + /// + /// The cookies + private void processCookies(CookieCollection cookies) + { + if (cookies.Count == 0) + return; - /// - /// Closes the connection asynchronously with the specified - /// . - /// - /// - /// - /// This method does not wait for the close to be complete. - /// - /// - /// And this method does nothing if the current state of - /// the connection is Closing or Closed. - /// - /// - /// - /// - /// One of the enum values. - /// - /// - /// It represents the status code indicating the reason for the close. - /// - /// - /// - /// - /// is - /// . - /// It cannot be used by clients. - /// - /// - /// -or- - /// - /// - /// is - /// . - /// It cannot be used by servers. - /// - /// - public void CloseAsync (CloseStatusCode code) - { - if (_client && code == CloseStatusCode.ServerError) { - var msg = "ServerError cannot be used."; - throw new ArgumentException (msg, "code"); - } + _cookies.SetOrRemove(cookies); + } + + /// + /// The processDataFrame + /// + /// The frame + /// The + private bool processDataFrame(WebSocketFrame frame) + { + enqueueToMessageEventQueue( + frame.IsCompressed + ? new MessageEventArgs( + frame.Opcode, frame.PayloadData.ApplicationData.Decompress(_compression)) + : new MessageEventArgs(frame)); + + return true; + } + + /// + /// The processFragmentFrame + /// + /// The frame + /// The + private bool processFragmentFrame(WebSocketFrame frame) + { + if (!_inContinuation) + { + // Must process first fragment. + if (frame.IsContinuation) + return true; + + _fragmentsOpcode = frame.Opcode; + _fragmentsCompressed = frame.IsCompressed; + _fragmentsBuffer = new MemoryStream(); + _inContinuation = true; + } - if (!_client && code == CloseStatusCode.MandatoryExtension) { - var msg = "MandatoryExtension cannot be used."; - throw new ArgumentException (msg, "code"); - } + _fragmentsBuffer.WriteBytes(frame.PayloadData.ApplicationData, 1024); + if (frame.IsFinal) + { + using (_fragmentsBuffer) + { + var data = _fragmentsCompressed + ? _fragmentsBuffer.DecompressToArray(_compression) + : _fragmentsBuffer.ToArray(); - closeAsync ((ushort) code, String.Empty); - } + enqueueToMessageEventQueue(new MessageEventArgs(_fragmentsOpcode, data)); + } - /// - /// Closes the connection asynchronously with the specified - /// and . - /// - /// - /// - /// This method does not wait for the close to be complete. - /// - /// - /// And this method does nothing if the current state of - /// the connection is Closing or Closed. - /// - /// - /// - /// - /// A that represents the status code - /// indicating the reason for the close. - /// - /// - /// The status codes are defined in - /// - /// Section 7.4 of RFC 6455. - /// - /// - /// - /// - /// A that represents the reason for the close. - /// - /// - /// The size must be 123 bytes or less in UTF-8. - /// - /// - /// - /// - /// is less than 1000 or greater than 4999. - /// - /// - /// -or- - /// - /// - /// The size of is greater than 123 bytes. - /// - /// - /// - /// - /// is 1011 (server error). - /// It cannot be used by clients. - /// - /// - /// -or- - /// - /// - /// is 1010 (mandatory extension). - /// It cannot be used by servers. - /// - /// - /// -or- - /// - /// - /// is 1005 (no status) and - /// there is . - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - public void CloseAsync (ushort code, string reason) - { - if (!code.IsCloseStatusCode ()) { - var msg = "Less than 1000 or greater than 4999."; - throw new ArgumentOutOfRangeException ("code", msg); - } - - if (_client && code == 1011) { - var msg = "1011 cannot be used."; - throw new ArgumentException (msg, "code"); - } - - if (!_client && code == 1010) { - var msg = "1010 cannot be used."; - throw new ArgumentException (msg, "code"); - } - - if (reason.IsNullOrEmpty ()) { - closeAsync (code, String.Empty); - return; - } - - if (code == 1005) { - var msg = "1005 cannot be used."; - throw new ArgumentException (msg, "code"); - } - - byte[] bytes; - if (!reason.TryGetUTF8EncodedBytes (out bytes)) { - var msg = "It could not be UTF-8-encoded."; - throw new ArgumentException (msg, "reason"); - } - - if (bytes.Length > 123) { - var msg = "Its size is greater than 123 bytes."; - throw new ArgumentOutOfRangeException ("reason", msg); - } - - closeAsync (code, reason); - } + _fragmentsBuffer = null; + _inContinuation = false; + } - /// - /// Closes the connection asynchronously with the specified - /// and . - /// - /// - /// - /// This method does not wait for the close to be complete. - /// - /// - /// And this method does nothing if the current state of - /// the connection is Closing or Closed. - /// - /// - /// - /// - /// One of the enum values. - /// - /// - /// It represents the status code indicating the reason for the close. - /// - /// - /// - /// - /// A that represents the reason for the close. - /// - /// - /// The size must be 123 bytes or less in UTF-8. - /// - /// - /// - /// - /// is - /// . - /// It cannot be used by clients. - /// - /// - /// -or- - /// - /// - /// is - /// . - /// It cannot be used by servers. - /// - /// - /// -or- - /// - /// - /// is - /// and - /// there is . - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// - /// The size of is greater than 123 bytes. - /// - public void CloseAsync (CloseStatusCode code, string reason) - { - if (_client && code == CloseStatusCode.ServerError) { - var msg = "ServerError cannot be used."; - throw new ArgumentException (msg, "code"); - } - - if (!_client && code == CloseStatusCode.MandatoryExtension) { - var msg = "MandatoryExtension cannot be used."; - throw new ArgumentException (msg, "code"); - } - - if (reason.IsNullOrEmpty ()) { - closeAsync ((ushort) code, String.Empty); - return; - } - - if (code == CloseStatusCode.NoStatus) { - var msg = "NoStatus cannot be used."; - throw new ArgumentException (msg, "code"); - } - - byte[] bytes; - if (!reason.TryGetUTF8EncodedBytes (out bytes)) { - var msg = "It could not be UTF-8-encoded."; - throw new ArgumentException (msg, "reason"); - } - - if (bytes.Length > 123) { - var msg = "Its size is greater than 123 bytes."; - throw new ArgumentOutOfRangeException ("reason", msg); - } - - closeAsync ((ushort) code, reason); - } + return true; + } - /// - /// Establishes a connection. - /// - /// - /// This method does nothing if the connection has already been established. - /// - /// - /// - /// This instance is not a client. - /// - /// - /// -or- - /// - /// - /// The close process is in progress. - /// - /// - /// -or- - /// - /// - /// A series of reconnecting has failed. - /// - /// - public void Connect () - { - if (!_client) { - var msg = "This instance is not a client."; - throw new InvalidOperationException (msg); - } - - if (_readyState == WebSocketState.Closing) { - var msg = "The close process is in progress."; - throw new InvalidOperationException (msg); - } - - if (_retryCountForConnect > _maxRetryCountForConnect) { - var msg = "A series of reconnecting has failed."; - throw new InvalidOperationException (msg); - } - - if (connect ()) - open (); - } + /// + /// The processPingFrame + /// + /// The frame + /// The + private bool processPingFrame(WebSocketFrame frame) + { + _logger.Trace("A ping was received."); - /// - /// Establishes a connection asynchronously. - /// - /// - /// - /// This method does not wait for the connect process to be complete. - /// - /// - /// This method does nothing if the connection has already been - /// established. - /// - /// - /// - /// - /// This instance is not a client. - /// - /// - /// -or- - /// - /// - /// The close process is in progress. - /// - /// - /// -or- - /// - /// - /// A series of reconnecting has failed. - /// - /// - public void ConnectAsync () - { - if (!_client) { - var msg = "This instance is not a client."; - throw new InvalidOperationException (msg); - } - - if (_readyState == WebSocketState.Closing) { - var msg = "The close process is in progress."; - throw new InvalidOperationException (msg); - } - - if (_retryCountForConnect > _maxRetryCountForConnect) { - var msg = "A series of reconnecting has failed."; - throw new InvalidOperationException (msg); - } - - Func connector = connect; - connector.BeginInvoke ( - ar => { - if (connector.EndInvoke (ar)) - open (); - }, - null - ); - } + var pong = WebSocketFrame.CreatePongFrame(frame.PayloadData, _client); - /// - /// Sends a ping using the WebSocket connection. - /// - /// - /// true if the send has done with no error and a pong has been - /// received within a time; otherwise, false. - /// - public bool Ping () - { - return ping (EmptyBytes); - } + lock (_forState) + { + if (_readyState != WebSocketState.Open) + { + _logger.Error("The connection is closing."); + return true; + } - /// - /// Sends a ping with using the WebSocket - /// connection. - /// - /// - /// true if the send has done with no error and a pong has been - /// received within a time; otherwise, false. - /// - /// - /// - /// A that represents the message to send. - /// - /// - /// The size must be 125 bytes or less in UTF-8. - /// - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// The size of is greater than 125 bytes. - /// - public bool Ping (string message) - { - if (message.IsNullOrEmpty ()) - return ping (EmptyBytes); + if (!sendBytes(pong.ToArray())) + return false; + } - byte[] bytes; - if (!message.TryGetUTF8EncodedBytes (out bytes)) { - var msg = "It could not be UTF-8-encoded."; - throw new ArgumentException (msg, "message"); - } + _logger.Trace("A pong to this ping has been sent."); - if (bytes.Length > 125) { - var msg = "Its size is greater than 125 bytes."; - throw new ArgumentOutOfRangeException ("message", msg); - } + if (_emitOnPing) + { + if (_client) + pong.Unmask(); - return ping (bytes); - } + enqueueToMessageEventQueue(new MessageEventArgs(frame)); + } - /// - /// Sends the specified data using the WebSocket connection. - /// - /// - /// An array of that represents the binary data to send. - /// - /// - /// The current state of the connection is not Open. - /// - /// - /// is . - /// - public void Send (byte[] data) - { - if (_readyState != WebSocketState.Open) { - var msg = "The current state of the connection is not Open."; - throw new InvalidOperationException (msg); - } + return true; + } - if (data == null) - throw new ArgumentNullException ("data"); + /// + /// The processPongFrame + /// + /// The frame + /// The + private bool processPongFrame(WebSocketFrame frame) + { + _logger.Trace("A pong was received."); - send (Opcode.Binary, new MemoryStream (data)); - } + try + { + _pongReceived.Set(); + } + catch (NullReferenceException ex) + { + _logger.Error(ex.Message); + _logger.Debug(ex.ToString()); - /// - /// Sends the specified file using the WebSocket connection. - /// - /// - /// - /// A that specifies the file to send. - /// - /// - /// The file is sent as the binary data. - /// - /// - /// - /// The current state of the connection is not Open. - /// - /// - /// is . - /// - /// - /// - /// The file does not exist. - /// - /// - /// -or- - /// - /// - /// The file could not be opened. - /// - /// - public void Send (FileInfo fileInfo) - { - if (_readyState != WebSocketState.Open) { - var msg = "The current state of the connection is not Open."; - throw new InvalidOperationException (msg); - } - - if (fileInfo == null) - throw new ArgumentNullException ("fileInfo"); - - if (!fileInfo.Exists) { - var msg = "The file does not exist."; - throw new ArgumentException (msg, "fileInfo"); - } - - FileStream stream; - if (!fileInfo.TryOpenRead (out stream)) { - var msg = "The file could not be opened."; - throw new ArgumentException (msg, "fileInfo"); - } - - send (Opcode.Binary, stream); - } + return false; + } + catch (ObjectDisposedException ex) + { + _logger.Error(ex.Message); + _logger.Debug(ex.ToString()); - /// - /// Sends the specified data using the WebSocket connection. - /// - /// - /// A that represents the text data to send. - /// - /// - /// The current state of the connection is not Open. - /// - /// - /// is . - /// - /// - /// could not be UTF-8-encoded. - /// - public void Send (string data) - { - if (_readyState != WebSocketState.Open) { - var msg = "The current state of the connection is not Open."; - throw new InvalidOperationException (msg); - } + return false; + } - if (data == null) - throw new ArgumentNullException ("data"); + _logger.Trace("It has been signaled."); + + return true; + } + + /// + /// The processReceivedFrame + /// + /// The frame + /// The + private bool processReceivedFrame(WebSocketFrame frame) + { + string msg; + if (!checkReceivedFrame(frame, out msg)) + throw new WebSocketException(CloseStatusCode.ProtocolError, msg); + + frame.Unmask(); + return frame.IsFragment + ? processFragmentFrame(frame) + : frame.IsData + ? processDataFrame(frame) + : frame.IsPing + ? processPingFrame(frame) + : frame.IsPong + ? processPongFrame(frame) + : frame.IsClose + ? processCloseFrame(frame) + : processUnsupportedFrame(frame); + } + + // As server + /// + /// The processSecWebSocketExtensionsClientHeader + /// + /// The value + private void processSecWebSocketExtensionsClientHeader(string value) + { + if (value == null) + return; - byte[] bytes; - if (!data.TryGetUTF8EncodedBytes (out bytes)) { - var msg = "It could not be UTF-8-encoded."; - throw new ArgumentException (msg, "data"); - } + var buff = new StringBuilder(80); + var comp = false; + + foreach (var elm in value.SplitHeaderValue(',')) + { + var extension = elm.Trim(); + if (extension.Length == 0) + continue; + + if (!comp) + { + if (extension.IsCompressionExtension(CompressionMethod.Deflate)) + { + _compression = CompressionMethod.Deflate; + + buff.AppendFormat( + "{0}, ", + _compression.ToExtensionString( + "client_no_context_takeover", "server_no_context_takeover" + ) + ); + + comp = true; + } + } + } - send (Opcode.Text, new MemoryStream (bytes)); - } + var len = buff.Length; + if (len <= 2) + return; - /// - /// Sends the data from the specified stream using the WebSocket connection. - /// - /// - /// - /// A instance from which to read the data to send. - /// - /// - /// The data is sent as the binary data. - /// - /// - /// - /// An that specifies the number of bytes to send. - /// - /// - /// The current state of the connection is not Open. - /// - /// - /// is . - /// - /// - /// - /// cannot be read. - /// - /// - /// -or- - /// - /// - /// is less than 1. - /// - /// - /// -or- - /// - /// - /// No data could be read from . - /// - /// - public void Send (Stream stream, int length) - { - if (_readyState != WebSocketState.Open) { - var msg = "The current state of the connection is not Open."; - throw new InvalidOperationException (msg); - } - - if (stream == null) - throw new ArgumentNullException ("stream"); - - if (!stream.CanRead) { - var msg = "It cannot be read."; - throw new ArgumentException (msg, "stream"); - } - - if (length < 1) { - var msg = "Less than 1."; - throw new ArgumentException (msg, "length"); - } - - var bytes = stream.ReadBytes (length); - - var len = bytes.Length; - if (len == 0) { - var msg = "No data could be read from it."; - throw new ArgumentException (msg, "stream"); - } - - if (len < length) { - _logger.Warn ( - String.Format ( - "Only {0} byte(s) of data could be read from the stream.", - len - ) - ); - } - - send (Opcode.Binary, new MemoryStream (bytes)); - } + buff.Length = len - 2; + _extensions = buff.ToString(); + } - /// - /// Sends the specified data asynchronously using the WebSocket connection. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// An array of that represents the binary data to send. - /// - /// - /// - /// An Action<bool> delegate or - /// if not needed. - /// - /// - /// The delegate invokes the method called when the send is complete. - /// - /// - /// true is passed to the method if the send has done with - /// no error; otherwise, false. - /// - /// - /// - /// The current state of the connection is not Open. - /// - /// - /// is . - /// - public void SendAsync (byte[] data, Action completed) - { - if (_readyState != WebSocketState.Open) { - var msg = "The current state of the connection is not Open."; - throw new InvalidOperationException (msg); - } + // As client + /// + /// The processSecWebSocketExtensionsServerHeader + /// + /// The value + private void processSecWebSocketExtensionsServerHeader(string value) + { + if (value == null) + { + _compression = CompressionMethod.None; + return; + } - if (data == null) - throw new ArgumentNullException ("data"); + _extensions = value; + } - sendAsync (Opcode.Binary, new MemoryStream (data), completed); - } + // As server + /// + /// The processSecWebSocketProtocolClientHeader + /// + /// The values + private void processSecWebSocketProtocolClientHeader( + IEnumerable values + ) + { + if (values.Contains(val => val == _protocol)) + return; - /// - /// Sends the specified file asynchronously using the WebSocket connection. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// - /// A that specifies the file to send. - /// - /// - /// The file is sent as the binary data. - /// - /// - /// - /// - /// An Action<bool> delegate or - /// if not needed. - /// - /// - /// The delegate invokes the method called when the send is complete. - /// - /// - /// true is passed to the method if the send has done with - /// no error; otherwise, false. - /// - /// - /// - /// The current state of the connection is not Open. - /// - /// - /// is . - /// - /// - /// - /// The file does not exist. - /// - /// - /// -or- - /// - /// - /// The file could not be opened. - /// - /// - public void SendAsync (FileInfo fileInfo, Action completed) - { - if (_readyState != WebSocketState.Open) { - var msg = "The current state of the connection is not Open."; - throw new InvalidOperationException (msg); - } - - if (fileInfo == null) - throw new ArgumentNullException ("fileInfo"); - - if (!fileInfo.Exists) { - var msg = "The file does not exist."; - throw new ArgumentException (msg, "fileInfo"); - } - - FileStream stream; - if (!fileInfo.TryOpenRead (out stream)) { - var msg = "The file could not be opened."; - throw new ArgumentException (msg, "fileInfo"); - } - - sendAsync (Opcode.Binary, stream, completed); - } + _protocol = null; + } - /// - /// Sends the specified data asynchronously using the WebSocket connection. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// A that represents the text data to send. - /// - /// - /// - /// An Action<bool> delegate or - /// if not needed. - /// - /// - /// The delegate invokes the method called when the send is complete. - /// - /// - /// true is passed to the method if the send has done with - /// no error; otherwise, false. - /// - /// - /// - /// The current state of the connection is not Open. - /// - /// - /// is . - /// - /// - /// could not be UTF-8-encoded. - /// - public void SendAsync (string data, Action completed) - { - if (_readyState != WebSocketState.Open) { - var msg = "The current state of the connection is not Open."; - throw new InvalidOperationException (msg); - } + /// + /// The processUnsupportedFrame + /// + /// The frame + /// The + private bool processUnsupportedFrame(WebSocketFrame frame) + { + _logger.Fatal("An unsupported frame:" + frame.PrintToString(false)); + fatal("There is no way to handle it.", CloseStatusCode.PolicyViolation); - if (data == null) - throw new ArgumentNullException ("data"); + return false; + } - byte[] bytes; - if (!data.TryGetUTF8EncodedBytes (out bytes)) { - var msg = "It could not be UTF-8-encoded."; - throw new ArgumentException (msg, "data"); - } + // As server + /// + /// The refuseHandshake + /// + /// The code + /// The reason + private void refuseHandshake(CloseStatusCode code, string reason) + { + _readyState = WebSocketState.Closing; - sendAsync (Opcode.Text, new MemoryStream (bytes), completed); - } + var res = createHandshakeFailureResponse(HttpStatusCode.BadRequest); + sendHttpResponse(res); - /// - /// Sends the data from the specified stream asynchronously using - /// the WebSocket connection. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// - /// A instance from which to read the data to send. - /// - /// - /// The data is sent as the binary data. - /// - /// - /// - /// An that specifies the number of bytes to send. - /// - /// - /// - /// An Action<bool> delegate or - /// if not needed. - /// - /// - /// The delegate invokes the method called when the send is complete. - /// - /// - /// true is passed to the method if the send has done with - /// no error; otherwise, false. - /// - /// - /// - /// The current state of the connection is not Open. - /// - /// - /// is . - /// - /// - /// - /// cannot be read. - /// - /// - /// -or- - /// - /// - /// is less than 1. - /// - /// - /// -or- - /// - /// - /// No data could be read from . - /// - /// - public void SendAsync (Stream stream, int length, Action completed) - { - if (_readyState != WebSocketState.Open) { - var msg = "The current state of the connection is not Open."; - throw new InvalidOperationException (msg); - } - - if (stream == null) - throw new ArgumentNullException ("stream"); - - if (!stream.CanRead) { - var msg = "It cannot be read."; - throw new ArgumentException (msg, "stream"); - } - - if (length < 1) { - var msg = "Less than 1."; - throw new ArgumentException (msg, "length"); - } - - var bytes = stream.ReadBytes (length); - - var len = bytes.Length; - if (len == 0) { - var msg = "No data could be read from it."; - throw new ArgumentException (msg, "stream"); - } - - if (len < length) { - _logger.Warn ( - String.Format ( - "Only {0} byte(s) of data could be read from the stream.", - len - ) - ); - } - - sendAsync (Opcode.Binary, new MemoryStream (bytes), completed); - } + releaseServerResources(); - /// - /// Sets an HTTP cookie to send with the handshake request. - /// - /// - /// This method does nothing if the connection has already been - /// established or it is closing. - /// - /// - /// A that represents the cookie to send. - /// - /// - /// This instance is not a client. - /// - /// - /// is . - /// - public void SetCookie (Cookie cookie) - { - string msg = null; + _readyState = WebSocketState.Closed; - if (!_client) { - msg = "This instance is not a client."; - throw new InvalidOperationException (msg); - } + var e = new CloseEventArgs(code, reason); - if (cookie == null) - throw new ArgumentNullException ("cookie"); + try + { + OnClose.Emit(this, e); + } + catch (Exception ex) + { + _logger.Error(ex.Message); + _logger.Debug(ex.ToString()); + } + } - if (!canSet (out msg)) { - _logger.Warn (msg); - return; - } + // As client + /// + /// The releaseClientResources + /// + private void releaseClientResources() + { + if (_stream != null) + { + _stream.Dispose(); + _stream = null; + } - lock (_forState) { - if (!canSet (out msg)) { - _logger.Warn (msg); - return; + if (_tcpClient != null) + { + _tcpClient.Close(); + _tcpClient = null; + } } - lock (_cookies.SyncRoot) - _cookies.SetOrRemove (cookie); - } - } - - /// - /// Sets the credentials for the HTTP authentication (Basic/Digest). - /// - /// - /// This method does nothing if the connection has already been - /// established or it is closing. - /// - /// - /// - /// A that represents the username associated with - /// the credentials. - /// - /// - /// or an empty string if initializes - /// the credentials. - /// - /// - /// - /// - /// A that represents the password for the username - /// associated with the credentials. - /// - /// - /// or an empty string if not necessary. - /// - /// - /// - /// true if sends the credentials for the Basic authentication in - /// advance with the first handshake request; otherwise, false. - /// - /// - /// This instance is not a client. - /// - /// - /// - /// contains an invalid character. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - public void SetCredentials (string username, string password, bool preAuth) - { - string msg = null; + /// + /// The releaseCommonResources + /// + private void releaseCommonResources() + { + if (_fragmentsBuffer != null) + { + _fragmentsBuffer.Dispose(); + _fragmentsBuffer = null; + _inContinuation = false; + } - if (!_client) { - msg = "This instance is not a client."; - throw new InvalidOperationException (msg); - } + if (_pongReceived != null) + { + _pongReceived.Close(); + _pongReceived = null; + } - if (!username.IsNullOrEmpty ()) { - if (username.Contains (':') || !username.IsText ()) { - msg = "It contains an invalid character."; - throw new ArgumentException (msg, "username"); + if (_receivingExited != null) + { + _receivingExited.Close(); + _receivingExited = null; + } } - } - if (!password.IsNullOrEmpty ()) { - if (!password.IsText ()) { - msg = "It contains an invalid character."; - throw new ArgumentException (msg, "password"); + /// + /// The releaseResources + /// + private void releaseResources() + { + if (_client) + releaseClientResources(); + else + releaseServerResources(); + + releaseCommonResources(); } - } - if (!canSet (out msg)) { - _logger.Warn (msg); - return; - } + // As server + /// + /// The releaseServerResources + /// + private void releaseServerResources() + { + if (_closeContext == null) + return; - lock (_forState) { - if (!canSet (out msg)) { - _logger.Warn (msg); - return; + _closeContext(); + _closeContext = null; + _stream = null; + _context = null; + } + + /// + /// The send + /// + /// The fin + /// The opcode + /// The data + /// The compressed + /// The + private bool send(Fin fin, Opcode opcode, byte[] data, bool compressed) + { + lock (_forState) + { + if (_readyState != WebSocketState.Open) + { + _logger.Error("The connection is closing."); + return false; + } + + var frame = new WebSocketFrame(fin, opcode, data, compressed, _client); + return sendBytes(frame.ToArray()); + } } - if (username.IsNullOrEmpty ()) { - _credentials = null; - _preAuth = false; - - return; + /// + /// The send + /// + /// The opcode + /// The stream + /// The + private bool send(Opcode opcode, Stream stream) + { + lock (_forSend) + { + var src = stream; + var compressed = false; + var sent = false; + try + { + if (_compression != CompressionMethod.None) + { + stream = stream.Compress(_compression); + compressed = true; + } + + sent = send(opcode, stream, compressed); + if (!sent) + error("A send has been interrupted.", null); + } + catch (Exception ex) + { + _logger.Error(ex.ToString()); + error("An error has occurred during a send.", ex); + } + finally + { + if (compressed) + stream.Dispose(); + + src.Dispose(); + } + + return sent; + } } - _credentials = new NetworkCredential ( - username, password, _uri.PathAndQuery - ); + /// + /// The send + /// + /// The opcode + /// The stream + /// The compressed + /// The + private bool send(Opcode opcode, Stream stream, bool compressed) + { + var len = stream.Length; + if (len == 0) + return send(Fin.Final, opcode, EmptyBytes, false); + + var quo = len / FragmentLength; + var rem = (int)(len % FragmentLength); + + byte[] buff = null; + if (quo == 0) + { + buff = new byte[rem]; + return stream.Read(buff, 0, rem) == rem + && send(Fin.Final, opcode, buff, compressed); + } - _preAuth = preAuth; - } - } + if (quo == 1 && rem == 0) + { + buff = new byte[FragmentLength]; + return stream.Read(buff, 0, FragmentLength) == FragmentLength + && send(Fin.Final, opcode, buff, compressed); + } - /// - /// Sets the URL of the HTTP proxy server through which to connect and - /// the credentials for the HTTP proxy authentication (Basic/Digest). - /// - /// - /// This method does nothing if the connection has already been - /// established or it is closing. - /// - /// - /// - /// A that represents the URL of the proxy server - /// through which to connect. - /// - /// - /// The syntax is http://<host>[:<port>]. - /// - /// - /// or an empty string if initializes the URL and - /// the credentials. - /// - /// - /// - /// - /// A that represents the username associated with - /// the credentials. - /// - /// - /// or an empty string if the credentials are not - /// necessary. - /// - /// - /// - /// - /// A that represents the password for the username - /// associated with the credentials. - /// - /// - /// or an empty string if not necessary. - /// - /// - /// - /// This instance is not a client. - /// - /// - /// - /// is not an absolute URI string. - /// - /// - /// -or- - /// - /// - /// The scheme of is not http. - /// - /// - /// -or- - /// - /// - /// includes the path segments. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - public void SetProxy (string url, string username, string password) - { - string msg = null; + /* Send fragments */ - if (!_client) { - msg = "This instance is not a client."; - throw new InvalidOperationException (msg); - } + // Begin + buff = new byte[FragmentLength]; + var sent = stream.Read(buff, 0, FragmentLength) == FragmentLength + && send(Fin.More, opcode, buff, compressed); - Uri uri = null; + if (!sent) + return false; - if (!url.IsNullOrEmpty ()) { - if (!Uri.TryCreate (url, UriKind.Absolute, out uri)) { - msg = "Not an absolute URI string."; - throw new ArgumentException (msg, "url"); - } + var n = rem == 0 ? quo - 2 : quo - 1; + for (long i = 0; i < n; i++) + { + sent = stream.Read(buff, 0, FragmentLength) == FragmentLength + && send(Fin.More, Opcode.Cont, buff, false); - if (uri.Scheme != "http") { - msg = "The scheme part is not http."; - throw new ArgumentException (msg, "url"); - } + if (!sent) + return false; + } - if (uri.Segments.Length > 1) { - msg = "It includes the path segments."; - throw new ArgumentException (msg, "url"); + // End + if (rem == 0) + rem = FragmentLength; + else + buff = new byte[rem]; + + return stream.Read(buff, 0, rem) == rem + && send(Fin.Final, Opcode.Cont, buff, false); + } + + /// + /// The sendAsync + /// + /// The opcode + /// The stream + /// The completed + private void sendAsync(Opcode opcode, Stream stream, Action completed) + { + Func sender = send; + sender.BeginInvoke( + opcode, + stream, + ar => + { + try + { + var sent = sender.EndInvoke(ar); + if (completed != null) + completed(sent); + } + catch (Exception ex) + { + _logger.Error(ex.ToString()); + error( + "An error has occurred during the callback for an async send.", + ex + ); + } + }, + null + ); } - } - if (!username.IsNullOrEmpty ()) { - if (username.Contains (':') || !username.IsText ()) { - msg = "It contains an invalid character."; - throw new ArgumentException (msg, "username"); + /// + /// The sendBytes + /// + /// The bytes + /// The + private bool sendBytes(byte[] bytes) + { + try + { + _stream.Write(bytes, 0, bytes.Length); + } + catch (Exception ex) + { + _logger.Error(ex.Message); + _logger.Debug(ex.ToString()); + + return false; + } + + return true; + } + + // As client + /// + /// The sendHandshakeRequest + /// + /// The + private HttpResponse sendHandshakeRequest() + { + var req = createHandshakeRequest(); + var res = sendHttpRequest(req, 90000); + if (res.IsUnauthorized) + { + var chal = res.Headers["WWW-Authenticate"]; + _logger.Warn(String.Format("Received an authentication requirement for '{0}'.", chal)); + if (chal.IsNullOrEmpty()) + { + _logger.Error("No authentication challenge is specified."); + return res; + } + + _authChallenge = AuthenticationChallenge.Parse(chal); + if (_authChallenge == null) + { + _logger.Error("An invalid authentication challenge is specified."); + return res; + } + + if (_credentials != null && + (!_preAuth || _authChallenge.Scheme == AuthenticationSchemes.Digest)) + { + if (res.HasConnectionClose) + { + releaseClientResources(); + setClientStream(); + } + + var authRes = new AuthenticationResponse(_authChallenge, _credentials, _nonceCount); + _nonceCount = authRes.NonceCount; + req.Headers["Authorization"] = authRes.ToString(); + res = sendHttpRequest(req, 15000); + } + } + + if (res.IsRedirect) + { + var url = res.Headers["Location"]; + _logger.Warn(String.Format("Received a redirection to '{0}'.", url)); + if (_enableRedirection) + { + if (url.IsNullOrEmpty()) + { + _logger.Error("No url to redirect is located."); + return res; + } + + Uri uri; + string msg; + if (!url.TryCreateWebSocketUri(out uri, out msg)) + { + _logger.Error("An invalid url to redirect is located: " + msg); + return res; + } + + releaseClientResources(); + + _uri = uri; + _secure = uri.Scheme == "wss"; + + setClientStream(); + return sendHandshakeRequest(); + } + } + + return res; } - } - if (!password.IsNullOrEmpty ()) { - if (!password.IsText ()) { - msg = "It contains an invalid character."; - throw new ArgumentException (msg, "password"); + // As client + /// + /// The sendHttpRequest + /// + /// The request + /// The millisecondsTimeout + /// The + private HttpResponse sendHttpRequest(HttpRequest request, int millisecondsTimeout) + { + _logger.Debug("A request to the server:\n" + request.ToString()); + var res = request.GetResponse(_stream, millisecondsTimeout); + _logger.Debug("A response to this request:\n" + res.ToString()); + + return res; } - } - if (!canSet (out msg)) { - _logger.Warn (msg); - return; - } + // As server + /// + /// The sendHttpResponse + /// + /// The response + /// The + private bool sendHttpResponse(HttpResponse response) + { + _logger.Debug( + String.Format( + "A response to {0}:\n{1}", _context.UserEndPoint, response + ) + ); + + return sendBytes(response.ToByteArray()); + } + + // As client + /// + /// The sendProxyConnectRequest + /// + private void sendProxyConnectRequest() + { + var req = HttpRequest.CreateConnectRequest(_uri); + var res = sendHttpRequest(req, 90000); + if (res.IsProxyAuthenticationRequired) + { + var chal = res.Headers["Proxy-Authenticate"]; + _logger.Warn( + String.Format("Received a proxy authentication requirement for '{0}'.", chal)); + + if (chal.IsNullOrEmpty()) + throw new WebSocketException("No proxy authentication challenge is specified."); + + var authChal = AuthenticationChallenge.Parse(chal); + if (authChal == null) + throw new WebSocketException("An invalid proxy authentication challenge is specified."); + + if (_proxyCredentials != null) + { + if (res.HasConnectionClose) + { + releaseClientResources(); + _tcpClient = new TcpClient(_proxyUri.DnsSafeHost, _proxyUri.Port); + _stream = _tcpClient.GetStream(); + } + + var authRes = new AuthenticationResponse(authChal, _proxyCredentials, 0); + req.Headers["Proxy-Authorization"] = authRes.ToString(); + res = sendHttpRequest(req, 15000); + } + + if (res.IsProxyAuthenticationRequired) + throw new WebSocketException("A proxy authentication is required."); + } + + if (res.StatusCode[0] != '2') + throw new WebSocketException( + "The proxy has failed a connection to the requested host and port."); + } + + // As client + /// + /// The setClientStream + /// + private void setClientStream() + { + if (_proxyUri != null) + { + _tcpClient = new TcpClient(_proxyUri.DnsSafeHost, _proxyUri.Port); + _stream = _tcpClient.GetStream(); + sendProxyConnectRequest(); + } + else + { + _tcpClient = new TcpClient(_uri.DnsSafeHost, _uri.Port); + _stream = _tcpClient.GetStream(); + } - lock (_forState) { - if (!canSet (out msg)) { - _logger.Warn (msg); - return; + if (_secure) + { + var conf = getSslConfiguration(); + var host = conf.TargetHost; + if (host != _uri.DnsSafeHost) + throw new WebSocketException( + CloseStatusCode.TlsHandshakeFailure, "An invalid host name is specified."); + + try + { + var sslStream = new SslStream( + _stream, + false, + conf.ServerCertificateValidationCallback, + conf.ClientCertificateSelectionCallback); + + sslStream.AuthenticateAsClient( + host, + conf.ClientCertificates, + conf.EnabledSslProtocols, + conf.CheckCertificateRevocation); + + _stream = sslStream; + } + catch (Exception ex) + { + throw new WebSocketException(CloseStatusCode.TlsHandshakeFailure, ex); + } + } } - if (url.IsNullOrEmpty ()) { - _proxyUri = null; - _proxyCredentials = null; + /// + /// The startReceiving + /// + private void startReceiving() + { + if (_messageEventQueue.Count > 0) + _messageEventQueue.Clear(); + + _pongReceived = new ManualResetEvent(false); + _receivingExited = new ManualResetEvent(false); + + Action receive = null; + receive = + () => + WebSocketFrame.ReadFrameAsync( + _stream, + false, + frame => + { + if (!processReceivedFrame(frame) || _readyState == WebSocketState.Closed) + { + var exited = _receivingExited; + if (exited != null) + exited.Set(); + + return; + } + + // Receive next asap because the Ping or Close needs a response to it. + receive(); + + if (_inMessage || !HasMessage || _readyState != WebSocketState.Open) + return; + + message(); + }, + ex => + { + _logger.Fatal(ex.ToString()); + fatal("An exception has occurred while receiving.", ex); + } + ); + + receive(); + } + + // As client + /// + /// The validateSecWebSocketAcceptHeader + /// + /// The value + /// The + private bool validateSecWebSocketAcceptHeader(string value) + { + return value != null && value == CreateResponseKey(_base64Key); + } + + // As client + /// + /// The validateSecWebSocketExtensionsServerHeader + /// + /// The value + /// The + private bool validateSecWebSocketExtensionsServerHeader(string value) + { + if (value == null) + return true; + + if (value.Length == 0) + return false; + + if (!_extensionsRequested) + return false; + + var comp = _compression != CompressionMethod.None; + foreach (var e in value.SplitHeaderValue(',')) + { + var ext = e.Trim(); + if (comp && ext.IsCompressionExtension(_compression)) + { + if (!ext.Contains("server_no_context_takeover")) + { + _logger.Error("The server hasn't sent back 'server_no_context_takeover'."); + return false; + } + + if (!ext.Contains("client_no_context_takeover")) + _logger.Warn("The server hasn't sent back 'client_no_context_takeover'."); + + var method = _compression.ToExtensionString(); + var invalid = + ext.SplitHeaderValue(';').Contains( + t => + { + t = t.Trim(); + return t != method + && t != "server_no_context_takeover" + && t != "client_no_context_takeover"; + } + ); + + if (invalid) + return false; + } + else + { + return false; + } + } - return; + return true; } - _proxyUri = uri; - _proxyCredentials = !username.IsNullOrEmpty () - ? new NetworkCredential ( - username, - password, - String.Format ( - "{0}:{1}", _uri.DnsSafeHost, _uri.Port - ) - ) - : null; - } - } + // As client + /// + /// The validateSecWebSocketProtocolServerHeader + /// + /// The value + /// The + private bool validateSecWebSocketProtocolServerHeader(string value) + { + if (value == null) + return !_protocolsRequested; + + if (value.Length == 0) + return false; - #endregion + return _protocolsRequested && _protocols.Contains(p => p == value); + } - #region Explicit Interface Implementations + // As client + /// + /// The validateSecWebSocketVersionServerHeader + /// + /// The value + /// The + private bool validateSecWebSocketVersionServerHeader(string value) + { + return value == null || value == _version; + } - /// - /// Closes the connection and releases all associated resources. - /// - /// - /// - /// This method closes the connection with close status 1001 (going away). - /// - /// - /// And this method does nothing if the current state of the connection is - /// Closing or Closed. - /// - /// - void IDisposable.Dispose () - { - close (1001, String.Empty); - } + /// + /// Closes the connection and releases all associated resources. + /// + void IDisposable.Dispose() + { + close(1001, String.Empty); + } - #endregion - } -} + #endregion + } +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/WebSocketException.cs b/src/WebSocket/WebSocketCore/WebSocketException.cs index 06e525741..e5cf1d76f 100644 --- a/src/WebSocket/WebSocketCore/WebSocketException.cs +++ b/src/WebSocket/WebSocketCore/WebSocketException.cs @@ -1,4 +1,3 @@ -#region License /* * WebSocketException.cs * @@ -24,86 +23,122 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; namespace WebSocketCore { - /// - /// The exception that is thrown when a fatal error occurs in - /// the WebSocket communication. - /// - public class WebSocketException : Exception - { - #region Private Fields - - private CloseStatusCode _code; - - #endregion - - #region Internal Constructors - - internal WebSocketException () - : this (CloseStatusCode.Abnormal, null, null) - { - } - - internal WebSocketException (Exception innerException) - : this (CloseStatusCode.Abnormal, null, innerException) - { - } - - internal WebSocketException (string message) - : this (CloseStatusCode.Abnormal, message, null) - { - } - - internal WebSocketException (CloseStatusCode code) - : this (code, null, null) - { - } - - internal WebSocketException (string message, Exception innerException) - : this (CloseStatusCode.Abnormal, message, innerException) - { - } - - internal WebSocketException (CloseStatusCode code, Exception innerException) - : this (code, null, innerException) - { - } - - internal WebSocketException (CloseStatusCode code, string message) - : this (code, message, null) + /// + /// The exception that is thrown when a fatal error occurs in + /// the WebSocket communication. + /// + public class WebSocketException : Exception { - } - - internal WebSocketException ( + #region ֶ + + /// + /// Defines the _code + /// + private CloseStatusCode _code; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + internal WebSocketException() + : this(CloseStatusCode.Abnormal, null, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The code + /// The message + /// The innerException + internal WebSocketException( CloseStatusCode code, string message, Exception innerException ) - : base (message ?? code.GetMessage (), innerException) - { - _code = code; - } - - #endregion - - #region Public Properties - - /// - /// Gets the status code indicating the cause of the exception. - /// - /// - /// One of the enum values that represents - /// the status code indicating the cause of the exception. - /// - public CloseStatusCode Code { - get { - return _code; - } + : base(message ?? code.GetMessage(), innerException) + { + _code = code; + } + + /// + /// Initializes a new instance of the class. + /// + /// The code + internal WebSocketException(CloseStatusCode code) + : this(code, null, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The code + /// The innerException + internal WebSocketException(CloseStatusCode code, Exception innerException) + : this(code, null, innerException) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The code + /// The message + internal WebSocketException(CloseStatusCode code, string message) + : this(code, message, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The innerException + internal WebSocketException(Exception innerException) + : this(CloseStatusCode.Abnormal, null, innerException) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message + internal WebSocketException(string message) + : this(CloseStatusCode.Abnormal, message, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message + /// The innerException + internal WebSocketException(string message, Exception innerException) + : this(CloseStatusCode.Abnormal, message, innerException) + { + } + + #endregion 캯 + + #region + + /// + /// Gets the status code indicating the cause of the exception. + /// + public CloseStatusCode Code + { + get + { + return _code; + } + } + + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/WebSocketFrame.cs b/src/WebSocket/WebSocketCore/WebSocketFrame.cs index c8b9d8049..6650032a6 100644 --- a/src/WebSocket/WebSocketCore/WebSocketFrame.cs +++ b/src/WebSocket/WebSocketCore/WebSocketFrame.cs @@ -1,4 +1,3 @@ -#region License /* * WebSocketFrame.cs * @@ -24,14 +23,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion -#region Contributors /* * Contributors: * - Chris Swiedler */ -#endregion using System; using System.Collections; @@ -41,363 +37,771 @@ namespace WebSocketCore { - internal class WebSocketFrame : IEnumerable - { - #region Private Fields - - private byte[] _extPayloadLength; - private Fin _fin; - private Mask _mask; - private byte[] _maskingKey; - private Opcode _opcode; - private PayloadData _payloadData; - private byte _payloadLength; - private Rsv _rsv1; - private Rsv _rsv2; - private Rsv _rsv3; - - #endregion - - #region Internal Fields - /// - /// Represents the ping frame without the payload data as an array of . + /// Defines the /// - /// - /// The value of this field is created from a non masked frame, so it can only be used to - /// send a ping from a server. - /// - internal static readonly byte[] EmptyPingBytes; - - #endregion + internal class WebSocketFrame : IEnumerable + { + #region ֶ + + /// + /// Represents the ping frame without the payload data as an array of . + /// + internal static readonly byte[] EmptyPingBytes; + + /// + /// Defines the _extPayloadLength + /// + private byte[] _extPayloadLength; + + /// + /// Defines the _fin + /// + private Fin _fin; + + /// + /// Defines the _mask + /// + private Mask _mask; + + /// + /// Defines the _maskingKey + /// + private byte[] _maskingKey; + + /// + /// Defines the _opcode + /// + private Opcode _opcode; + + /// + /// Defines the _payloadData + /// + private PayloadData _payloadData; + + /// + /// Defines the _payloadLength + /// + private byte _payloadLength; + + /// + /// Defines the _rsv1 + /// + private Rsv _rsv1; + + /// + /// Defines the _rsv2 + /// + private Rsv _rsv2; + + /// + /// Defines the _rsv3 + /// + private Rsv _rsv3; + + #endregion ֶ + + #region 캯 + + /// + /// Initializes a new instance of the class. + /// + /// The fin + /// The opcode + /// The payloadData + /// The compressed + /// The mask + internal WebSocketFrame( + Fin fin, Opcode opcode, PayloadData payloadData, bool compressed, bool mask) + { + _fin = fin; + _rsv1 = opcode.IsData() && compressed ? Rsv.On : Rsv.Off; + _rsv2 = Rsv.Off; + _rsv3 = Rsv.Off; + _opcode = opcode; + + var len = payloadData.Length; + if (len < 126) + { + _payloadLength = (byte)len; + _extPayloadLength = WebSocket.EmptyBytes; + } + else if (len < 0x010000) + { + _payloadLength = (byte)126; + _extPayloadLength = ((ushort)len).InternalToByteArray(ByteOrder.Big); + } + else + { + _payloadLength = (byte)127; + _extPayloadLength = len.InternalToByteArray(ByteOrder.Big); + } + + if (mask) + { + _mask = Mask.On; + _maskingKey = createMaskingKey(); + payloadData.Mask(_maskingKey); + } + else + { + _mask = Mask.Off; + _maskingKey = WebSocket.EmptyBytes; + } + + _payloadData = payloadData; + } - #region Static Constructor + /// + /// Initializes a new instance of the class. + /// + /// The fin + /// The opcode + /// The data + /// The compressed + /// The mask + internal WebSocketFrame(Fin fin, Opcode opcode, byte[] data, bool compressed, bool mask) + : this(fin, opcode, new PayloadData(data), compressed, mask) + { + } - static WebSocketFrame () - { - EmptyPingBytes = CreatePingFrame (false).ToArray (); - } + /// + /// Initializes a new instance of the class. + /// + /// The opcode + /// The payloadData + /// The mask + internal WebSocketFrame(Opcode opcode, PayloadData payloadData, bool mask) + : this(Fin.Final, opcode, payloadData, false, mask) + { + } - #endregion + /// + /// Prevents a default instance of the class from being created. + /// + private WebSocketFrame() + { + } - #region Private Constructors + /// + /// Initializes static members of the class. + /// + static WebSocketFrame() + { + EmptyPingBytes = CreatePingFrame(false).ToArray(); + } - private WebSocketFrame () - { - } + #endregion 캯 - #endregion + #region - #region Internal Constructors + /// + /// Gets the ExtendedPayloadLength + /// + public byte[] ExtendedPayloadLength + { + get + { + return _extPayloadLength; + } + } - internal WebSocketFrame (Opcode opcode, PayloadData payloadData, bool mask) - : this (Fin.Final, opcode, payloadData, false, mask) - { - } + /// + /// Gets the Fin + /// + public Fin Fin + { + get + { + return _fin; + } + } - internal WebSocketFrame (Fin fin, Opcode opcode, byte[] data, bool compressed, bool mask) - : this (fin, opcode, new PayloadData (data), compressed, mask) - { - } + /// + /// Gets a value indicating whether IsBinary + /// + public bool IsBinary + { + get + { + return _opcode == Opcode.Binary; + } + } - internal WebSocketFrame ( - Fin fin, Opcode opcode, PayloadData payloadData, bool compressed, bool mask) - { - _fin = fin; - _rsv1 = opcode.IsData () && compressed ? Rsv.On : Rsv.Off; - _rsv2 = Rsv.Off; - _rsv3 = Rsv.Off; - _opcode = opcode; - - var len = payloadData.Length; - if (len < 126) { - _payloadLength = (byte) len; - _extPayloadLength = WebSocket.EmptyBytes; - } - else if (len < 0x010000) { - _payloadLength = (byte) 126; - _extPayloadLength = ((ushort) len).InternalToByteArray (ByteOrder.Big); - } - else { - _payloadLength = (byte) 127; - _extPayloadLength = len.InternalToByteArray (ByteOrder.Big); - } - - if (mask) { - _mask = Mask.On; - _maskingKey = createMaskingKey (); - payloadData.Mask (_maskingKey); - } - else { - _mask = Mask.Off; - _maskingKey = WebSocket.EmptyBytes; - } - - _payloadData = payloadData; - } + /// + /// Gets a value indicating whether IsClose + /// + public bool IsClose + { + get + { + return _opcode == Opcode.Close; + } + } - #endregion + /// + /// Gets a value indicating whether IsCompressed + /// + public bool IsCompressed + { + get + { + return _rsv1 == Rsv.On; + } + } - #region Internal Properties + /// + /// Gets a value indicating whether IsContinuation + /// + public bool IsContinuation + { + get + { + return _opcode == Opcode.Cont; + } + } - internal int ExtendedPayloadLengthCount { - get { - return _payloadLength < 126 ? 0 : (_payloadLength == 126 ? 2 : 8); - } - } + /// + /// Gets a value indicating whether IsControl + /// + public bool IsControl + { + get + { + return _opcode >= Opcode.Close; + } + } - internal ulong FullPayloadLength { - get { - return _payloadLength < 126 - ? _payloadLength - : _payloadLength == 126 - ? _extPayloadLength.ToUInt16 (ByteOrder.Big) - : _extPayloadLength.ToUInt64 (ByteOrder.Big); - } - } + /// + /// Gets a value indicating whether IsData + /// + public bool IsData + { + get + { + return _opcode == Opcode.Text || _opcode == Opcode.Binary; + } + } - #endregion + /// + /// Gets a value indicating whether IsFinal + /// + public bool IsFinal + { + get + { + return _fin == Fin.Final; + } + } - #region Public Properties + /// + /// Gets a value indicating whether IsFragment + /// + public bool IsFragment + { + get + { + return _fin == Fin.More || _opcode == Opcode.Cont; + } + } - public byte[] ExtendedPayloadLength { - get { - return _extPayloadLength; - } - } + /// + /// Gets a value indicating whether IsMasked + /// + public bool IsMasked + { + get + { + return _mask == Mask.On; + } + } - public Fin Fin { - get { - return _fin; - } - } + /// + /// Gets a value indicating whether IsPing + /// + public bool IsPing + { + get + { + return _opcode == Opcode.Ping; + } + } - public bool IsBinary { - get { - return _opcode == Opcode.Binary; - } - } + /// + /// Gets a value indicating whether IsPong + /// + public bool IsPong + { + get + { + return _opcode == Opcode.Pong; + } + } - public bool IsClose { - get { - return _opcode == Opcode.Close; - } - } + /// + /// Gets a value indicating whether IsText + /// + public bool IsText + { + get + { + return _opcode == Opcode.Text; + } + } - public bool IsCompressed { - get { - return _rsv1 == Rsv.On; - } - } + /// + /// Gets the Length + /// + public ulong Length + { + get + { + return 2 + (ulong)(_extPayloadLength.Length + _maskingKey.Length) + _payloadData.Length; + } + } - public bool IsContinuation { - get { - return _opcode == Opcode.Cont; - } - } + /// + /// Gets the Mask + /// + public Mask Mask + { + get + { + return _mask; + } + } - public bool IsControl { - get { - return _opcode >= Opcode.Close; - } - } + /// + /// Gets the MaskingKey + /// + public byte[] MaskingKey + { + get + { + return _maskingKey; + } + } - public bool IsData { - get { - return _opcode == Opcode.Text || _opcode == Opcode.Binary; - } - } + /// + /// Gets the Opcode + /// + public Opcode Opcode + { + get + { + return _opcode; + } + } - public bool IsFinal { - get { - return _fin == Fin.Final; - } - } + /// + /// Gets the PayloadData + /// + public PayloadData PayloadData + { + get + { + return _payloadData; + } + } - public bool IsFragment { - get { - return _fin == Fin.More || _opcode == Opcode.Cont; - } - } + /// + /// Gets the PayloadLength + /// + public byte PayloadLength + { + get + { + return _payloadLength; + } + } - public bool IsMasked { - get { - return _mask == Mask.On; - } - } + /// + /// Gets the Rsv1 + /// + public Rsv Rsv1 + { + get + { + return _rsv1; + } + } - public bool IsPing { - get { - return _opcode == Opcode.Ping; - } - } + /// + /// Gets the Rsv2 + /// + public Rsv Rsv2 + { + get + { + return _rsv2; + } + } - public bool IsPong { - get { - return _opcode == Opcode.Pong; - } - } + /// + /// Gets the Rsv3 + /// + public Rsv Rsv3 + { + get + { + return _rsv3; + } + } - public bool IsText { - get { - return _opcode == Opcode.Text; - } - } + /// + /// Gets the ExtendedPayloadLengthCount + /// + internal int ExtendedPayloadLengthCount + { + get + { + return _payloadLength < 126 ? 0 : (_payloadLength == 126 ? 2 : 8); + } + } - public ulong Length { - get { - return 2 + (ulong) (_extPayloadLength.Length + _maskingKey.Length) + _payloadData.Length; - } - } + /// + /// Gets the FullPayloadLength + /// + internal ulong FullPayloadLength + { + get + { + return _payloadLength < 126 + ? _payloadLength + : _payloadLength == 126 + ? _extPayloadLength.ToUInt16(ByteOrder.Big) + : _extPayloadLength.ToUInt64(ByteOrder.Big); + } + } - public Mask Mask { - get { - return _mask; - } - } + #endregion - public byte[] MaskingKey { - get { - return _maskingKey; - } - } + #region - public Opcode Opcode { - get { - return _opcode; - } - } + /// + /// The GetEnumerator + /// + /// The + public IEnumerator GetEnumerator() + { + foreach (var b in ToArray()) + yield return b; + } - public PayloadData PayloadData { - get { - return _payloadData; - } - } + /// + /// The Print + /// + /// The dumped + public void Print(bool dumped) + { + Console.WriteLine(dumped ? dump(this) : print(this)); + } - public byte PayloadLength { - get { - return _payloadLength; - } - } + /// + /// The PrintToString + /// + /// The dumped + /// The + public string PrintToString(bool dumped) + { + return dumped ? dump(this) : print(this); + } - public Rsv Rsv1 { - get { - return _rsv1; - } - } + /// + /// The ToArray + /// + /// The + public byte[] ToArray() + { + using (var buff = new MemoryStream()) + { + var header = (int)_fin; + header = (header << 1) + (int)_rsv1; + header = (header << 1) + (int)_rsv2; + header = (header << 1) + (int)_rsv3; + header = (header << 4) + (int)_opcode; + header = (header << 1) + (int)_mask; + header = (header << 7) + (int)_payloadLength; + buff.Write(((ushort)header).InternalToByteArray(ByteOrder.Big), 0, 2); + + if (_payloadLength > 125) + buff.Write(_extPayloadLength, 0, _payloadLength == 126 ? 2 : 8); + + if (_mask == Mask.On) + buff.Write(_maskingKey, 0, 4); + + if (_payloadLength > 0) + { + var bytes = _payloadData.ToArray(); + if (_payloadLength < 127) + buff.Write(bytes, 0, bytes.Length); + else + buff.WriteBytes(bytes, 1024); + } + + buff.Close(); + return buff.ToArray(); + } + } - public Rsv Rsv2 { - get { - return _rsv2; - } - } + /// + /// The ToString + /// + /// The + public override string ToString() + { + return BitConverter.ToString(ToArray()); + } - public Rsv Rsv3 { - get { - return _rsv3; - } - } + /// + /// The CreateCloseFrame + /// + /// The payloadData + /// The mask + /// The + internal static WebSocketFrame CreateCloseFrame( + PayloadData payloadData, bool mask + ) + { + return new WebSocketFrame( + Fin.Final, Opcode.Close, payloadData, false, mask + ); + } - #endregion + /// + /// The CreatePingFrame + /// + /// The mask + /// The + internal static WebSocketFrame CreatePingFrame(bool mask) + { + return new WebSocketFrame( + Fin.Final, Opcode.Ping, PayloadData.Empty, false, mask + ); + } - #region Private Methods + /// + /// The CreatePingFrame + /// + /// The data + /// The mask + /// The + internal static WebSocketFrame CreatePingFrame(byte[] data, bool mask) + { + return new WebSocketFrame( + Fin.Final, Opcode.Ping, new PayloadData(data), false, mask + ); + } - private static byte[] createMaskingKey () - { - var key = new byte[4]; - WebSocket.RandomNumber.GetBytes (key); + /// + /// The CreatePongFrame + /// + /// The payloadData + /// The mask + /// The + internal static WebSocketFrame CreatePongFrame( + PayloadData payloadData, bool mask + ) + { + return new WebSocketFrame( + Fin.Final, Opcode.Pong, payloadData, false, mask + ); + } - return key; - } + /// + /// The ReadFrame + /// + /// The stream + /// The unmask + /// The + internal static WebSocketFrame ReadFrame(Stream stream, bool unmask) + { + var frame = readHeader(stream); + readExtendedPayloadLength(stream, frame); + readMaskingKey(stream, frame); + readPayloadData(stream, frame); + + if (unmask) + frame.Unmask(); + + return frame; + } - private static string dump (WebSocketFrame frame) - { - var len = frame.Length; - var cnt = (long) (len / 4); - var rem = (int) (len % 4); - - int cntDigit; - string cntFmt; - if (cnt < 10000) { - cntDigit = 4; - cntFmt = "{0,4}"; - } - else if (cnt < 0x010000) { - cntDigit = 4; - cntFmt = "{0,4:X}"; - } - else if (cnt < 0x0100000000) { - cntDigit = 8; - cntFmt = "{0,8:X}"; - } - else { - cntDigit = 16; - cntFmt = "{0,16:X}"; - } - - var spFmt = String.Format ("{{0,{0}}}", cntDigit); - var headerFmt = String.Format (@" -{0} 01234567 89ABCDEF 01234567 89ABCDEF -{0}+--------+--------+--------+--------+\n", spFmt); - var lineFmt = String.Format ("{0}|{{1,8}} {{2,8}} {{3,8}} {{4,8}}|\n", cntFmt); - var footerFmt = String.Format ("{0}+--------+--------+--------+--------+", spFmt); - - var output = new StringBuilder (64); - Func> linePrinter = () => { - long lineCnt = 0; - return (arg1, arg2, arg3, arg4) => - output.AppendFormat (lineFmt, ++lineCnt, arg1, arg2, arg3, arg4); - }; - var printLine = linePrinter (); - - output.AppendFormat (headerFmt, String.Empty); - - var bytes = frame.ToArray (); - for (long i = 0; i <= cnt; i++) { - var j = i * 4; - if (i < cnt) { - printLine ( - Convert.ToString (bytes[j], 2).PadLeft (8, '0'), - Convert.ToString (bytes[j + 1], 2).PadLeft (8, '0'), - Convert.ToString (bytes[j + 2], 2).PadLeft (8, '0'), - Convert.ToString (bytes[j + 3], 2).PadLeft (8, '0')); - - continue; - } - - if (rem > 0) - printLine ( - Convert.ToString (bytes[j], 2).PadLeft (8, '0'), - rem >= 2 ? Convert.ToString (bytes[j + 1], 2).PadLeft (8, '0') : String.Empty, - rem == 3 ? Convert.ToString (bytes[j + 2], 2).PadLeft (8, '0') : String.Empty, - String.Empty); - } - - output.AppendFormat (footerFmt, String.Empty); - return output.ToString (); - } + /// + /// The ReadFrameAsync + /// + /// The stream + /// The unmask + /// The completed + /// The error + internal static void ReadFrameAsync( + Stream stream, + bool unmask, + Action completed, + Action error + ) + { + readHeaderAsync( + stream, + frame => + readExtendedPayloadLengthAsync( + stream, + frame, + frame1 => + readMaskingKeyAsync( + stream, + frame1, + frame2 => + readPayloadDataAsync( + stream, + frame2, + frame3 => + { + if (unmask) + frame3.Unmask(); + + completed(frame3); + }, + error + ), + error + ), + error + ), + error + ); + } - private static string print (WebSocketFrame frame) - { - // Payload Length - var payloadLen = frame._payloadLength; + /// + /// The Unmask + /// + internal void Unmask() + { + if (_mask == Mask.Off) + return; + + _mask = Mask.Off; + _payloadData.Mask(_maskingKey); + _maskingKey = WebSocket.EmptyBytes; + } - // Extended Payload Length - var extPayloadLen = payloadLen > 125 ? frame.FullPayloadLength.ToString () : String.Empty; + /// + /// The createMaskingKey + /// + /// The + private static byte[] createMaskingKey() + { + var key = new byte[4]; + WebSocket.RandomNumber.GetBytes(key); - // Masking Key - var maskingKey = BitConverter.ToString (frame._maskingKey); + return key; + } - // Payload Data - var payload = payloadLen == 0 - ? String.Empty - : payloadLen > 125 - ? "---" - : frame.IsText && !(frame.IsFragment || frame.IsMasked || frame.IsCompressed) - ? frame._payloadData.ApplicationData.UTF8Decode () - : frame._payloadData.ToString (); + /// + /// The dump + /// + /// The frame + /// The + private static string dump(WebSocketFrame frame) + { + var len = frame.Length; + var cnt = (long)(len / 4); + var rem = (int)(len % 4); + + int cntDigit; + string cntFmt; + if (cnt < 10000) + { + cntDigit = 4; + cntFmt = "{0,4}"; + } + else if (cnt < 0x010000) + { + cntDigit = 4; + cntFmt = "{0,4:X}"; + } + else if (cnt < 0x0100000000) + { + cntDigit = 8; + cntFmt = "{0,8:X}"; + } + else + { + cntDigit = 16; + cntFmt = "{0,16:X}"; + } + + var spFmt = String.Format("{{0,{0}}}", cntDigit); + var headerFmt = String.Format(@" +{0} 01234567 89ABCDEF 01234567 89ABCDEF +{0}+--------+--------+--------+--------+\n", spFmt); + var lineFmt = String.Format("{0}|{{1,8}} {{2,8}} {{3,8}} {{4,8}}|\n", cntFmt); + var footerFmt = String.Format("{0}+--------+--------+--------+--------+", spFmt); + + var output = new StringBuilder(64); + Func> linePrinter = () => + { + long lineCnt = 0; + return (arg1, arg2, arg3, arg4) => + output.AppendFormat(lineFmt, ++lineCnt, arg1, arg2, arg3, arg4); + }; + var printLine = linePrinter(); + + output.AppendFormat(headerFmt, String.Empty); + + var bytes = frame.ToArray(); + for (long i = 0; i <= cnt; i++) + { + var j = i * 4; + if (i < cnt) + { + printLine( + Convert.ToString(bytes[j], 2).PadLeft(8, '0'), + Convert.ToString(bytes[j + 1], 2).PadLeft(8, '0'), + Convert.ToString(bytes[j + 2], 2).PadLeft(8, '0'), + Convert.ToString(bytes[j + 3], 2).PadLeft(8, '0')); + + continue; + } + + if (rem > 0) + printLine( + Convert.ToString(bytes[j], 2).PadLeft(8, '0'), + rem >= 2 ? Convert.ToString(bytes[j + 1], 2).PadLeft(8, '0') : String.Empty, + rem == 3 ? Convert.ToString(bytes[j + 2], 2).PadLeft(8, '0') : String.Empty, + String.Empty); + } + + output.AppendFormat(footerFmt, String.Empty); + return output.ToString(); + } - var fmt = @" + /// + /// The print + /// + /// The frame + /// The + private static string print(WebSocketFrame frame) + { + // Payload Length + var payloadLen = frame._payloadLength; + + // Extended Payload Length + var extPayloadLen = payloadLen > 125 ? frame.FullPayloadLength.ToString() : String.Empty; + + // Masking Key + var maskingKey = BitConverter.ToString(frame._maskingKey); + + // Payload Data + var payload = payloadLen == 0 + ? String.Empty + : payloadLen > 125 + ? "---" + : frame.IsText && !(frame.IsFragment || frame.IsMasked || frame.IsCompressed) + ? frame._payloadData.ApplicationData.UTF8Decode() + : frame._payloadData.ToString(); + + var fmt = @" FIN: {0} RSV1: {1} RSV2: {2} @@ -409,389 +813,302 @@ private static string print (WebSocketFrame frame) Masking Key: {8} Payload Data: {9}"; - return String.Format ( - fmt, - frame._fin, - frame._rsv1, - frame._rsv2, - frame._rsv3, - frame._opcode, - frame._mask, - payloadLen, - extPayloadLen, - maskingKey, - payload); - } - - private static WebSocketFrame processHeader (byte[] header) - { - if (header.Length != 2) - throw new WebSocketException ("The header of a frame cannot be read from the stream."); - - // FIN - var fin = (header[0] & 0x80) == 0x80 ? Fin.Final : Fin.More; - - // RSV1 - var rsv1 = (header[0] & 0x40) == 0x40 ? Rsv.On : Rsv.Off; - - // RSV2 - var rsv2 = (header[0] & 0x20) == 0x20 ? Rsv.On : Rsv.Off; - - // RSV3 - var rsv3 = (header[0] & 0x10) == 0x10 ? Rsv.On : Rsv.Off; - - // Opcode - var opcode = (byte) (header[0] & 0x0f); - - // MASK - var mask = (header[1] & 0x80) == 0x80 ? Mask.On : Mask.Off; - - // Payload Length - var payloadLen = (byte) (header[1] & 0x7f); - - var err = !opcode.IsSupported () - ? "An unsupported opcode." - : !opcode.IsData () && rsv1 == Rsv.On - ? "A non data frame is compressed." - : opcode.IsControl () && fin == Fin.More - ? "A control frame is fragmented." - : opcode.IsControl () && payloadLen > 125 - ? "A control frame has a long payload length." - : null; - - if (err != null) - throw new WebSocketException (CloseStatusCode.ProtocolError, err); - - var frame = new WebSocketFrame (); - frame._fin = fin; - frame._rsv1 = rsv1; - frame._rsv2 = rsv2; - frame._rsv3 = rsv3; - frame._opcode = (Opcode) opcode; - frame._mask = mask; - frame._payloadLength = payloadLen; + return String.Format( + fmt, + frame._fin, + frame._rsv1, + frame._rsv2, + frame._rsv3, + frame._opcode, + frame._mask, + payloadLen, + extPayloadLen, + maskingKey, + payload); + } - return frame; - } + /// + /// The processHeader + /// + /// The header + /// The + private static WebSocketFrame processHeader(byte[] header) + { + if (header.Length != 2) + throw new WebSocketException("The header of a frame cannot be read from the stream."); + + // FIN + var fin = (header[0] & 0x80) == 0x80 ? Fin.Final : Fin.More; + + // RSV1 + var rsv1 = (header[0] & 0x40) == 0x40 ? Rsv.On : Rsv.Off; + + // RSV2 + var rsv2 = (header[0] & 0x20) == 0x20 ? Rsv.On : Rsv.Off; + + // RSV3 + var rsv3 = (header[0] & 0x10) == 0x10 ? Rsv.On : Rsv.Off; + + // Opcode + var opcode = (byte)(header[0] & 0x0f); + + // MASK + var mask = (header[1] & 0x80) == 0x80 ? Mask.On : Mask.Off; + + // Payload Length + var payloadLen = (byte)(header[1] & 0x7f); + + var err = !opcode.IsSupported() + ? "An unsupported opcode." + : !opcode.IsData() && rsv1 == Rsv.On + ? "A non data frame is compressed." + : opcode.IsControl() && fin == Fin.More + ? "A control frame is fragmented." + : opcode.IsControl() && payloadLen > 125 + ? "A control frame has a long payload length." + : null; + + if (err != null) + throw new WebSocketException(CloseStatusCode.ProtocolError, err); + + var frame = new WebSocketFrame(); + frame._fin = fin; + frame._rsv1 = rsv1; + frame._rsv2 = rsv2; + frame._rsv3 = rsv3; + frame._opcode = (Opcode)opcode; + frame._mask = mask; + frame._payloadLength = payloadLen; + + return frame; + } - private static WebSocketFrame readExtendedPayloadLength (Stream stream, WebSocketFrame frame) - { - var len = frame.ExtendedPayloadLengthCount; - if (len == 0) { - frame._extPayloadLength = WebSocket.EmptyBytes; - return frame; - } - - var bytes = stream.ReadBytes (len); - if (bytes.Length != len) - throw new WebSocketException ( - "The extended payload length of a frame cannot be read from the stream."); - - frame._extPayloadLength = bytes; - return frame; - } + /// + /// The readExtendedPayloadLength + /// + /// The stream + /// The frame + /// The + private static WebSocketFrame readExtendedPayloadLength(Stream stream, WebSocketFrame frame) + { + var len = frame.ExtendedPayloadLengthCount; + if (len == 0) + { + frame._extPayloadLength = WebSocket.EmptyBytes; + return frame; + } + + var bytes = stream.ReadBytes(len); + if (bytes.Length != len) + throw new WebSocketException( + "The extended payload length of a frame cannot be read from the stream."); + + frame._extPayloadLength = bytes; + return frame; + } - private static void readExtendedPayloadLengthAsync ( + /// + /// The readExtendedPayloadLengthAsync + /// + /// The stream + /// The frame + /// The completed + /// The error + private static void readExtendedPayloadLengthAsync( Stream stream, WebSocketFrame frame, Action completed, Action error) - { - var len = frame.ExtendedPayloadLengthCount; - if (len == 0) { - frame._extPayloadLength = WebSocket.EmptyBytes; - completed (frame); - - return; - } - - stream.ReadBytesAsync ( - len, - bytes => { - if (bytes.Length != len) - throw new WebSocketException ( - "The extended payload length of a frame cannot be read from the stream."); - - frame._extPayloadLength = bytes; - completed (frame); - }, - error); - } + { + var len = frame.ExtendedPayloadLengthCount; + if (len == 0) + { + frame._extPayloadLength = WebSocket.EmptyBytes; + completed(frame); + + return; + } + + stream.ReadBytesAsync( + len, + bytes => + { + if (bytes.Length != len) + throw new WebSocketException( + "The extended payload length of a frame cannot be read from the stream."); + + frame._extPayloadLength = bytes; + completed(frame); + }, + error); + } - private static WebSocketFrame readHeader (Stream stream) - { - return processHeader (stream.ReadBytes (2)); - } + /// + /// The readHeader + /// + /// The stream + /// The + private static WebSocketFrame readHeader(Stream stream) + { + return processHeader(stream.ReadBytes(2)); + } - private static void readHeaderAsync ( + /// + /// The readHeaderAsync + /// + /// The stream + /// The completed + /// The error + private static void readHeaderAsync( Stream stream, Action completed, Action error) - { - stream.ReadBytesAsync (2, bytes => completed (processHeader (bytes)), error); - } + { + stream.ReadBytesAsync(2, bytes => completed(processHeader(bytes)), error); + } - private static WebSocketFrame readMaskingKey (Stream stream, WebSocketFrame frame) - { - var len = frame.IsMasked ? 4 : 0; - if (len == 0) { - frame._maskingKey = WebSocket.EmptyBytes; - return frame; - } - - var bytes = stream.ReadBytes (len); - if (bytes.Length != len) - throw new WebSocketException ("The masking key of a frame cannot be read from the stream."); - - frame._maskingKey = bytes; - return frame; - } + /// + /// The readMaskingKey + /// + /// The stream + /// The frame + /// The + private static WebSocketFrame readMaskingKey(Stream stream, WebSocketFrame frame) + { + var len = frame.IsMasked ? 4 : 0; + if (len == 0) + { + frame._maskingKey = WebSocket.EmptyBytes; + return frame; + } + + var bytes = stream.ReadBytes(len); + if (bytes.Length != len) + throw new WebSocketException("The masking key of a frame cannot be read from the stream."); + + frame._maskingKey = bytes; + return frame; + } - private static void readMaskingKeyAsync ( + /// + /// The readMaskingKeyAsync + /// + /// The stream + /// The frame + /// The completed + /// The error + private static void readMaskingKeyAsync( Stream stream, WebSocketFrame frame, Action completed, Action error) - { - var len = frame.IsMasked ? 4 : 0; - if (len == 0) { - frame._maskingKey = WebSocket.EmptyBytes; - completed (frame); - - return; - } - - stream.ReadBytesAsync ( - len, - bytes => { - if (bytes.Length != len) - throw new WebSocketException ( - "The masking key of a frame cannot be read from the stream."); - - frame._maskingKey = bytes; - completed (frame); - }, - error); - } + { + var len = frame.IsMasked ? 4 : 0; + if (len == 0) + { + frame._maskingKey = WebSocket.EmptyBytes; + completed(frame); + + return; + } + + stream.ReadBytesAsync( + len, + bytes => + { + if (bytes.Length != len) + throw new WebSocketException( + "The masking key of a frame cannot be read from the stream."); + + frame._maskingKey = bytes; + completed(frame); + }, + error); + } - private static WebSocketFrame readPayloadData (Stream stream, WebSocketFrame frame) - { - var len = frame.FullPayloadLength; - if (len == 0) { - frame._payloadData = PayloadData.Empty; - return frame; - } - - if (len > PayloadData.MaxLength) - throw new WebSocketException (CloseStatusCode.TooBig, "A frame has a long payload length."); - - var llen = (long) len; - var bytes = frame._payloadLength < 127 - ? stream.ReadBytes ((int) len) - : stream.ReadBytes (llen, 1024); - - if (bytes.LongLength != llen) - throw new WebSocketException ( - "The payload data of a frame cannot be read from the stream."); - - frame._payloadData = new PayloadData (bytes, llen); - return frame; - } + /// + /// The readPayloadData + /// + /// The stream + /// The frame + /// The + private static WebSocketFrame readPayloadData(Stream stream, WebSocketFrame frame) + { + var len = frame.FullPayloadLength; + if (len == 0) + { + frame._payloadData = PayloadData.Empty; + return frame; + } + + if (len > PayloadData.MaxLength) + throw new WebSocketException(CloseStatusCode.TooBig, "A frame has a long payload length."); + + var llen = (long)len; + var bytes = frame._payloadLength < 127 + ? stream.ReadBytes((int)len) + : stream.ReadBytes(llen, 1024); + + if (bytes.LongLength != llen) + throw new WebSocketException( + "The payload data of a frame cannot be read from the stream."); + + frame._payloadData = new PayloadData(bytes, llen); + return frame; + } - private static void readPayloadDataAsync ( + /// + /// The readPayloadDataAsync + /// + /// The stream + /// The frame + /// The completed + /// The error + private static void readPayloadDataAsync( Stream stream, WebSocketFrame frame, Action completed, Action error) - { - var len = frame.FullPayloadLength; - if (len == 0) { - frame._payloadData = PayloadData.Empty; - completed (frame); - - return; - } - - if (len > PayloadData.MaxLength) - throw new WebSocketException (CloseStatusCode.TooBig, "A frame has a long payload length."); - - var llen = (long) len; - Action compl = bytes => { - if (bytes.LongLength != llen) - throw new WebSocketException ( - "The payload data of a frame cannot be read from the stream."); - - frame._payloadData = new PayloadData (bytes, llen); - completed (frame); - }; - - if (frame._payloadLength < 127) { - stream.ReadBytesAsync ((int) len, compl, error); - return; - } - - stream.ReadBytesAsync (llen, 1024, compl, error); - } - - #endregion - - #region Internal Methods - - internal static WebSocketFrame CreateCloseFrame ( - PayloadData payloadData, bool mask - ) - { - return new WebSocketFrame ( - Fin.Final, Opcode.Close, payloadData, false, mask - ); - } - - internal static WebSocketFrame CreatePingFrame (bool mask) - { - return new WebSocketFrame ( - Fin.Final, Opcode.Ping, PayloadData.Empty, false, mask - ); - } - - internal static WebSocketFrame CreatePingFrame (byte[] data, bool mask) - { - return new WebSocketFrame ( - Fin.Final, Opcode.Ping, new PayloadData (data), false, mask - ); - } - - internal static WebSocketFrame CreatePongFrame ( - PayloadData payloadData, bool mask - ) - { - return new WebSocketFrame ( - Fin.Final, Opcode.Pong, payloadData, false, mask - ); - } - - internal static WebSocketFrame ReadFrame (Stream stream, bool unmask) - { - var frame = readHeader (stream); - readExtendedPayloadLength (stream, frame); - readMaskingKey (stream, frame); - readPayloadData (stream, frame); - - if (unmask) - frame.Unmask (); - - return frame; - } - - internal static void ReadFrameAsync ( - Stream stream, - bool unmask, - Action completed, - Action error - ) - { - readHeaderAsync ( - stream, - frame => - readExtendedPayloadLengthAsync ( - stream, - frame, - frame1 => - readMaskingKeyAsync ( - stream, - frame1, - frame2 => - readPayloadDataAsync ( - stream, - frame2, - frame3 => { - if (unmask) - frame3.Unmask (); - - completed (frame3); - }, - error - ), - error - ), - error - ), - error - ); - } - - internal void Unmask () - { - if (_mask == Mask.Off) - return; - - _mask = Mask.Off; - _payloadData.Mask (_maskingKey); - _maskingKey = WebSocket.EmptyBytes; - } - - #endregion - - #region Public Methods - - public IEnumerator GetEnumerator () - { - foreach (var b in ToArray ()) - yield return b; - } - - public void Print (bool dumped) - { - Console.WriteLine (dumped ? dump (this) : print (this)); - } - - public string PrintToString (bool dumped) - { - return dumped ? dump (this) : print (this); - } - - public byte[] ToArray () - { - using (var buff = new MemoryStream ()) { - var header = (int) _fin; - header = (header << 1) + (int) _rsv1; - header = (header << 1) + (int) _rsv2; - header = (header << 1) + (int) _rsv3; - header = (header << 4) + (int) _opcode; - header = (header << 1) + (int) _mask; - header = (header << 7) + (int) _payloadLength; - buff.Write (((ushort) header).InternalToByteArray (ByteOrder.Big), 0, 2); - - if (_payloadLength > 125) - buff.Write (_extPayloadLength, 0, _payloadLength == 126 ? 2 : 8); - - if (_mask == Mask.On) - buff.Write (_maskingKey, 0, 4); - - if (_payloadLength > 0) { - var bytes = _payloadData.ToArray (); - if (_payloadLength < 127) - buff.Write (bytes, 0, bytes.Length); - else - buff.WriteBytes (bytes, 1024); - } - - buff.Close (); - return buff.ToArray (); - } - } - - public override string ToString () - { - return BitConverter.ToString (ToArray ()); - } - - #endregion + { + var len = frame.FullPayloadLength; + if (len == 0) + { + frame._payloadData = PayloadData.Empty; + completed(frame); + + return; + } + + if (len > PayloadData.MaxLength) + throw new WebSocketException(CloseStatusCode.TooBig, "A frame has a long payload length."); + + var llen = (long)len; + Action compl = bytes => + { + if (bytes.LongLength != llen) + throw new WebSocketException( + "The payload data of a frame cannot be read from the stream."); + + frame._payloadData = new PayloadData(bytes, llen); + completed(frame); + }; + + if (frame._payloadLength < 127) + { + stream.ReadBytesAsync((int)len, compl, error); + return; + } + + stream.ReadBytesAsync(llen, 1024, compl, error); + } - #region Explicit Interface Implementations + /// + /// The GetEnumerator + /// + /// The + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } - IEnumerator IEnumerable.GetEnumerator () - { - return GetEnumerator (); + #endregion } - - #endregion - } -} +} \ No newline at end of file diff --git a/src/WebSocket/WebSocketCore/WebSocketState.cs b/src/WebSocket/WebSocketCore/WebSocketState.cs index 192d9df30..3976f1b54 100644 --- a/src/WebSocket/WebSocketCore/WebSocketState.cs +++ b/src/WebSocket/WebSocketCore/WebSocketState.cs @@ -1,4 +1,3 @@ -#region License /* * WebSocketState.cs * @@ -24,42 +23,48 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#endregion using System; namespace WebSocketCore { - /// - /// Indicates the state of a WebSocket connection. - /// - /// - /// The values of this enumeration are defined in - /// - /// The WebSocket API. - /// - public enum WebSocketState : ushort - { - /// - /// Equivalent to numeric value 0. Indicates that the connection has not - /// yet been established. - /// - Connecting = 0, - /// - /// Equivalent to numeric value 1. Indicates that the connection has - /// been established, and the communication is possible. - /// - Open = 1, - /// - /// Equivalent to numeric value 2. Indicates that the connection is - /// going through the closing handshake, or the close method has - /// been invoked. - /// - Closing = 2, + #region ö + /// - /// Equivalent to numeric value 3. Indicates that the connection has - /// been closed or could not be established. + /// Indicates the state of a WebSocket connection. /// - Closed = 3 - } -} + /// + /// The values of this enumeration are defined in + /// + /// The WebSocket API. + /// + public enum WebSocketState : ushort + { + /// + /// Equivalent to numeric value 0. Indicates that the connection has not + /// yet been established. + /// + Connecting = 0, + + /// + /// Equivalent to numeric value 1. Indicates that the connection has + /// been established, and the communication is possible. + /// + Open = 1, + + /// + /// Equivalent to numeric value 2. Indicates that the connection is + /// going through the closing handshake, or the close method has + /// been invoked. + /// + Closing = 2, + + /// + /// Equivalent to numeric value 3. Indicates that the connection has + /// been closed or could not be established. + /// + Closed = 3 + } + + #endregion ö +} \ No newline at end of file diff --git a/src/packages/SharedSolutionFiles/SharedAssemblyInfo.cs b/src/packages/SharedSolutionFiles/SharedAssemblyInfo.cs index 3b20d1964..7146af105 100644 --- a/src/packages/SharedSolutionFiles/SharedAssemblyInfo.cs +++ b/src/packages/SharedSolutionFiles/SharedAssemblyInfo.cs @@ -19,7 +19,7 @@ // 程序集的版本信息由下面四个值组成: // // 主版本 -// 次版本 +// 次版本 // 内部版本号 // 修订号 // @@ -27,4 +27,4 @@ // 方法是按如下所示使用“*”: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("0.4.7.0")] -[assembly: AssemblyFileVersion("0.4.7.0")] +[assembly: AssemblyFileVersion("0.4.7.0")] \ No newline at end of file