public async Task HandleTcp(IClient client, CancellationToken cancellationToken = default)
        {
            if (null == client)
            {
                return;
            }

            await HandleClient(client, Defaults.ReceiveBufferSize,
                               (pipe) =>
            {
                var cipher = _remoteServerConfig.CreateCipher(_logger);
                IClientFilter cipherFilter = new Cipher.TcpCipherFilter(cipher, _logger);
                pipe.AddFilter(client, cipherFilter);
            },
                               async (targetIPEndPoint) =>
            {
                return(await TcpClient1.ConnectAsync(targetIPEndPoint, _logger));
            },
                               async (request, pipe, targetClient, targetSsAddr) =>
            {
                pipe.ClientB = targetClient;

                if (request.SignificantLength > targetSsAddr.RawMemory.Length)   //have some payload
                {
                    _logger?.LogInformation($"Writing payload before piping...");
                    //await targetClient.WriteAsync(request.SignificantMemory.Slice(ssaddr.RawMemory.Length));
                    await pipe.GetWriter(targetClient).Write(request.SignificantMemory.Slice(targetSsAddr.RawMemory.Length), cancellationToken);
                }
                request.Dispose();
            },
                               cancellationToken);

            await Task.CompletedTask;
        }
        public async Task HandleHttp(IClient client, CancellationToken cancellationToken)
        {
            if (null == client)
            {
                return;
            }
            using (SmartBuffer clientRequest = SmartBuffer.Rent(2048))
            {
                clientRequest.SignificantLength = await client.ReadAsync(clientRequest.Memory, cancellationToken);

                if (10 >= clientRequest.SignificantLength)
                {
                    goto GOODBYE503;
                }


                if (HttpProxyHeaderResolver.TryResolve(clientRequest.SignificantMemory,
                                                       out HttpProxyHeaderResolver.Verb httpVerb, out Uri targetHost, out byte[] normalHttpRequestHeader))
                {
                    //_logger?.LogInformation($"HttpRoxyServer Verb={httpVerb.ToString()}.");
                    var server = _serverLoader.Load(null);
                    if (null == server)
                    {
                        _logger?.LogInformation($"HttpRoxyServer Proxy server not found.");
                        goto GOODBYE503;
                    }
                    var serverAddr = await server.GetIPEndPoint();

                    if (null == serverAddr)
                    {
                        _logger?.LogInformation($"HttpRoxyServer Unable to get proxy server address.");
                        goto GOODBYE503;
                    }

                    var relayClient = await TcpClient1.ConnectAsync(serverAddr, _logger); //A. connect ss-remote

                    if (null == relayClient)                                              //unable to connect ss-remote
                    {
                        _logger?.LogInformation($"HttpRoxyServer Unable to connect ss-remote:[{serverAddr.ToString()}]");
                        goto GOODBYE503;
                    }

                    using (var relayRequst = SmartBuffer.Rent(HttpProxyHeaderResolver.Verb.CONNECT == httpVerb ? 300 : normalHttpRequestHeader.Length + 300))
                    {
                        if (ShadowsocksAddress.TryParse(targetHost, out Tuple <byte, ushort, byte[]> ssaddr))//B. construct sssaddr
                        {
                            //_logger?.LogInformation($"HttpRoxyServer ATYP={ssaddr.Item1}, port={ssaddr.Item2}");
                            ShadowsocksAddress.TrySerailizeTo(ssaddr.Item1, ssaddr.Item3, ssaddr.Item2, relayRequst.Memory, out int written);
                            relayRequst.SignificantLength = written;

                            if (HttpProxyHeaderResolver.Verb.CONNECT != httpVerb)
                            {
                                normalHttpRequestHeader.CopyTo(relayRequst.FreeMemory);
                                relayRequst.SignificantLength += normalHttpRequestHeader.Length;
                            }

                            var                    cipher       = server.CreateCipher(_logger);
                            DefaultPipe            pipe         = new DefaultPipe(client, relayClient, Defaults.ReceiveBufferSize, _logger);
                            Cipher.TcpCipherFilter cipherFilter = new Cipher.TcpCipherFilter(relayClient, cipher, _logger);
                            pipe.ApplyClientFilter(cipherFilter);

                            var writeResult = await pipe.Writer[relayClient].Write(relayRequst.SignificantMemory, cancellationToken);//C. send target addr (& http header) to ss-remote.
                            _logger?.LogInformation($"Send target addr {writeResult.Written} bytes. {writeResult.Result}.");

                            if (HttpProxyHeaderResolver.Verb.CONNECT == httpVerb)
                            {
                                await client.WriteAsync(RESPONSE_200_Connection_Established, cancellationToken);
                            }
                            PipeClient(pipe, cancellationToken);//D. start piping.

                            return;
                        }
                        else
                        {
                            _logger?.LogInformation($"HttpRoxyServer parse host URL failed."); goto GOODBYE503;
                        }
                    }
                }
        public async Task HandleTcp(IClient client, CancellationToken cancellationToken)
        {
            if (null == client)
            {
                return;
            }
            if (!await Handshake(client, cancellationToken))
            {
                return;
            }                                                            //Handshake
            using (SmartBuffer request = SmartBuffer.Rent(300), response = SmartBuffer.Rent(300))
            {
                request.SignificantLength = await client.ReadAsync(request.Memory, cancellationToken);

                if (5 >= request.SignificantLength)
                {
                    client.Close(); return;
                }
                #region socks5

                /*
                 +----+-----+-------+------+----------+----------+
                 |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
                 +----+-----+-------+------+----------+----------+
                 | 1  |  1  | X'00' |  1   | Variable |    2     |
                 +----+-----+-------+------+----------+----------+
                 |
                 | Where:
                 |
                 |    o  VER    protocol version: X'05'
                 |    o  CMD
                 |       o  CONNECT X'01'
                 |       o  BIND X'02'
                 |       o  UDP ASSOCIATE X'03'
                 |    o  RSV    RESERVED
                 |    o  ATYP   address type of following address
                 |       o  IP V4 address: X'01'
                 |       o  DOMAINNAME: X'03'
                 |       o  IP V6 address: X'04'
                 |    o  DST.ADDR       desired destination address
                 |    o  DST.PORT desired destination port in network octet
                 |       order
                 |
                 |
                 +----+-----+-------+------+----------+----------+
                 |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
                 +----+-----+-------+------+----------+----------+
                 | 1  |  1  | X'00' |  1   | Variable |    2     |
                 +----+-----+-------+------+----------+----------+
                 |
                 | Where:
                 |
                 |    o  VER    protocol version: X'05'
                 |    o  REP    Reply field:
                 |       o  X'00' succeeded
                 |       o  X'01' general SOCKS server failure
                 |       o  X'02' connection not allowed by ruleset
                 |       o  X'03' Network unreachable
                 |       o  X'04' Host unreachable
                 |       o  X'05' Connection refused
                 |       o  X'06' TTL expired
                 |       o  X'07' Command not supported
                 |       o  X'08' Address type not supported
                 |       o  X'09' to X'FF' unassigned
                 |    o  RSV    RESERVED
                 |    o  ATYP   address type of following address
                 |       o  IP V4 address: X'01'
                 |       o  DOMAINNAME: X'03'
                 |       o  IP V6 address: X'04'
                 |    o  BND.ADDR       server bound address
                 |    o  BND.PORT       server bound port in network octet order
                 */

                #endregion

                switch (request.Memory.Span[1])
                {
                case 0x1:    //connect //TODO validate addr
                {
                    var server = _serverLoader.Load(null);
                    if (null == server)
                    {
                        _logger?.LogInformation($"proxy server not found.");
                        client.Close();
                        break;
                    }
                    var serverAddr = await server.GetIPEndPoint();

                    if (null == serverAddr)
                    {
                        _logger?.LogInformation($"unable to get proxy server address.");
                        client.Close();
                        break;
                    }

                    var relayClient = await TcpClient1.ConnectAsync(serverAddr, _logger); //A. connect ss-remote

                    if (null == relayClient)                                              //unable to connect ss-remote
                    {
                        _logger?.LogInformation($"unable to connect ss-remote:[{serverAddr.ToString()}]");
                        await client.WriteAsync(NegotiationResponse.CommandConnectFailed, cancellationToken);

                        client.Close();
                        break;
                    }

                    var clientRequest = request;
                    if (ShadowsocksAddress.TryResolve(clientRequest.Memory.Slice(3), out ShadowsocksAddress ssaddr))        //B.resove target addr.
                    {
                        var cipher = server.CreateCipher(_logger);
                        using (var targetAddrCipher = cipher.EncryptTcp(ssaddr.RawMemory))
                        {
                            int sent = await relayClient.WriteAsync(targetAddrCipher.SignificanMemory, cancellationToken);        //C. send target addr to ss-remote.

                            _logger?.LogInformation($"Send target addr {sent}={targetAddrCipher.SignificantLength}.");

                            await client.WriteAsync(NegotiationResponse.CommandConnectOK, cancellationToken); //D. notify client to send data.

                            PipeTcp(client, relayClient, cipher, cancellationToken);                          //E. start piping.
                        }
                    }
                    else
                    {
                        _logger?.LogWarning("resolve target addr failed.");
                        client.Close();
                    }
                }
                break;

                case 0x2:    //bind
                {
                    request.SignificanMemory.CopyTo(response.Memory);
                    response.Memory.Span[1] = 0x7;
                    await client.WriteAsync(response.Memory.Slice(0, request.SignificantLength), cancellationToken);

                    client.Close();
                }
                break;

                case 0x3:    //udp assoc
                {
                    if (ShadowsocksAddress.TrySerailizeTo(
                            (byte)(AddressFamily.InterNetworkV6 == client.LocalEndPoint.AddressFamily ? 0x4 : 0x1),
                            client.LocalEndPoint.Address.GetAddressBytes(),
                            (ushort)client.LocalEndPoint.Port,
                            response.Memory,
                            out int written))
                    {
                        response.SignificantLength = written;
                        await client.WriteAsync(response.SignificanMemory, cancellationToken);
                    }

                    //TODO
                    client.Closing += this.Client_Closing;
                }
                break;

                default:
                    break;
                }
            }
        }
        public async Task HandleTcp(IClient client, CancellationToken cancellationToken = default)
        {
            if (null == client)
            {
                return;
            }
            using (SmartBuffer localRequestCipher = SmartBuffer.Rent(Defaults.ReceiveBufferSize))
            {
                localRequestCipher.SignificantLength = await client.ReadAsync(localRequestCipher.Memory, cancellationToken);//A. Receive target addr.

                //decrypt
                var cipher = _remoteServerConfig.CreateCipher(_logger);
                using (var localReqestPlain = cipher.DecryptTcp(localRequestCipher.SignificanMemory))
                {
                    if (0 == localRequestCipher.SignificantLength)
                    {
                        //decrypt failed, available options: 1.leave it. 2.close connection. 3.add to blocklist.
                        _logger?.LogWarning($"Decrypt target addr failed, client=[{client.EndPoint.ToString()}]");
                        client.Close();//->local pipe broken-> local pipe close.
                        return;
                    }
                    IPAddress targetIP = IPAddress.Any;                                                        //TODO target address check
                    if (ShadowsocksAddress.TryResolve(localReqestPlain.Memory, out ShadowsocksAddress ssaddr)) //B. Resolve target addr.
                    {
                        if (0x3 == ssaddr.ATYP)                                                                //a domain name
                        {
                            var ips = await _dnsCache.ResolveHost(Encoding.UTF8.GetString(ssaddr.Address.ToArray()));

                            if (ips != null && ips.Length > 0)
                            {
                                targetIP = ips[0];
                            }
                        }
                        else//IPv4/v6
                        {
                            targetIP = new IPAddress(ssaddr.Address.Span);
                        }
                        if (IPAddress.Any == targetIP)//a empty IP.
                        {
                            _logger?.LogWarning($"Invalid target addr. client=[{client.EndPoint.ToString()}]");
                            client.Close();
                            return;
                        }
                        IPEndPoint ipeTarget = new IPEndPoint(targetIP, ssaddr.Port);
                        _logger.LogInformation($"Resolved target address:[{ipeTarget.ToString()}]");
                        _logger.LogInformation($"Connecting to [{ipeTarget.ToString()}]...");
                        var targetClient = await TcpClient1.ConnectAsync(ipeTarget, _logger); //C. Connect target

                        if (null == targetClient)                                             //connect target failed.
                        {
                            _logger?.LogInformation($"Unable to connect target [{ipeTarget.ToString()}]. client=[{client.EndPoint.ToString()}]");
                            client.Close();
                            return;
                        }
                        _logger.LogInformation($"Connected to [{ipeTarget.ToString()}]");

                        if (localReqestPlain.Memory.Length > ssaddr.RawMemory.Length)//have some payload
                        {
                            _logger.LogInformation($"Writing payload before piping...");
                            await targetClient.WriteAsync(localReqestPlain.SignificanMemory.Slice(ssaddr.RawMemory.Length));
                        }
                        _logger.LogInformation($"Start piping...");
                        PipeTcp(client, targetClient, cipher, cancellationToken);//D. start piping.
                    }
                    else//invalid socks5 addr
                    {
                        _logger?.LogWarning($"StandardRemoteSocks5Handler HandleTcp resolve target addr failed. client=[{client.EndPoint.ToString()}]");
                        client.Close();
                        return;
                    }
                }
            }
            await Task.CompletedTask;
        }