commit da9245bf1c166ab4cd57df4edd855a2275ca77c7
Author: Eliška Mourycová <eliska.mourycova@seznam.cz>
Date:   Mon May 24 12:40:32 2021 +0200

    Re #8941. Implemented asynchronous listening for clients' requests.

diff --git a/Server/ServerApp/Connection/ConnectionListener.cs b/Server/ServerApp/Connection/ConnectionListener.cs
index 7cebbe8..c55b334 100644
--- a/Server/ServerApp/Connection/ConnectionListener.cs
+++ b/Server/ServerApp/Connection/ConnectionListener.cs
@@ -7,6 +7,7 @@ using ServerApp.Predictor;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.IO;
 using System.Net;
 using System.Net.Sockets;
@@ -17,142 +18,208 @@ using System.Web;
 namespace ServerApp.Connection
 {
 
-	//public class HttpListenerCallbackState
-	//{
-	//    private readonly HttpListener _listener;
-	//    private readonly AutoResetEvent _listenForNextRequest;
-
-	//    public HttpListenerCallbackState(HttpListener listener)
-	//    {
-	//        if (listener == null) throw new ArgumentNullException("listener");
-	//        _listener = listener;
-	//        _listenForNextRequest = new AutoResetEvent(false);
-	//    }
-
-	//    public HttpListener Listener { get { return _listener; } }
-	//    public AutoResetEvent ListenForNextRequest { get { return _listenForNextRequest; } }
-	//}
-
-	//public class HttpRequestHandler
-	//{
-	//    private int requestCounter = 0;
-	//    private ManualResetEvent stopEvent = new ManualResetEvent(false);
-	//    private int PORT;
-
-	//    public HttpRequestHandler(int port)
-	//    {
-	//        this.PORT = port;
-	//    }
-
-	//    public void ListenAsynchronously(/*IEnumerable<string> prefixes*/)
-	//    {
-	//        HttpListener listener = new HttpListener();
-
-	//        //foreach (string s in prefixes)
-	//        //{
-	//        //    listener.Prefixes.Add(s);
-	//        //}
+	public class ConnectionListenerAsync
+	{
+		private readonly int PORT;
+		private IPredictionController predictionController;
+		//HttpListener listener;
+
+		public ConnectionListenerAsync(int port, IPredictionController predictionController)
+		{
+			this.PORT = port;
+			this.predictionController = predictionController;
+		}
+
+
+		public void StartListening()
+		{
+			string ip = GetLocalIPAddress();
+			Console.WriteLine("ip : " + ip);
+			HttpListener listener = new HttpListener();
+			//HttpListener server = new HttpListener();
+			//server.Prefixes.Add($"https://{ip}:{PORT}/");
+			listener.Prefixes.Add($"http://localhost:{PORT}/");
+
+			listener.Start();
+
+			Console.WriteLine("Listening...");
+
+
+			var result = listener.BeginGetContext(ListenerCallback, listener);
+			// result.AsyncWaitHandle.WaitOne(); // ????
+		}
+
+
+
+		private void ListenerCallback(IAsyncResult result)
+		{
+			Console.WriteLine("in ListenerCallback");
+			HttpListener listener = (HttpListener)result.AsyncState;
+
+			// Call EndGetContext to complete the asynchronous operation.
+			HttpListenerContext context = listener.EndGetContext(result);
+
+			// tell listener to get the next context directly.
+			listener.BeginGetContext(ListenerCallback, listener);
+
+			// Obtain a request object.
+			HttpListenerRequest request = context.Request;
+			// Obtain a response object.
+			HttpListenerResponse response = context.Response;
+			
+			// Simulate work
+			//Thread.Sleep(5000);
 
-	//        listener.Prefixes.Add($"http://localhost:{PORT}/");
 
-	//        listener.Start();
-	//        Console.WriteLine("Listening...");
-	//        HttpListenerCallbackState state = new HttpListenerCallbackState(listener);
-	//        ThreadPool.QueueUserWorkItem(Listen, state);
-	//    }
 
-	//    public void StopListening()
-	//    {
-	//        stopEvent.Set();
-	//    }
+			// add the headers and everything:
+			response.AddHeader("Access-Control-Allow-Credentials", "true");
+			response.AddHeader("Access-Control-Expose-Headers", "ETag");
+			response.AddHeader("Access-Control-Allow-Headers", "Accept, X-Access-Token, X-Application-Name, X-Request-Sent-Time");
+			response.AddHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
+			response.AddHeader("Access-Control-Allow-Origin", "*");
 
+			string responseString = "";
+			// Construct a response.
+			if (context.Request.HttpMethod == "GET") // when client says download
+			{
+				Console.WriteLine("received GET request");
+				responseString = ConstructGETResponse();
 
-	//    private void Listen(object state)
-	//    {
-	//        HttpListenerCallbackState callbackState = (HttpListenerCallbackState)state;
+			}
+			else if (context.Request.HttpMethod == "POST") // when client says upload
+			{
+				Console.WriteLine("received POST request");
+				string text;
+				using (var reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding))
+					text = reader.ReadToEnd();
+				string requestString = HttpUtility.UrlDecode(text);
 
-	//        while (callbackState.Listener.IsListening)
-	//        {
-	//            callbackState.Listener.BeginGetContext(new AsyncCallback(ListenerCallback), callbackState);
-	//            int n = WaitHandle.WaitAny(new WaitHandle[] { callbackState.ListenForNextRequest, stopEvent });
+				responseString = ConstructPOSTResponse(requestString);
 
-	//            if (n == 1)
-	//            {
-	//                // stopEvent was signalled 
-	//                callbackState.Listener.Stop();
-	//                break;
-	//            }
-	//        }
-	//    }
 
-	//    private void ListenerCallback(IAsyncResult ar)
-	//    {
-	//        HttpListenerCallbackState callbackState = (HttpListenerCallbackState)ar.AsyncState;
-	//        HttpListenerContext context = null;
+			}
+			else
+			{
+				Console.WriteLine("received request other than GET or POST");
 
-	//        int requestNumber = Interlocked.Increment(ref requestCounter);
+			}
 
-	//        try
-	//        {
-	//            context = callbackState.Listener.EndGetContext(ar);
-	//        }
-	//        catch (Exception ex)
-	//        {
-	//            return;
-	//        }
-	//        finally
-	//        {
-	//            callbackState.ListenForNextRequest.Set();
-	//        }
 
-	//        if (context == null) return;
 
 
-	//        HttpListenerRequest request = context.Request;
 
-	//        if (request.HasEntityBody)
-	//        {
-	//            using (System.IO.StreamReader sr = new System.IO.StreamReader(request.InputStream, request.ContentEncoding))
-	//            {
-	//                string requestData = sr.ReadToEnd();
+			//string responseString = "<HTML><BODY> Hello world!</BODY></HTML>";
+			byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
 
-	//                //Stuff I do with the request happens here  
-	//            }
-	//        }
+			// Get a response stream and write the response to it.
+			response.ContentLength64 = buffer.Length;
+			System.IO.Stream output = response.OutputStream;
+			output.Write(buffer, 0, buffer.Length);
 
+			// Close the output stream.
+			output.Close();
+			response.Close();
 
-	//        try
-	//        {
-	//            using (HttpListenerResponse response = context.Response)
-	//            {
-	//                Console.WriteLine("dealing with response");
-	//                //Thread.Sleep(10000);
-	//                //response stuff happens here  
-	//                string responseString = "Ok";
+			Console.WriteLine("At the end of ListenerCallback");
 
-	//                byte[] buffer = Encoding.UTF8.GetBytes(responseString);
-	//                response.ContentLength64 = buffer.LongLength;
-	//                response.OutputStream.Write(buffer, 0, buffer.Length);
-	//                response.Close();
-	//            }
-	//        }
-	//        catch (Exception e)
-	//        {
-	//        }
-	//    }
-	//}
+		}
 
 
+		private string ConstructGETResponse()
+		{
+			Console.WriteLine("Constructiing a response for GET request.");
+			
+			int rand = new Random().Next(1, 10);
+			string msg = "This is a response from the server :) " + rand;
 
+			Response xmlResp = Response.Randomize();
+			var xml = XmlCommunication.Serialize(xmlResp);
 
+			return xml;
 
 
+		}
 
+		private string ConstructPOSTResponse(string requestString)
+		{
+			Console.WriteLine("Constructiing a response for POST request.");
+			Console.WriteLine("Request string: ");
+			Console.WriteLine(requestString);
 
 
 
-	public class ConnectionListener
-	{
+			// prepare the type:
+			Request newRequest = new Request();
+			Request requestDeserialized = null;
+			try
+			{
+				requestDeserialized = XmlCommunication.Deserialize(newRequest, requestString);
+			}
+			catch
+			{
+				Console.WriteLine("wrong format of request!");
+
+				// don't bother to send anything:
+				return string.Empty;
+			}
+
+
+			// get the prediction from the model:
+			Response responsePrediction = predictionController.Predict(requestDeserialized);
+
+
+			// build the xml from the prediction: - TODO mbe an exception can happen here as well? - it shouldnt
+			return XmlCommunication.Serialize(responsePrediction);
+
+
+
+		}
+
+
+		private string GetLocalIPAddress()
+		{
+			var host = Dns.GetHostEntry(Dns.GetHostName());
+			foreach (var ip in host.AddressList)
+			{
+				if (ip.AddressFamily == AddressFamily.InterNetwork)
+				{
+					return ip.ToString();
+				}
+			}
+			throw new Exception("No network adapters with an IPv4 address in the system!");
+		}
+	}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+		// -----------------------------------------------------------------------------
+
+		public class ConnectionListener
+		{
 		private readonly int PORT;
 		private IPredictionController predictionController;
 
@@ -162,13 +229,14 @@ namespace ServerApp.Connection
 			this.predictionController = predictionController;
 		}
 
+
 		public void StartListening()
 		{
 			string ip = GetLocalIPAddress();
 			Console.WriteLine("ip : " + ip);
 			HttpListener server = new HttpListener();
-			server.Prefixes.Add($"https://{ip}:{PORT}/");
-			//server.Prefixes.Add($"http://localhost:{PORT}/");
+			//server.Prefixes.Add($"https://{ip}:{PORT}/");
+			server.Prefixes.Add($"http://localhost:{PORT}/");
 
 			server.Start();
 
@@ -178,6 +246,7 @@ namespace ServerApp.Connection
 			{
 				Console.WriteLine("Waiting for a client request...");
 
+				//ThreadPool.QueueUserWorkItem(Process, server.GetContext());
 				HttpListenerContext context = server.GetContext();
 
 
@@ -247,7 +316,7 @@ namespace ServerApp.Connection
 						context.Response.Close();
 						continue;
 					}
-					
+
 
 					// response headers:
 					HttpListenerResponse response = context.Response;
@@ -358,14 +427,14 @@ namespace ServerApp.Connection
 		public Response ClientResponse;
     }
 
-    public class ConnectionListenerMulti
+    public class ConnectionListenerQueue
     {
         private readonly int PORT;
         private ConcurrentDictionary<int, ClientService> readyRequests;
         private ConcurrentQueue<ClientService> waitingRequests;
 
 
-        public ConnectionListenerMulti(int port)
+        public ConnectionListenerQueue(int port)
         {
             this.PORT = port;
         }
diff --git a/Server/ServerApp/Program.cs b/Server/ServerApp/Program.cs
index b4ce14e..0ec5a63 100644
--- a/Server/ServerApp/Program.cs
+++ b/Server/ServerApp/Program.cs
@@ -42,7 +42,6 @@ namespace ServerApp
 				return;
 			}
 
-			//ConsoleForamtingTest();
 
 			
 
@@ -92,30 +91,6 @@ namespace ServerApp
 			Console.ReadLine();
         }
 
