Example #1
0
        /// <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();
        }
Example #2
0
        /// <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();
                }
            }
        }
Example #3
0
        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();
            }
        }