public void FCGIApp_LargeRequest() { var app = new FCGIApplication(); var expectedLength = 128317; app.OnRequestReceived += (sender, request) => { var responseBody = new byte[expectedLength]; request.WriteResponse(responseBody); request.Close(); }; // Connect to the app, impoersonating a webserver var inputStream = new MemoryStream(); inputStream.Capacity = 4096; var outputStream = new MemoryStream(); outputStream.Capacity = 4096 + expectedLength; // Send a request to it and make sure it responds to requests. var requestId = 5172; var beginRequestContent = new byte[] { 0x00, // role byte 1 (byte)Constants.FCGI_RESPONDER, // role byte 2 Constants.FCGI_KEEP_CONN, // flags 0x00, 0x00, 0x00, 0x00, 0x00 // reserved bytes }; var beginRequest = new Record { Type = Record.RecordType.BeginRequest, RequestId = requestId, ContentLength = beginRequestContent.Length, ContentData = beginRequestContent }; beginRequest.WriteToStream(inputStream); // Empty stdin indicates that the request is fully transmitted var stdinRequest = new Record { Type = Record.RecordType.Stdin, RequestId = requestId, ContentLength = 0, ContentData = new byte[0] }; stdinRequest.WriteToStream(inputStream); inputStream.Seek(0, SeekOrigin.Begin); app.ProcessStream(inputStream, outputStream); outputStream.Seek(0, SeekOrigin.Begin); // Now the app should respond with a large response, splitted into 64KB stdout records var response = Record.ReadRecord(outputStream); Assert.AreEqual(65535, response.ContentLength); var totalLength = 0; while (response.ContentLength > 0) { Assert.AreEqual(Record.RecordType.Stdout, response.Type); Assert.AreEqual(requestId, response.RequestId); totalLength += response.ContentLength; response = Record.ReadRecord(outputStream); } Assert.AreEqual(expectedLength, totalLength); // Then, an empty stdout record should indicate the end of the response body Assert.AreEqual(Record.RecordType.Stdout, response.Type); Assert.AreEqual(requestId, response.RequestId); Assert.AreEqual(0, response.ContentLength); // And finally, a EndRequest record should close the request. response = Record.ReadRecord(outputStream); Assert.AreEqual(Record.RecordType.EndRequest, response.Type); Assert.AreEqual(requestId, response.RequestId); }
public void FCGIApp_Connections() { // Create an app that actually listens on the loopback interface var app = new FCGIApplication(); app.Timeout = 100; try { app.Listen(new IPEndPoint(IPAddress.Loopback, 21511)); Request receivedRequest = null; app.OnRequestReceived += (sender, request) => { receivedRequest = request; request.WriteResponseASCII("Hello Server!"); request.Close(); }; app.Process(); var serverSocket = new Socket(SocketType.Stream, ProtocolType.Tcp); var asyncResult = serverSocket.BeginConnect(new IPEndPoint(IPAddress.Loopback, 21511), (r) => { }, null); while(!asyncResult.IsCompleted) app.Process(); serverSocket.EndConnect(asyncResult); var serverStream = new NetworkStream(serverSocket); // Now send a request to the app and make sure it is received correctly. var requestId = 5172; var beginRequestContent = new byte[] { 0x00, // role byte 1 (byte)Constants.FCGI_RESPONDER, // role byte 2 0x00, // flags 0x00, 0x00, 0x00, 0x00, 0x00 // reserved bytes }; var beginRequest = new Record { Type = Record.RecordType.BeginRequest, RequestId = requestId, ContentLength = beginRequestContent.Length, ContentData = beginRequestContent }; beginRequest.WriteToStream(serverStream); var stdinRequest = new Record { Type = Record.RecordType.Stdin, RequestId = requestId, ContentLength = 0, ContentData = new byte[0] }; stdinRequest.WriteToStream(serverStream); serverStream.Flush(); // Make the app digest everything while(receivedRequest == null) { app.Process(); } // Do we have the correct request? Assert.AreEqual(requestId, receivedRequest.RequestId); // And did the response work? var responseRecord = Record.ReadRecord(serverStream); Assert.AreEqual(Record.RecordType.Stdout, responseRecord.Type); Assert.AreEqual("Hello Server!", Encoding.ASCII.GetString(responseRecord.ContentData)); // Change the timeout while still connected app.Timeout = 100; app.StopListening(); Assert.IsFalse(app.Connected); } // If the port is already in use, an execution can be thrown. // Report the test as inconclusive then. catch(SocketException e) { Assert.Inconclusive("SocketException: " + e.Message); } }
public void FCGIApp_GetValues() { var app = new FCGIApplication(); // Connect to the app, impoersonating a webserver var inputStream = new MemoryStream(); inputStream.Capacity = 4096; var outputStream = new MemoryStream(); outputStream.Capacity = 4096; // Send a GetValues record var getValues = new Record { Type = Record.RecordType.GetValues, RequestId = 0, ContentLength = 0, }; getValues.WriteToStream(inputStream); inputStream.Seek(0, SeekOrigin.Begin); app.ProcessStream(inputStream, outputStream); outputStream.Seek(0, SeekOrigin.Begin); // Now the app should respond with a GetValuesResult var response = Record.ReadRecord(outputStream); Assert.AreEqual(Record.RecordType.GetValuesResult, response.Type); Assert.AreEqual(0, response.RequestId); var responseValues = response.GetNameValuePairs(); // Response should include these three values Assert.Contains("FCGI_MAX_CONNS", responseValues.Keys); Assert.Contains("FCGI_MAX_REQS", responseValues.Keys); Assert.Contains("FCGI_MPXS_CONNS", responseValues.Keys); // No other values should be included Assert.AreEqual(3, responseValues.Count); int responseMaxConns, responseMaxReqs, responseMultiplexing; // Make sure the returned values look plausible var sMaxConns = Encoding.ASCII.GetString(responseValues["FCGI_MAX_CONNS"]); var sMaxReqs = Encoding.ASCII.GetString(responseValues["FCGI_MAX_REQS"]); var sMultiplexing = Encoding.ASCII.GetString(responseValues["FCGI_MPXS_CONNS"]); Assert.IsTrue(int.TryParse(sMaxConns, out responseMaxConns)); Assert.IsTrue(int.TryParse(sMaxReqs, out responseMaxReqs)); Assert.IsTrue(int.TryParse(sMultiplexing, out responseMultiplexing)); Assert.GreaterOrEqual(responseMaxConns, 0); Assert.GreaterOrEqual(responseMaxReqs, 0); Assert.GreaterOrEqual(responseMultiplexing, 0); Assert.LessOrEqual(responseMultiplexing, 1); }
public void FCGIApp_Request() { var app = new FCGIApplication(); app.OnRequestReceived += (sender, request) => { request.WriteResponseASCII("Hello!"); request.Close(); }; // Connect to the app, impoersonating a webserver var streamServerToApp = new MemoryStream(); streamServerToApp.Capacity = 4096; var streamAppToServer = new MemoryStream(); streamAppToServer.Capacity = 4096; // Send a request to it and make sure it responds to requests. var requestId = 5172; var beginRequestContent = new byte[] { 0x00, // role byte 1 (byte)Constants.FCGI_RESPONDER, // role byte 2 Constants.FCGI_KEEP_CONN, // flags 0x00, 0x00, 0x00, 0x00, 0x00 // reserved bytes }; var beginRequest = new Record { Type = Record.RecordType.BeginRequest, RequestId = requestId, ContentLength = beginRequestContent.Length, ContentData = beginRequestContent }; beginRequest.WriteToStream(streamServerToApp); // Empty stdin indicates that the request is fully transmitted var stdinRequest = new Record { Type = Record.RecordType.Stdin, RequestId = requestId, ContentLength = 0, ContentData = new byte[0] }; stdinRequest.WriteToStream(streamServerToApp); streamServerToApp.Seek(0, SeekOrigin.Begin); app.ProcessStream(streamServerToApp, streamAppToServer);; streamAppToServer.Seek(0, SeekOrigin.Begin); // Now the app should respond with 'Hello!' var response = Record.ReadRecord(streamAppToServer); var expectedBytes = Encoding.ASCII.GetBytes("Hello!"); Assert.AreEqual(Record.RecordType.Stdout, response.Type); Assert.AreEqual(requestId, response.RequestId); Assert.AreEqual(expectedBytes.Length, response.ContentLength); Assert.AreEqual(expectedBytes, response.ContentData); // Then, an empty stdout record should indicate the end of the response body response = Record.ReadRecord(streamAppToServer); Assert.AreEqual(Record.RecordType.Stdout, response.Type); Assert.AreEqual(requestId, response.RequestId); Assert.AreEqual(0, response.ContentLength); // And finally, a EndRequest record should close the request. response = Record.ReadRecord(streamAppToServer); Assert.AreEqual(Record.RecordType.EndRequest, response.Type); Assert.AreEqual(requestId, response.RequestId); }
public void FCGIApp_Params() { var app = new FCGIApplication(); Request receivedRequest = null; app.OnRequestReceived += (sender, request) => { receivedRequest = request; }; // Connect to the app, impoersonating a webserver var streamServerToApp = new MemoryStream(); streamServerToApp.Capacity = 4096; var streamAppToServer = new MemoryStream(); streamAppToServer.Capacity = 4096; // Send a request to it and make sure it responds to requests. var requestId = 5172; var beginRequestContent = new byte[] { 0x00, // role byte 1 (byte)Constants.FCGI_RESPONDER, // role byte 2 Constants.FCGI_KEEP_CONN, // flags 0x00, 0x00, 0x00, 0x00, 0x00 // reserved bytes }; var beginRequest = new Record { Type = Record.RecordType.BeginRequest, RequestId = requestId, ContentLength = beginRequestContent.Length, ContentData = beginRequestContent }; beginRequest.WriteToStream(streamServerToApp); var paramDict = new Dictionary<string, byte[]> { {"SOME_NAME", Encoding.UTF8.GetBytes("SOME_KEY") }, {"SOME_OTHER_NAME", Encoding.UTF8.GetBytes("SOME_KEY") }, //{"ONE_MEGABYTE_OF_ZEROS", new byte[1024 * 1024] }, {"EMPTY_VALUE", new byte[0] }, {"1", Encoding.UTF8.GetBytes("☕☳üß - \n \r\n .,;(){}%$!") }, {"2", Encoding.UTF8.GetBytes("") }, {"3", Encoding.UTF8.GetBytes(" ") }, {"4", Encoding.UTF8.GetBytes("\n") }, {"5", Encoding.UTF8.GetBytes(";") }, }; var paramsRequest = new Record { Type = Record.RecordType.Params, RequestId = requestId }; paramsRequest.SetNameValuePairs(paramDict); paramsRequest.WriteToStream(streamServerToApp); // Empty params record indicates that the parameters are fully transmitted var paramsCloseRequest = new Record { Type = Record.RecordType.Params, RequestId = requestId, ContentLength = 0, ContentData = new byte[0] }; paramsCloseRequest.WriteToStream(streamServerToApp); // Empty stdin indicates that the request is fully transmitted var stdinRequest = new Record { Type = Record.RecordType.Stdin, RequestId = requestId, ContentLength = 0, ContentData = new byte[0] }; stdinRequest.WriteToStream(streamServerToApp); streamServerToApp.Seek(0, SeekOrigin.Begin); app.ProcessStream(streamServerToApp, streamAppToServer); // Now the app should have received the request. Make sure the Parameters correctly decoded Assert.IsNotNull(receivedRequest); Assert.AreEqual(paramDict.Count, receivedRequest.Parameters.Count); foreach (var entry in paramDict) { Assert.Contains(entry.Key, receivedRequest.Parameters.Keys); Assert.AreEqual(entry.Value, receivedRequest.Parameters[entry.Key]); } }
public void Records_BrokenRecords() { // Create some broken records and make sure the right things happen Record result; // An empty stream should simply result in a null record var emptyStream = new MemoryStream(new byte[0]); result = Record.ReadRecord(emptyStream); Assert.IsNull(result); // A wrong version number bytes should throw a InvalidDataException var wrongVersion = new MemoryStream(new byte[1] { Constants.FCGI_VERSION_1 + 1 }); Assert.Throws(typeof(InvalidDataException), () => { Record.ReadRecord(wrongVersion); }); // So should a bunch of zeroes var zeroes = new MemoryStream(new byte[4] { 0, 0, 0, 0 }); Assert.Throws(typeof(InvalidDataException), () => { Record.ReadRecord(zeroes); }); // And a correct version number with an incomplete header var incomplete = new MemoryStream(new byte[4] { Constants.FCGI_VERSION_1, 0, 0, 0 }); Assert.Throws(typeof(InvalidDataException), () => { Record.ReadRecord(incomplete); }); // Writing should fail for content above 64KB var tooLong = new Record { Version = Constants.FCGI_VERSION_1, Type = Record.RecordType.Stderr, RequestId = 123, ContentLength = 127000, ContentData = new byte[127000] }; Assert.Throws(typeof(InvalidOperationException), () => { tooLong.WriteToStream(new MemoryStream()); }); }