Exemplo n.º 1
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();
            }
        }
Exemplo n.º 2
0
 /// <summary>
 /// The constructor of this class. You dont have to use this by your own, NoIIS will handle this for you.
 /// </summary>
 /// <param name="request">The client's request.</param>
 /// <param name="response">Your response to the client's request.</param>
 public NoIISContext(NoIISRequest request, NoIISResponse response)
 {
     this.request  = request;
     this.response = response;
 }
Exemplo n.º 3
0
        /// <summary>
        /// Processes the request to get all uploaded files.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="bodyStream">The stream for the request's body.</param>
        /// <param name="tmpFolderPATH">The temporary folder for this request.</param>
        /// <param name="niiRequest">The NoIIS request to register additional open streams.</param>
        /// <returns>Returns the collection with all uploaded files. The collection is possibily empty.</returns>
        public static HttpFileCollectionBase GetFiles(this HttpListenerRequest request, Stream bodyStream, string tmpFolderPATH, NoIISRequest niiRequest)
        {
            if (request == null || tmpFolderPATH == null || bodyStream == null)
            {
                return(new NoIISFileCollection());
            }

            if (!request.HasEntityBody)
            {
                return(new NoIISFileCollection());
            }

            // Case #1: Just a form
            //		Content Type = application/x-www-form-urlencoded
            //		Body = MAX_FILE_SIZE=100000&t%C3%A4stValue=das+ist+ein+test

            //
            // ==> Does not matter
            //

            // Case #2: Just a file
            //      Content type = multipart/form-data; boundary=----WebKitFormBoundaryeghC9TqseFEkpCXg
            //      Body =
            //      ------WebKitFormBoundaryeghC9TqseFEkpCXg
            //      Content-Disposition: form-data; name="meinFileö"; filename="Short Time Notes.txt"
            //      Content-Type: text/plain
            //
            //		DATA STREAM
            //		------WebKitFormBoundaryeghC9TqseFEkpCXg--

            //
            // ==> Does not matter
            //

            // Case #3: Multiple files
            //      Content type = multipart/form-data; boundary=----WebKitFormBoundaryeghC9TqseFEkpCXg
            //      Body =
            //      ------WebKitFormBoundaryeghC9TqseFEkpCXg
            //      Content-Disposition: form-data; name="meinFileö"; filename="Short Time Notes.txt"
            //      Content-Type: text/plain
            //
            //		DATA STREAM
            //		------WebKitFormBoundaryeghC9TqseFEkpCXg
            //      Content-Disposition: form-data; name="meinFile2"; filename="fsdfsdf.txt"
            //      Content-Type: text/plain
            //
            //		DATA STREAM
            //		------WebKitFormBoundaryeghC9TqseFEkpCXg--

            //
            // ==> Does not matter
            //

            // Case #4: A file and form data
            //      Content type = multipart/form-data; boundary=----WebKitFormBoundaryeghC9TqseFEkpCXg
            //      Body =
            //      ------WebKitFormBoundaryeghC9TqseFEkpCXg
            //      Content-Disposition: form-data; name="meinFileö"; filename="Short Time Notes.txt"
            //      Content-Type: text/plain
            //
            //		DATA STREAM
            //		------WebKitFormBoundaryeghC9TqseFEkpCXg
            //		Content-Disposition: form-data; name="einsÖÄÜ"
            //
            //		2354öäpü
            //		------WebKitFormBoundaryeghC9TqseFEkpCXg
            //		Content-Disposition: form-data; name="zwei"
            //
            //		[email protected]
            //		------WebKitFormBoundaryeghC9TqseFEkpCXg--

            //
            // ==> Does not matter, use case #5.
            //

            // Case #5: Multiple files and form data
            //      Content type = multipart/form-data; boundary=----WebKitFormBoundaryeghC9TqseFEkpCXg
            //      Body =
            //      ------WebKitFormBoundaryeghC9TqseFEkpCXg
            //      Content-Disposition: form-data; name="meinFileö"; filename="Short Time Notes.txt"
            //      Content-Type: text/plain
            //
            //		DATA STREAM
            //		------WebKitFormBoundaryeghC9TqseFEkpCXg
            //		Content-Disposition: form-data; name="einsÖÄÜ"
            //
            //		2354öäpü
            //		------WebKitFormBoundaryeghC9TqseFEkpCXg
            //		Content-Disposition: form-data; name="zwei"
            //
            //		[email protected]
            //		------WebKitFormBoundaryeghC9TqseFEkpCXg
            //      Content-Disposition: form-data; name="meinFile3"; filename="Ssdfsdfd.fdf"
            //      Content-Type: text/plain
            //
            //		DATA STREAM
            //		------WebKitFormBoundaryeghC9TqseFEkpCXg
            //		Content-Disposition: form-data; name="rterz"
            //
            //		[email protected]
            //		------WebKitFormBoundaryeghC9TqseFEkpCXg--

            // If the request's content type does not match the requirements,
            // return here:
            if (!request.ContentType.Contains("multipart/form-data; boundary="))
            {
                return(new NoIISFileCollection());
            }

            // The cache for all tmp. filenames for files in the request:
            var cacheFilenames = new ConcurrentDictionary <string, NoIISPostedFile>();

            using (bodyStream)
            {
                // Dummy to consume the name of each parameter:
                var dummy = string.Empty;

                // Create the parser for the body:
                var smftp = new StreamingMultipartFormDataParser(bodyStream);

                // The parser requires both, the parameter and the file handler:
                smftp.ParameterHandler += parameter => dummy = parameter.Name;

                // The file handler gets called for every chunk of each file inside:
                smftp.FileHandler += (name, fileName, type, disposition, buffer, bytes) =>
                {
                    // Generate a key for this file:
                    var key = name + fileName + type + disposition;

                    // The destination tmp. file name:
                    var destination = string.Empty;
                    if (!cacheFilenames.ContainsKey(key))
                    {
                        // Case: A new file.
                        cacheFilenames[key] = new NoIISPostedFile();

                        // Generate the tmp. file name with path:
                        cacheFilenames[key].TMPFilenamePATH = tmpFolderPATH + Guid.NewGuid().ToString();
                        cacheFilenames[key].FormName        = name;
                        cacheFilenames[key].setContentType(type);
                        cacheFilenames[key].setFileName(fileName);
                    }

                    // Read the current destination:
                    destination = cacheFilenames[key].TMPFilenamePATH;

                    try
                    {
                        // Append the bytes to the tmp. file:
                        using (var fileStream = File.Open(destination, FileMode.Append, FileAccess.Write, FileShare.None))
                        {
                            fileStream.Write(buffer, 0, bytes);
                        }
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine("Exception while processing the file upload '{1}' (name='{2}'): {0}", e.Message, fileName, name);
                        Console.WriteLine(e.StackTrace);
                    }
                };

                try
                {
                    // Start the processing. This call will block until end of processing:
                    smftp.Run();
                }
                catch (Exception e)
                {
                    Console.WriteLine("Exception while processing the file upload: {0}", e.Message);
                    Console.WriteLine(e.StackTrace);
                }
            }

            //
            // The files should now be available as tmp. data.
            //

            // The result's collection:
            var result = new List <NoIISPostedFile>(cacheFilenames.Count);

            // Loop over all files:
            foreach (var key in cacheFilenames.Keys)
            {
                // Get a file:
                var file = cacheFilenames[key];

                // Get the tmp. file's name:
                var filename = file.TMPFilenamePATH;

                // Get the stream to the file's content:
                var stream = File.OpenRead(filename);

                // Register this open stream:
                niiRequest.AddProbablyOpenStream(stream);

                // Store the file's length:
                file.setContentLength((int)stream.Length);

                // Store the stream:
                file.setInputStream(stream);

                // Store this file to the result:
                result.Add(file);
            }

            return(new NoIISFileCollection(result));
        }