kerryjiang / SuperSocket

SuperSocket is a light weight, cross platform and extensible socket server application framework.
Apache License 2.0
3.89k stars 1.15k forks source link

PipelineFilterBase - Filter(ref SequenceReader<byte> reader) - when method returns null something wierd is happening #688

Closed panterlo closed 1 week ago

panterlo commented 10 months ago

Try this code with 2.0.0-beta.18 and when returning null it will fail after a few "line returns". Removing the return null and returning non null it will read of the data correctly. The sever is a simple RAW socket with CRLF termination. I am trying to deal with the scenario that commands sent to the server could be responded in "one line" responses or a longer response that ends with the word "END".

public class CustomFilter : PipelineFilterBase<TextPackageInfo>
{
    private StringBuilder _result = new StringBuilder();

    public CustomFilter()
    {
    }

    public override TextPackageInfo Filter(ref SequenceReader<byte> reader)
    {
        var terminator = new ReadOnlyMemory<byte>(new[] { (byte)'\r', (byte)'\n' });
        var terminatorSpan = terminator.Span;

        if (!reader.TryReadTo(out ReadOnlySequence<byte> pack, terminatorSpan, advancePastDelimiter: false))
            return null; // Not reached line end

        try
        {
            var text = pack.GetString(Encoding.ASCII);

            if (text.StartsWith(">") == false &&
                text.StartsWith("SUCCESS:") == false &&
                text.StartsWith("ERROR:") == false)
                _result.AppendLine(text);

            Console.WriteLine(_result.ToString());
            return null; // Will work for a few line returns then nothing happens....
            return new TextPackageInfo() { Text = text }; // Works all the way
        }
        finally
        {
            reader.Advance(terminator.Length);
        }
    }

    protected override TextPackageInfo DecodePackage(ref ReadOnlySequence<byte> buffer)
    {
        return new TextPackageInfo { Text = buffer.GetString(Encoding.ASCII) };
    }
}
panterlo commented 10 months ago

Anyone have any ideas ?

chucklu commented 10 months ago

I will suggest you to use FixedHeaderPipelineFilter, which is more reasonable protocol than your current

chucklu commented 10 months ago

When you return null package, the subsequent code logic is here https://github.com/kerryjiang/SuperSocket/blob/07d5a6f86601cbb7c76b2f18503103b3b232aede/src/SuperSocket.Channel/PipeChannel.cs#L496

 if (packageInfo == null)
                {
                    // the current pipeline filter needs more data to process
                    if (!filterSwitched)
                    {
                        // set consumed position and then continue to receive...
                        consumed = buffer.GetPosition(bytesConsumedTotal);
                        return true;
                    }

                    // we should reset the previous pipeline filter after switch
                    currentPipelineFilter.Reset();
                }
                else
                {
                    // reset the pipeline filter after we parse one full package
                    currentPipelineFilter.Reset();
                    _packagePipe.Write(packageInfo);
                }

If you think you encounter an bug, try to reproduce it and provide a sample repository with both client and server code for me to troubleshooting

panterlo commented 10 months ago

Hmm, I am noticing if the server sends only a carriage return \r\n and nothing else it for some reason locks up further calling on the pipeline filter.

I am using OpenVPN Management socket which essentially loooks like this:

Description: [FROM SERVER WHEN CLIENT CONNECTS]
>INFO:OpenVPN Management Interface Version 5 -- type 'help' for more info
Description: [FROM CLIENT TO SERVER]
status
Description: [SERVER RESPONDS WITH]
OpenVPN STATISTICS
Updated,2023-11-02 09:18:54
TUN/TAP read bytes,780
TUN/TAP write bytes,5363
TCP/UDP read bytes,8151
TCP/UDP write bytes,6208
Auth read bytes,0
END

The socket server have these variations: 1) The line starts with SUCCESS: or ERROR: which indicates there is a oneline response from the server ending with \r\n 2) The line starts with > meaning it's an event like "greeting" or some data that the server sends periodically. Ending with \r\n 3) The line doesnt start with 1) or 2) but ends with the word END and \r\n

I will put the short code for testing on a repo now.

panterlo commented 10 months ago

I have added a repo here https://github.com/panterlo/OpenVPNManagementClient and the client connects to a forwarded port on my firewall so you don't have to setup OpenVPN. So you should just be able to run the code.

chucklu commented 10 months ago

So, you are using the EasyClient with pipelineFilter? working as a socket client instead of socket server?

_client = new EasyClient<OpenVPNManagementPackage>(new LinePipelineFilterOpenVPN(), new SuperSocket.Channel.ChannelOptions()).AsClient();

Please refer to the sample code https://github.com/kerryjiang/SuperSocket/blob/07d5a6f86601cbb7c76b2f18503103b3b232aede/test/SuperSocket.Tests/ClientTest.cs#L211, and write a server according the sample

panterlo commented 10 months ago

Correct, it's a client I am trying to build against an already existing server, OpenVPN Mangement Socket Server. I have added a test server in the code with the external IP so there is no need to mock a separate server (that ideally wouldn't be the same).

panterlo commented 10 months ago

You can just use Putty or whatever socket client there is with RAW sockets and connect to the IP and port and run the commands to see how it should behave....

kerryjiang commented 7 months ago

It is a PipelineFilter implementation issue.