-		private static void ConsoleForamtingTest()
-		{
-
-			Console.WriteLine("List of available commands and their description: ");
-			Console.WriteLine("--------------------------------------------------");
-			// data
-			Console.WriteLine("data [OPTION] [DATE]...");
-			Console.WriteLine("   Description: Capable of showing saved data files and downloading new ones.");
-			Console.WriteLine("   -list: Lists currently saved data files. This list does not have to match the files which any of the predictors is trained on!");
-			Console.WriteLine("      Example: data -list");
-			Console.WriteLine("   -dl [startDate] [endDate]: Downloads jis, login and weather files from a specified time span. Both [startDate] and [endDate] are inclusive. Date format must be entered as [month]-[year].");
-			Console.WriteLine("      Example: data -dl 1-2019 12-2020");
-			Console.WriteLine("");
-
-			// model
-			Console.WriteLine("model [OPTION]");
-			Console.WriteLine("   Description: Capable of retraining the prediction model, going back to the previous version and listing files on which the predictor was trained. ");
-			Console.WriteLine("   -files: Lists the data files on which the currently used model was trained.");
-			Console.WriteLine("      Example: model -files");
-			Console.WriteLine("   -retrain: Retrains the model using the files currently saved in the data directory.");
-			Console.WriteLine("      Example: model -retrain");
-			Console.WriteLine("   -rollback: Switches the model back to the previous version.");
-			Console.WriteLine("      Example: model -rollback");
-		}
 
 		private static DataDownloader DataDownloadAndRetrievalTest(Config config)
 		{
@@ -211,7 +186,7 @@ namespace ServerApp
 
 		private static void ConnectionTest(IPredictionController predictionController, Config config)
 		{
-			ConnectionListener cl = new ConnectionListener(int.Parse(config.Port), predictionController);
+			ConnectionListenerAsync cl = new ConnectionListenerAsync(int.Parse(config.Port), predictionController);
 			cl.StartListening();
 
 			//HttpRequestHandler hrh = new HttpRequestHandler(int.Parse(config.Port));
