/// <summary> /// The entry point for the web server. Gets called after you start NoIIS. /// </summary> /// <param name="args">The parameters you give to NoIIS.</param> public static void Main(string[] args) { if (args.Length < 8) { Console.WriteLine("Please provide at least four arguments:"); Console.WriteLine(" 1. The assembly containing the handler factories e.g. 'my-app.dll'"); Console.WriteLine(" 2. The temp. folder for uploaded files as cache for the processing"); Console.WriteLine(" 3. The max. request size (bytes)"); Console.WriteLine(" 4. The minimal visits of clients to prevent blocking. Use 0 to disable"); Console.WriteLine(" 5. The entry-time (seconds) wherein the client must gain the minimal visits"); Console.WriteLine(" 6. The maximal visits of clients before blocking. Use 0 to disable"); Console.WriteLine(" 7. The keep-alive time (seconds) before visits get deleted. Client can gain only max. visits within"); Console.WriteLine(" 8. The client's block time (seconds)"); Console.WriteLine(" 9. The client's profile life time (seconds)"); Console.WriteLine(" 10+. The prefix(es) for accepted request e.g. 'http://127.0.0.1:8080/' or 'http://*/test/*', etc."); Console.WriteLine(); return; } NoIISServer.assembly = args[0].Trim(); NoIISServer.tempFolder = args[1].EndsWith(string.Empty + Path.DirectorySeparatorChar) ? args[1] : args[1] + Path.DirectorySeparatorChar; NoIISServer.maxRequestSizeBytes = int.Parse(args[2]); NoIISServer.visitsMinimum = int.Parse(args[3]); NoIISServer.entryTimeSeconds = int.Parse(args[4]); NoIISServer.visitsMaximum = int.Parse(args[5]); NoIISServer.keepAliveTimeSeconds = int.Parse(args[6]); NoIISServer.blockTimeSeconds = int.Parse(args[7]); NoIISServer.clientLifeTimeSeconds = int.Parse(args[8]); NoIISServer.hosts = args.Skip(9).ToArray(); NoIISServer.factories = FindHttpHandlerFactories.findFactories(NoIISServer.assembly); NoIISServer.runner(); }
/// <summary> /// The main-thread of NoIIS where all client requests are arrives. /// </summary> public static void runner() { // Set the min. number of threads for the thread-pool: ThreadPool.SetMinThreads(1000, 600); // Start maintaining: Task.Factory.StartNew(() => NoIISServer.maintenanceThread(), TaskCreationOptions.LongRunning); // The HTTP listener: var listener = new HttpListener(); // Add all hosts to the listener as end-points: NoIISServer.hosts.ToList().ForEach((n) => { listener.Prefixes.Add(n); Console.WriteLine(n); }); // Start listening: listener.Start(); // Loop forever: while (true) { try { // Force a maximum number of concurrent clients: NoIISServer.semaphoreClients.Wait(); // Start the new client thread: Task.Factory.StartNew(() => NoIISServer.clientThread(listener)); } catch (Exception e) { Console.WriteLine("There was an error for starting a client thread: " + e.Message); Console.WriteLine(e.StackTrace); NoIISServer.semaphoreClients.Release(); } } }
private static async void clientThread(HttpListener listener) { try { // Will wait for the next client request: var context = await listener.GetContextAsync(); // Filter-out any requests which are to huge: if (context.Request.ContentLength64 > NoIISServer.maxRequestSizeBytes) { Console.WriteLine("A request was to huge: {0} bytes.", context.Request.ContentLength64); context.Response.Abort(); return; } // Read the client's address: var clientAddress = context.Request.RemoteEndPoint.Address.ToString(); // Is this address known? if (!NoIISServer.clients.ContainsKey(clientAddress)) { // Unknown: NoIISServer.createAndStoreClientProfile(clientAddress); } // // In the meanwhile the maintenance thread could take action. Scenario: This client is known // but was not visiting for the allowed time span. One moment after the previous check, the // allowed time span expires and the maintenance thread deletes the entry. In such a case, // an exception will be throwed. Is this the case, we add a new client profile. This is // fine and a valid case. // Client clientProfile; try { // Read the client's profile: clientProfile = NoIISServer.clients[clientAddress]; } catch (KeyNotFoundException) { // Valide case. Create a new entry: NoIISServer.createAndStoreClientProfile(clientAddress); // Read it again. It is not possible that the case occurs again: clientProfile = NoIISServer.clients[clientAddress]; } // Is this client blocked? if (clientProfile.Blocked) { Console.WriteLine("A blocked client tried to access: {0}.", clientAddress); context.Response.Abort(); return; } // File this visit. It is intended to do so after the block check. // Otherwise, an attacker could use this fact to flood the memory // with visits: NoIISServer.clientVisits[clientAddress].Enqueue(DateTime.UtcNow); clientProfile.LastVisitUTC = DateTime.UtcNow; // Store the changed time. This could happens parallel with several // threads. Thus, it is not atomic. This behaviour is intended! Only // the roughly time is necessary. NoIISServer.clients[clientAddress] = clientProfile; try { // Create the NoIIS request: var request = new NoIISRequest(context, NoIISServer.tempFolder); // Create the NoIIS response: var response = new NoIISResponse(context); // Create the NoIIS context with request and response: var webContext = new NoIISContext(request, response); // Search for a handler inside all factories which matches the request: var foundHandler = false; foreach (var factory in NoIISServer.factories) { // Does this factory is able to deliver a handler for this request? var handler = factory.GetHandler(webContext, request.RequestType, request.Path, string.Empty); // Case: Yes, we have found the first handler: if (handler != null) { // Let the handler process the request: handler.ProcessRequest(webContext); foundHandler = true; // We only use the first matching handler: break; } } // Case: No handler was found if (!foundHandler) { Console.WriteLine("No handler found for the URL '{0}'.", request.RawUrl); response.StatusCode = 404; } try { response.Dispose(); } catch { } try { request.Dispose(); } catch { } } catch (Exception e) { Console.WriteLine("Exception while processing request: {0}", e.Message); Console.WriteLine(e.StackTrace); try { context.Response.Abort(); } catch { } } } catch (Exception e) { Console.WriteLine("Exception while accepting request: {0}", e.Message); Console.WriteLine(e.StackTrace); } finally { NoIISServer.semaphoreClients.Release(); } }