/// <summary> /// Creates a new request. Usually, you don't need to call this. /// </summary> /// <remarks> Records are created by <see cref="FCGIApplication"/> when a new request has been received.</remarks> public Request(int requestId, Stream responseStream, FCGIApplication app = null, bool keepAlive = false) { this.RequestId = requestId; ResponseStream = responseStream; ParamStream = new MemoryStream(); ManagingApp = app; KeepAlive = keepAlive; }
/// <summary> /// Creates a new request. Usually, you don't need to call this. /// </summary> /// <remarks> Records are created by <see cref="FCGIApplication"/> when a new request has been received.</remarks> public Request(int requestId, Stream responseStream, FCGIApplication app = null, string body = "", bool keepAlive = false) { this.RequestId = requestId; ResponseStream = responseStream; ParamStream = new MemoryStream(); ManagingApp = app; Body = body; KeepAlive = keepAlive; }
static void Main(string[] args) { // Create a new FCGIApplication, will accept FastCGI requests var app = new FCGIApplication(); // Handle requests by responding with a 'Hello World' message app.OnRequestReceived += (sender, request) => { request.WriteResponseASCII("HTTP/1.1 200 OK\nContent-Type:text/html\n\nHello World!"); request.Close(); }; // Start listening on port 19000 app.Run(19000); }
public void Nginx_SingleRequest() { AssertNginxInPath(); var nginx = StartNginx("ServerConfigs/NginxBasicConfig.conf"); var expectedResult = "Hello World!"; var app = new FCGIApplication(); app.OnRequestReceived += (sender, request) => { request.WriteResponseASCII("HTTP/1.1 200 OK\nContent-Type:text/html\n\n" + expectedResult); request.Close(); }; var appThread = new Thread(() => { app.Run(19000); }); appThread.Start(); var result = GetHttp("http://localhost:8182"); app.Stop(); StopNginx(nginx); Assert.AreEqual(expectedResult, result); }
public void Nginx_ManyRequests_Keepalive() { AssertNginxInPath(); var nginx = StartNginx("ServerConfigs/NginxKeepalive.conf"); var expectedResult = "Hello World!"; var app = new FCGIApplication(); app.OnRequestReceived += (sender, request) => { request.WriteResponseASCII("HTTP/1.1 200 OK\nContent-Type:text/html\n\n" + expectedResult); request.Close(); }; var appThread = new Thread(() => { app.Run(19000); }); appThread.Start(); Task<string>[] results = new Task<string>[1000]; for (int i = 0; i < results.Length; i++) { results[i] = GetHttpAsync("http://localhost:8182"); Thread.Sleep(1); } for (int i = 0; i < results.Length; i++) { results[i].Wait(20000); var result = results[i].Result; Assert.AreEqual(expectedResult, result); } StopNginx(nginx); app.Stop(); appThread.Join(500); }
public FastCGIBackend(int port) { _port = port; _fcgiApplication = new FCGIApplication(); _fcgiApplication.OnRequestReceived += FcgiApplicationOnOnRequestReceived; }
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_DisconnectedOperations() { // Create a new FCGIApplication without opening a connection // and make sure everything can be called without any problems var app = new FCGIApplication(); Assert.IsFalse(app.Process()); Assert.IsFalse(app.ProcessStream(new MemoryStream(), new MemoryStream())); Assert.IsFalse(app.ProcessSingleRecord(new MemoryStream(), new MemoryStream())); Assert.IsFalse(app.Connected); app.StopListening(); }
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]); } }