0
\$\begingroup\$

I'm implementing HTTP traffic monitor (proxy) for my project right now. It's performance is satisfactory. I've updated regex settings and improved performance but I think there are also ways to make it better.

//mipt
public class TrafficMonitor
{
    private int _port;
    private bool _isWorking;
    private TcpListener _listener;
    private static readonly Regex myReg = new Regex(@"Host: (((?<hostName>.+?):(?<port>\d+?))|(?<hostName>.+?))\s+",
                RegexOptions.Compiled);
    public TrafficMonitor(int port)
    {
        _port = port;
        _listener = new TcpListener(IPAddress.Parse("127.0.0.1"), _port);
    }

    public void Start()
    {
        _listener.Start();
        _isWorking = true;

        while (_isWorking)
        {
            TcpClient client = _listener.AcceptTcpClient();                
            Task.Factory.StartNew(() => Worker(client.Client));
        }
    }

    public void Stop()
    {
        _isWorking = false;
        _listener.Stop();
    }

    private void Worker(Socket clientSocket)
    {
        if (clientSocket.Connected)
        {
            byte[] httpRequest = ReadToEnd(clientSocket);

            Match m = myReg.Match(Encoding.ASCII.GetString(httpRequest));
            string hostName = m.Groups["hostName"].Value;
            int port = 0;

            if (!int.TryParse(m.Groups["port"].Value, out port))
            {
                port = 80;
            }
            IPHostEntry hostEntry = Dns.GetHostEntry(hostName);
            IPEndPoint endPoint = new IPEndPoint(hostEntry.AddressList[0], port);

            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socket.Connect(endPoint);
            if (socket.Send(httpRequest, httpRequest.Length, SocketFlags.None) == httpRequest.Length)
            {
                byte[] httpResponse = ReadToEnd(socket);
                if (httpResponse != null && httpResponse.Length > 0 )
                    clientSocket.Send(httpResponse, httpResponse.Length, SocketFlags.None);
            }
            socket.Close();
            clientSocket.Close();
        }
    }

    private byte[] ReadToEnd(Socket socket)
    {
        byte[] recievedData = new byte[socket.ReceiveBufferSize];
        int len = 0;

        using (MemoryStream m = new MemoryStream())
        {
            while (socket.Poll(1000000, SelectMode.SelectRead) && (len = socket.Receive(recievedData, socket.ReceiveBufferSize, SocketFlags.None)) > 0)
            {
                m.Write(recievedData, 0, len);
            }

            return m.ToArray();
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ your regex has to backtrack a lot. Try something like Host: (?<host>[\w.-]+) :? (?<port> (?: (?<=:) \d+ )? ) \s (spaces added for readability; they should be removed). \$\endgroup\$ – Oh My Goodness Jan 12 at 23:32
2
\$\begingroup\$

I don't program in C and I don't know the exact input strings that you're expecting, but I can offer a couple of regex suggestions based on reasonable assumptions.

Your regex:

@"Host: (((?<hostName>.+?):(?<port>\d+?))|(?<hostName>.+?))\s+"

Your pattern looks for a hostname followed by a colon then a port number then whitespace characters OR a hostname followed by whitespace characters.

  • Since you are not capturing the whitespace and merely using the whitespace as an end point, the pattern can be reconfigured.
  • Since the colon-port substring is optional, the zero or one quantifier ? can be used to eliminate the redundant named capture group (which was causing trouble at regex101.com).
  • +? calls for "lazy" matching. If you want your pattern to execute quickly, rewrite your pattern to use greedy quantifiers.

New pattern:

@"Host: (?<hostName>[^:\s]+)(:(?<port>\d+))?"

Here is a cheap and cheerful demo: https://regex101.com/r/NqSJ8L/1/

Note, my above pattern could be massaged a few different ways. For instance, you might use ^ and an m pattern modifier to anchor the pattern to the start of the line of text. Again, I don't program in C, so I don't know if there are incapatabilities with certain pattern entities.

Or instead of a negated character class, you can list the valid character for a hostname:

@"Host: (?<hostName>[\w.-]+)(:(?<port>\d+))?"

P.s. It just occurred to me that my optional pattern will not always deliver a named capture group for the port, so if you don't want to write code to check that the port capture group is generated, here is OhMyGoodness's commented pattern without the trailing whitespace matching. (?<= ) is a lookbehind. (?: ) is a non-capturing group.

Host: (?<hostName>[\w.-]+):?(?<port>(?:(?<=:)\d+)?)
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.