CosmosOS / Cosmos

Cosmos is an operating system "construction kit". Build your own OS using managed languages such as C#, VB.NET, and more!
https://www.goCosmos.org
BSD 3-Clause "New" or "Revised" License
2.93k stars 553 forks source link

TCPListener CPU exception when accepting new clients #2811

Closed Jspa2 closed 11 months ago

Jspa2 commented 12 months ago

Area of Cosmos - What area of Cosmos are we dealing with?

Networking stack

Expected Behaviour - What do you think that should happen?

The TCP listener should receive HTTP requests properly.

Actual Behaviour - What unexpectedly happens?

When refreshing the page repeatedly in a web browser, a CPU exception or freeze occurs. (Maybe this is because the browser is cancelling its request halfway through?)

Reproduction - How did you get this error to appear?

Run this HTTP server code in VMware and refresh the page rapidly (hold Ctrl+R). Observe that it gets stuck at Waiting... or a CPU exception occurs at RX (receiving).

using System;
using System.Text;
using Sys = Cosmos.System;
using Cosmos.System.Network.IPv4.UDP.DHCP;
using Cosmos.System.Network.IPv4.TCP;
using Cosmos.System.Network.IPv4;

namespace WebKernel
{
    public class Kernel : Sys.Kernel
    {
        protected override void BeforeRun()
        {
            using DHCPClient dhcp = new DHCPClient();
            if (dhcp.SendDiscoverPacket() == -1)
            {
                Console.WriteLine("DHCP timed out. Halt.");
                Cosmos.Core.CPU.Halt();
            }
        }

        protected override void Run()
        {
            ulong count = 0;

            using var tcpListener = new TcpListener(80);

            tcpListener.Start();
            Console.WriteLine("Listening on port 80.");

            for (;;)
            {
                count++;

                Console.Write($"#{count}: Waiting...");
                TcpClient client = tcpListener.AcceptTcpClient();
                if (client == null || !client.IsConnected()) { Console.WriteLine(" (failed)"); continue; }
                Console.Write($" RX: {client.RemoteEndPoint.Address.ToString()} ");

                EndPoint endpoint = new EndPoint(Address.Zero, 0);
                if (!client.IsConnected()) { Console.WriteLine(" (disconnected)"); continue; }
                byte[] data = client.Receive(ref endpoint);
                if (data.Length == 0) { Console.WriteLine("(no data)"); client.Close(); continue; }

                string request = Encoding.ASCII.GetString(data);
                string[] lines = request.Split("\r\n");
                Console.Write(lines[0]);

                string res = "<!DOCTYPE html><html><body><h1>OK</h1><p>Web server powered by Cosmos</p></body></html>";
                byte[] content = Encoding.UTF8.GetBytes(res);
                byte[] headers = Encoding.ASCII.GetBytes($"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: {content.Length}\r\nServer: Cosmos\r\nConnection: close\r\n\r\n");

                if (!client.IsConnected()) { Console.WriteLine("(disconnected)"); continue; }
                client.Send(headers);
                client.Send(content);

                Console.WriteLine(" Responded.");
                client.Close();
            }
        }
    }
}

Version - Were you using the User Kit or Dev Kit? And what User Kit version or Dev Kit commit (Cosmos, IL2CPU, X#)?

Cosmos Dev Kit bda93dd06 Your branch is up to date with 'origin/feature/networkConnectionTests'.

valentinbreiz commented 12 months ago

Thanks for your issue that's not normal

I reproduced the issue but this code works fine if you need a working server:

using System;
using System.Text;
using Sys = Cosmos.System;
using Cosmos.System.Network.IPv4.UDP.DHCP;
using Cosmos.System.Network.IPv4.TCP;
using Cosmos.System.Network.IPv4;
using Cosmos.System.Network.Config;

namespace WebKernel
{
    public class Kernel : Sys.Kernel
    {
        protected override void BeforeRun()
        {
            using DHCPClient dhcp = new DHCPClient();
            if (dhcp.SendDiscoverPacket() == -1)
            {
                Console.WriteLine("DHCP timed out. Halt.");
                Cosmos.Core.CPU.Halt();
            }

            while (NetworkConfiguration.CurrentAddress.ToString() == "");

            Console.WriteLine("IP:", NetworkConfiguration.CurrentAddress.ToString());
        }

        protected override void Run()
        {

            ulong count = 0;

            for (; ; )
            {

                using var tcpListener = new TcpListener(80);

                tcpListener.Start();
                Console.WriteLine("Listening on port 80.");

                count++;

                Console.Write($"#{count}: Waiting...");
                TcpClient client = tcpListener.AcceptTcpClient();
                if (client == null || !client.IsConnected()) { Console.WriteLine(" (failed)"); continue; }
                Console.Write($" RX: {client.RemoteEndPoint.Address.ToString()} ");

                EndPoint endpoint = new EndPoint(Address.Zero, 0);
                if (!client.IsConnected()) { Console.WriteLine(" (disconnected)"); continue; }
                byte[] data = client.Receive(ref endpoint);
                if (data.Length == 0) { Console.WriteLine("(no data)"); client.Close(); continue; }

                string request = Encoding.ASCII.GetString(data);
                string[] lines = request.Split("\r\n");
                Console.Write(lines[0]);

                string res = "<!DOCTYPE html><html><body><h1>OK</h1><p>Web server powered by Cosmos</p></body></html>";
                byte[] content = Encoding.UTF8.GetBytes(res);
                byte[] headers = Encoding.ASCII.GetBytes($"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: {content.Length}\r\nServer: Cosmos\r\nConnection: close\r\n\r\n");

                if (!client.IsConnected()) { Console.WriteLine("(disconnected)"); continue; }
                client.Send(headers);
                client.Send(content);

                Console.WriteLine(" Responded.");

                tcpListener.Stop();
            }
        }
    }
}
valentinbreiz commented 11 months ago

Should be fixed with https://github.com/CosmosOS/Cosmos/pull/2812

Read the docs for the new way of using TCP networking