MockServer/MockWebServer.cs (113 lines of code) (raw):

using System; using System.Collections.Generic; using System.Net; using System.Text; using System.Threading; using System.IO; namespace MockServer { /// <summary> /// Mock web server that can return custom Http responses to a Http request. Mainly used for testing purposes. /// </summary> public class MockWebServer { /// <summary> /// Locah host name /// </summary> public const string Host = "localhost"; /// <summary> /// Thread worker where the server runs /// </summary> private readonly Thread _threadWorker; /// <summary> /// Internal list which enques custom Http responses user wants to return. For example: If user wants to return a response of 408 user enqueues a MockResponse /// for 408 /// </summary> private readonly Queue<MockResponse> _list; /// <summary> /// Http listener that immplements a mock server on a localhost and a particular port /// </summary> private readonly HttpListener _webListener; /// <summary> /// Initializes the Httplistener to listen to a particular port of localhost /// </summary> /// <param name="port"></param> public MockWebServer(int port) { _threadWorker = new Thread(Run); _list = new Queue<MockResponse>(); _webListener = new HttpListener(); _webListener.Prefixes.Add("http://" + Host + ":" + port + "/"); } /// <summary> /// Starts the server thread /// </summary> public void StartServer() { _threadWorker.Start(); } /// <summary> /// Stops the MockWebServer thread /// </summary> public void StopServer() { EnqueMockResponse(null); _threadWorker.Join(); } /// <summary> /// Adds a custom MockResponse to the back of the queue. /// </summary> /// <param name="item">MockResponse</param> public void EnqueMockResponse(MockResponse item) { lock (_list) { _list.Enqueue(item); Monitor.Pulse(_list); } } /// <summary> /// Retrieves the Mockresponse from the queue /// </summary> /// <returns></returns> private MockResponse GetMockResponse() { lock (_list) { while (_list.Count == 0) { Monitor.Wait(_list); } return _list.Dequeue(); } } /// <summary> /// Runs the server. Polls for a mockresponse from the queue, waits for the request to come in and then sets the response of the request. /// Keeps doing this till the server is topped. /// </summary> public void Run() { _webListener.Start(); while (true) { MockResponse resp = GetMockResponse(); if (resp == null) { _webListener.Close(); return; } try { var context = _webListener.GetContext(); HttpListenerRequest request = context.Request; var response = context.Response; Console.WriteLine(request.Headers.AllKeys); response.StatusCode = (int)resp.StatusCode.Value; response.StatusDescription = resp.StatusDescription; if (resp.ResponseBody != null) { Wait(context.Request.InputStream); var bytes = Encoding.UTF8.GetBytes(resp.ResponseBody); response.ContentType = "application/json"; response.ContentLength64 = bytes.Length; response.OutputStream.WriteAsync(bytes, 0, bytes.Length).Wait(); } response.Close(); }catch (Exception ex) { Console.WriteLine(ex); return; } } } /// <summary> /// Waits for a specified amount of time. Used to test cancellation token. /// </summary> /// <param name="inputStream"></param> private void Wait(Stream inputStream) { byte[] buff = new byte[250]; int noOfBytes = inputStream.Read(buff, 0, buff.Length); string val = Encoding.UTF8.GetString(buff, 0, noOfBytes); string[] arr = val.Split(':'); if (arr[0].Equals("wait")) { int waitTime = Int32.Parse(arr[1]); Thread.Sleep(waitTime * 1000); } } public void StopAbruptly() { _webListener.Close(); } } /// <summary> /// Custom Http response that the mock server uses to set the response of a request /// </summary> public class MockResponse { /// <summary> /// Http Status code /// </summary> public HttpStatusCode? StatusCode { get; set; } /// <summary> /// Http status message /// </summary> public string StatusDescription { get; set; } public string ResponseBody { get; set; } public MockResponse(int status, string description, string body=null) { StatusCode = (HttpStatusCode)status; StatusDescription = description; ResponseBody = body; } } }