/// <summary>
 /// Passes the given response message to the endpoint.
 /// </summary>
 /// <param name="response"></param>
 public void PassResponse(ProxyMessage response, Socket clientSocket)
 {
     // Send the response to the client
     if (clientSocket != null && clientSocket.Connected)
     {
         clientSocket.Send(response.Content);
     }
 }
        /// <summary>
        /// Passes the given client message to the given endpoint.
        /// </summary>
        /// <param name="message">The message of the client and his endpoint.</param>
        /// <param name="dstEP">The endpoint that will receive the client's message</param>
        public void PassClientMessage(ProxyMessage message, IPEndPoint dstEP)
        {
            // Build the message in our proxy format:
            // <endpoint_bytes><client_message>
            var ipBytes   = message.ClientEndpoint.Address.GetAddressBytes();
            var portBytes = BitConverter.GetBytes(message.ClientEndpoint.Port).Take(PORT_LENGTH_IN_BYTES).ToArray();

            byte[] messageToSend = ipBytes.Concat(portBytes).Concat(message.Content).ToArray();

            // Send the proxy-formatted message to the destination
            Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp);

            try
            {
                socket.Connect(dstEP);
            }
            catch
            {
                throw new Exception("Connection to server failed.");
            }
            socket.Send(messageToSend);
        }
        static void Main(string[] args)
        {
            // Load all the servers from the configuration file in a constant interval
            Thread serverLoadingThread = new Thread(() =>
            {
                while (true)
                {
                    HashSet <IPEndPoint> addedServers = LoadServers(SERVERS_FILE);
                    //Console.WriteLine("Reloading servers...");

                    // Add the new servers to the Load Balancer
                    foreach (var server in addedServers)
                    {
                        lock (_loadBalancer)
                        {
                            _loadBalancer.AddLoadCarrier(server);
                        }
                    }

                    // Sleep for ten Seconds
                    Thread.Sleep(SECOND);
                }
            });

            serverLoadingThread.Start();

            // Start the status-printing thread
            Thread statusPrintingThread = new Thread(() =>
            {
                while (true)
                {
                    PrintLoadBalancingStatus();
                    Thread.Sleep(100);
                }
            });

            statusPrintingThread.Start();

            // Set-up the server and start listening for client-requests and server-responses
            ProxyServer proxyServer = new ProxyServer(SERVE_PORT, PROXY_PORT);

            proxyServer.Start();

            while (true)
            {
                // Get request from client
                ProxyMessage clientRequest = proxyServer.ReceiveClientMessage();
                _clients[clientRequest.ClientEndpoint] = clientRequest.Socket;
                //ColorizedWriteLine($"REQUEST from {clientRequest.ClientEndpoint}", ConsoleColor.Green);

                // Pick the most available server
                IPEndPoint handlingServer;
                lock (_loadBalancer)
                {
                    //Console.WriteLine("Picking Web Server...");
                    handlingServer = _loadBalancer.PickMostAvailableCarrier();
                    //Console.WriteLine($"Picked {handlingServer}");
                }

                try
                {
                    // Pass the request of the client to the server
                    //Console.WriteLine($"Passing Client Request to {handlingServer}");
                    proxyServer.PassClientMessage(clientRequest, handlingServer);
                    // Add the load to the handling server
                    _loadBalancer.AddLoad(handlingServer);
                }
                catch // There's a problem with the web server
                {
                    // Send the client an error message
                    byte[] problemResponseData = Encoding.ASCII.GetBytes("HTTP/1.1 500 Internal Server Error");
                    var    clientEP            = clientRequest.ClientEndpoint;
                    proxyServer.PassResponse(new ProxyMessage(clientEP, null, problemResponseData), clientRequest.Socket);

                    // Remove the load from the handling server
                    _loadBalancer.RemoveLoad(handlingServer);

                    // Notify in the console that the web server has a problem
                    ColorizedWriteLine($"Web Server {handlingServer} has a problem! (2)", ConsoleColor.Red);
                    continue;
                }

                // Queue a thread for handling the request
                ThreadPool.QueueUserWorkItem((obj) =>
                {
                    try
                    {
                        // Get the response for the client form the server
                        ProxyMessage serverResponse = proxyServer.ReceiveResponse();

                        // Pass the response to the client
                        proxyServer.PassResponse(serverResponse, _clients[serverResponse.ClientEndpoint]);
                        _clients.Remove(serverResponse.ClientEndpoint);
                        //ColorizedWriteLine($"RESPONSE to {clientRequest.ClientEndpoint}", ConsoleColor.Blue);
                    }
                    catch (Exception e)
                    {
                        // Send the client an error message
                        byte[] problemResponseData = Encoding.ASCII.GetBytes("HTTP/1.1 500 Internal Server Error");
                        var clientEP = clientRequest.ClientEndpoint;
                        proxyServer.PassResponse(new ProxyMessage(clientEP, null, problemResponseData), clientRequest.Socket);

                        // Notify in the console that the web server has a problem
                        ColorizedWriteLine($"Web Server {handlingServer} has a problem!\n", ConsoleColor.Red);
                    }

                    // Remove the load from the handling server
                    _loadBalancer.RemoveLoad(handlingServer);
                });
            }
        }