diff --git a/src/SuperSocket.ProtoBase/ProxyProtocol/ProxyCommand.cs b/src/SuperSocket.ProtoBase/ProxyProtocol/ProxyCommand.cs
new file mode 100644
index 000000000..9e94fc669
--- /dev/null
+++ b/src/SuperSocket.ProtoBase/ProxyProtocol/ProxyCommand.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Buffers;
+using System.Net;
+
+namespace SuperSocket.ProtoBase.ProxyProtocol
+{
+ public enum ProxyCommand
+ {
+ ///
+ /// the connection was established on purpose by the proxy
+ /// without being relayed. The connection endpoints are the sender and the
+ /// receiver. Such connections exist when the proxy sends health-checks to the
+ /// server. The receiver must accept this connection as valid and must use the
+ /// real connection endpoints and discard the protocol block including the
+ /// family which is ignored.
+ ///
+ LOCAL,
+
+ ///
+ /// the connection was established on behalf of another node,
+ /// and reflects the original connection endpoints. The receiver must then use
+ /// the information provided in the protocol block to get original the address.
+ ///
+ PROXY
+ }
+}
\ No newline at end of file
diff --git a/src/SuperSocket.ProtoBase/ProxyProtocol/ProxyInfo.cs b/src/SuperSocket.ProtoBase/ProxyProtocol/ProxyInfo.cs
new file mode 100644
index 000000000..0fc4e4d84
--- /dev/null
+++ b/src/SuperSocket.ProtoBase/ProxyProtocol/ProxyInfo.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Buffers;
+using System.Net;
+using System.Net.Sockets;
+
+namespace SuperSocket.ProtoBase.ProxyProtocol
+{
+ public class ProxyInfo
+ {
+ public IPAddress SourceIPAddress { get; internal set; }
+
+ public int SourcePort { get; internal set; }
+
+ public IPAddress DestinationIPAddress { get; internal set; }
+
+ public int DestinationPort { get; internal set; }
+
+ public ProxyCommand Command { get; internal set; }
+
+ public int Version { get; internal set; }
+
+ public AddressFamily AddressFamily { get; internal set; }
+
+ public ProtocolType ProtocolType { get; internal set; }
+
+ internal int AddressLength { get; set; }
+
+ public ProxyInfo()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/SuperSocket.ProtoBase/ProxyProtocol/ProxyProtocolPackagePartReader.cs b/src/SuperSocket.ProtoBase/ProxyProtocol/ProxyProtocolPackagePartReader.cs
new file mode 100644
index 000000000..b0b2840a4
--- /dev/null
+++ b/src/SuperSocket.ProtoBase/ProxyProtocol/ProxyProtocolPackagePartReader.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Buffers;
+
+namespace SuperSocket.ProtoBase.ProxyProtocol
+{
+ abstract class ProxyProtocolPackagePartReader : IPackagePartReader
+ {
+ static ProxyProtocolPackagePartReader()
+ {
+ ProxyProtocolSwitch = new ProxyProtocolSwitchPartReader();
+ ProxyProtocolV1Reader = new ProxyProtocolV1PartReader();
+ }
+
+ public abstract bool Process(TPackageInfo package, object filterContext, SequenceReader reader, out IPackagePartReader nextPartReader, out bool needMoreData);
+
+ internal static IPackagePartReader ProxyProtocolSwitch { get; }
+
+ internal static IPackagePartReader ProxyProtocolV1Reader { get; }
+
+ internal static IPackagePartReader ProxyProtocolV2Reader { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/SuperSocket.ProtoBase/ProxyProtocol/ProxyProtocolPipelineFilter.cs b/src/SuperSocket.ProtoBase/ProxyProtocol/ProxyProtocolPipelineFilter.cs
new file mode 100644
index 000000000..b885867c9
--- /dev/null
+++ b/src/SuperSocket.ProtoBase/ProxyProtocol/ProxyProtocolPipelineFilter.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Buffers;
+
+namespace SuperSocket.ProtoBase.ProxyProtocol
+{
+ public class ProxyProtocolPipelineFilter : PackagePartsPipelineFilter
+ where TPackageInfo : class
+ {
+ private readonly IPipelineFilter _applicationPipelineFilter;
+
+ private object _originalFilterContext;
+
+ public ProxyInfo ProxyInfo { get; private set; }
+
+ public ProxyProtocolPipelineFilter(IPipelineFilter applicationPipelineFilter)
+ {
+ _applicationPipelineFilter = applicationPipelineFilter;
+ }
+
+ protected override TPackageInfo CreatePackage()
+ {
+ return default;
+ }
+
+ protected override IPackagePartReader GetFirstPartReader()
+ {
+ return ProxyProtocolPackagePartReader.ProxyProtocolSwitch;
+ }
+
+ public override void Reset()
+ {
+ // This method will be called when the proxy package handling finishes
+ NextFilter = _applicationPipelineFilter;
+ base.Reset();
+ Context = _originalFilterContext;
+ }
+
+ public override TPackageInfo Filter(SequenceReader reader)
+ {
+ if (ProxyInfo == null)
+ {
+ _originalFilterContext = Context;
+ Context = ProxyInfo = new ProxyInfo();
+ }
+
+ return base.Filter(reader);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/SuperSocket.ProtoBase/ProxyProtocol/ProxyProtocolSwitchPartReader.cs b/src/SuperSocket.ProtoBase/ProxyProtocol/ProxyProtocolSwitchPartReader.cs
new file mode 100644
index 000000000..4bfa5d4df
--- /dev/null
+++ b/src/SuperSocket.ProtoBase/ProxyProtocol/ProxyProtocolSwitchPartReader.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Buffers;
+using System.Text;
+
+namespace SuperSocket.ProtoBase.ProxyProtocol
+{
+ class ProxyProtocolSwitchPartReader : ProxyProtocolPackagePartReader
+ {
+ private const int SWITCH_PART_SIZE = 12;
+
+ private static readonly byte[] PROXYPROTOCOL_V2_SIGNATURE = new byte[] { 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A };
+
+ private static readonly byte[] PROXY_TAG = Encoding.ASCII.GetBytes("PROXY TCP");
+
+ public override bool Process(TPackageInfo package, object filterContext, SequenceReader reader, out IPackagePartReader nextPartReader, out bool needMoreData)
+ {
+ nextPartReader = null;
+
+ if (reader.Length < SWITCH_PART_SIZE)
+ {
+ needMoreData = true;
+ return false;
+ }
+
+ needMoreData = false;
+
+ if (reader.IsNext(PROXYPROTOCOL_V2_SIGNATURE, true))
+ {
+ nextPartReader = ProxyProtocolV2Reader;
+ return false;
+ }
+
+ int read = 0;
+
+ try
+ {
+ if (TryReadProxyProtocolV1Tag(reader, out read))
+ {
+ nextPartReader = ProxyProtocolV1Reader;
+ return false;
+ }
+
+ return true;
+ }
+ finally
+ {
+ if (read > 0)
+ reader.Rewind(read);
+ }
+ }
+
+ private bool TryReadProxyProtocolV1Tag(SequenceReader reader, out int read)
+ {
+ read = 0;
+
+ if (!reader.IsNext(PROXY_TAG, true))
+ {
+ return false;
+ }
+
+ read += PROXY_TAG.Length;
+
+ if (!reader.TryRead(out var ver))
+ {
+ return false;
+ }
+
+ read++;
+
+ if (ver != '4' && ver != '6')
+ {
+ return false;
+ }
+
+ if (!reader.TryRead(out var space))
+ {
+ return false;
+ }
+
+ read++;
+
+ return space == ' ';
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/SuperSocket.ProtoBase/ProxyProtocol/ProxyProtocolV1PartReader.cs b/src/SuperSocket.ProtoBase/ProxyProtocol/ProxyProtocolV1PartReader.cs
new file mode 100644
index 000000000..08be63651
--- /dev/null
+++ b/src/SuperSocket.ProtoBase/ProxyProtocol/ProxyProtocolV1PartReader.cs
@@ -0,0 +1,102 @@
+using System;
+using System.Buffers;
+using System.Collections;
+using System.Collections.Generic;
+using System.Net;
+using System.Text;
+using Microsoft.VisualBasic;
+
+namespace SuperSocket.ProtoBase.ProxyProtocol
+{
+ class ProxyProtocolV1PartReader : ProxyProtocolPackagePartReader
+ {
+ private static readonly byte[] PROXY_DELIMITER = Encoding.ASCII.GetBytes("\r\n");
+
+ private static readonly IProxySgementProcessor[] PROXY_SEGMENT_PARSERS = new IProxySgementProcessor[]
+ {
+ new SourceIPAddressProcessor(),
+ new DestinationIPAddressProcessor(),
+ new SourcePortProcessor(),
+ new DestinationPortProcessor()
+ };
+
+ public override bool Process(TPackageInfo package, object filterContext, SequenceReader reader, out IPackagePartReader nextPartReader, out bool needMoreData)
+ {
+ if (!reader.TryReadTo(out ReadOnlySequence proxyLineSequence, PROXY_DELIMITER, true))
+ {
+ needMoreData = true;
+ nextPartReader = null;
+ return false;
+ }
+
+ needMoreData = false;
+ nextPartReader = null;
+
+ var proxyLineReader = new SequenceReader(proxyLineSequence);
+
+ var proxyLine = proxyLineReader.ReadString();
+
+ LoadProxyInfo(filterContext as ProxyInfo, proxyLine, 12, 13);
+
+ return true;
+ }
+
+ private void LoadProxyInfo(ProxyInfo proxyInfo, string line, int startPos, int lookForOffet)
+ {
+ var span = line.AsSpan();
+ var segmentIndex = 0;
+
+ while (lookForOffet < line.Length)
+ {
+ var spacePos = line.IndexOf(' ', lookForOffet);
+
+ if (spacePos < 0)
+ break;
+
+ startPos = spacePos + 1;
+ lookForOffet = startPos + 1;
+
+ var segment = span.Slice(startPos, spacePos - startPos);
+
+ PROXY_SEGMENT_PARSERS[segmentIndex++].Process(segment, proxyInfo);
+ }
+ }
+
+ interface IProxySgementProcessor
+ {
+ void Process(ReadOnlySpan segment, ProxyInfo proxyInfo);
+ }
+
+ class SourceIPAddressProcessor : IProxySgementProcessor
+ {
+ public void Process(ReadOnlySpan segment, ProxyInfo proxyInfo)
+ {
+ proxyInfo.SourceIPAddress = IPAddress.Parse(segment);
+ }
+ }
+
+ class DestinationIPAddressProcessor : IProxySgementProcessor
+ {
+ public void Process(ReadOnlySpan segment, ProxyInfo proxyInfo)
+ {
+ proxyInfo.DestinationIPAddress = IPAddress.Parse(segment);
+ }
+ }
+
+ class SourcePortProcessor : IProxySgementProcessor
+ {
+ public void Process(ReadOnlySpan segment, ProxyInfo proxyInfo)
+ {
+ proxyInfo.SourcePort = int.Parse(segment);
+ }
+ }
+
+ class DestinationPortProcessor : IProxySgementProcessor
+ {
+ public void Process(ReadOnlySpan segment, ProxyInfo proxyInfo)
+ {
+ proxyInfo.SourcePort = int.Parse(segment);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/SuperSocket.ProtoBase/ProxyProtocol/ProxyProtocolV2PartReader.cs b/src/SuperSocket.ProtoBase/ProxyProtocol/ProxyProtocolV2PartReader.cs
new file mode 100644
index 000000000..2e1139e40
--- /dev/null
+++ b/src/SuperSocket.ProtoBase/ProxyProtocol/ProxyProtocolV2PartReader.cs
@@ -0,0 +1,121 @@
+using System;
+using System.Buffers;
+using System.Collections;
+using System.Collections.Generic;
+using System.Net;
+using System.Net.Http;
+using System.Net.Sockets;
+using System.Text;
+using Microsoft.VisualBasic;
+
+namespace SuperSocket.ProtoBase.ProxyProtocol
+{
+ class ProxyProtocolV2PartReader : ProxyProtocolPackagePartReader
+ {
+ private static readonly int FIXPART_LEN_AFTER_SIGNATURE = 4;
+
+ private static readonly ArrayPool _bufferPool = ArrayPool.Shared;
+
+ public override bool Process(TPackageInfo package, object filterContext, SequenceReader reader, out IPackagePartReader nextPartReader, out bool needMoreData)
+ {
+ nextPartReader = null;
+
+ var proxyInfo = filterContext as ProxyInfo;
+
+ if (proxyInfo.AddressLength == 0)
+ {
+ if (reader.Length < FIXPART_LEN_AFTER_SIGNATURE)
+ {
+ needMoreData = true;
+ return false;
+ }
+
+ reader.TryRead(out var versionAndCommand);
+
+ proxyInfo.Version = (int)versionAndCommand / 16;
+ proxyInfo.Command = (versionAndCommand % 16) == 0 ? ProxyCommand.LOCAL : ProxyCommand.PROXY;
+
+ reader.TryRead(out var addressFamilyAndProtocol);
+
+ proxyInfo.AddressFamily = ((int)addressFamilyAndProtocol / 16) switch
+ {
+ 0 => AddressFamily.Unspecified,
+ 1 => AddressFamily.InterNetwork,
+ 2 => AddressFamily.InterNetworkV6,
+ 3 => AddressFamily.Unix,
+ _ => throw new NotSupportedException(),
+ };
+
+ proxyInfo.ProtocolType = ((int)addressFamilyAndProtocol % 16) switch
+ {
+ 0 => ProtocolType.Unspecified,
+ 1 => ProtocolType.Tcp,
+ 2 => ProtocolType.Udp,
+ _ => throw new NotSupportedException(),
+ };
+
+ reader.TryRead(out var len_high);
+ reader.TryRead(out var len_low);
+
+ proxyInfo.AddressLength = len_high * 256 + len_low;
+
+ needMoreData = false;
+ return false;
+ }
+
+ if (reader.Length < proxyInfo.AddressLength)
+ {
+ needMoreData = true;
+ return false;
+ }
+
+ needMoreData = false;
+
+ if (proxyInfo.AddressFamily == AddressFamily.InterNetwork)
+ {
+ reader.TryReadBigEndian(out UInt32 sourceIpData);
+ proxyInfo.SourceIPAddress = new IPAddress(sourceIpData);
+
+ reader.TryReadBigEndian(out UInt32 destinationIpData);
+ proxyInfo.DestinationIPAddress = new IPAddress(destinationIpData);
+
+ reader.TryReadBigEndian(out UInt16 sourcePort);
+ proxyInfo.SourcePort = sourcePort;
+
+ reader.TryReadBigEndian(out UInt16 destinationPort);
+ proxyInfo.DestinationPort = destinationPort;
+ }
+ else if (proxyInfo.AddressFamily == AddressFamily.InterNetworkV6)
+ {
+ var addressBuffer = _bufferPool.Rent(32);
+
+ try
+ {
+ var addressBufferSpan = addressBuffer.AsSpan()[..32];
+
+ reader.Sequence.Slice(0, 32).CopyTo(addressBufferSpan);
+ reader.Advance(32);
+
+ proxyInfo.SourceIPAddress = new IPAddress(addressBufferSpan);
+
+ reader.Sequence.Slice(0, 32).CopyTo(addressBufferSpan);
+ reader.Advance(32);
+
+ proxyInfo.DestinationIPAddress = new IPAddress(addressBufferSpan);
+ }
+ finally
+ {
+ _bufferPool.Return(addressBuffer);
+ }
+
+ reader.TryReadBigEndian(out UInt16 sourcePort);
+ proxyInfo.SourcePort = sourcePort;
+
+ reader.TryReadBigEndian(out UInt16 destinationPort);
+ proxyInfo.DestinationPort = destinationPort;
+ }
+
+ return true;
+ }
+ }
+}
\ No newline at end of file