Provides methods to parse a multipart/form-data stream into it's parameters and file data.

A parameter is defined as any non-file data passed in the multipart stream. For example any form fields would be considered a parameter.

The parser determines if a section is a file or not based on the presence or absence of the filename argument for the Content-Type header. If filename is set then the section is assumed to be a file, otherwise it is assumed to be parameter data.

Beispiel #1
1
		public static NameValueCollection Parse(Stream stream, string contentType, out PostedFile[] files)
		{
			var parser = new MultipartFormDataParser(stream);
			var filesloaded = new List<PostedFile>();
			var form = new NameValueCollection();

			foreach(var f in parser.Files)
			{
				Stream data = f.Data;
				var file = new PostedFile();
				file.Contents = new MemoryStream (f.Data.ReadAllBytes ());
				file.Filename = f.FileName;
				filesloaded.Add (file);
			}

			files = filesloaded.ToArray ();

			var result = new NameValueCollection ();
			foreach (var kvp in parser.Parameters) 
			{
				result.Add (kvp.Key.ToString (), kvp.Value.ToString ());
			}

			return result;
		}
Beispiel #2
0
        public static HttpResponseMessage Process(HttpRequestMessage request)
        {
            // Check if the request contains multipart/form-data.
            if (!request.Content.IsMimeMultipartContent())
            {
                throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
            }

            try
            {
                Stream reqStream = request.Content.ReadAsStreamAsync().Result;
                MemoryStream tempStream = new MemoryStream();
                reqStream.CopyTo(tempStream);
                
                tempStream.Seek(0, SeekOrigin.End);
                Log.Submit(LogLevel.Debug, "Upload request has " + tempStream.Length + " bytes");
                tempStream.Position = 0;
                
                StreamContent streamContent = new StreamContent(tempStream);
                foreach (KeyValuePair<string, IEnumerable<string>> header in request.Content.Headers)
                {
                    streamContent.Headers.Add(header.Key, header.Value);
                    Log.Submit(LogLevel.Debug, "Header " + header.Key + ": " + string.Join(",", header.Value));
                }

                MultipartFormDataParser parser = new MultipartFormDataParser(tempStream);

                // This illustrates how to get the file names.
                FilePart file = parser.Files.FirstOrDefault();
                if (parser.Files == null || parser.Files.Count != 1)
                    throw new InvalidOperationException();
                if (file == null || file.FileName != "package")
                    throw new InvalidOperationException();

                PackageDAO.ProcessPackage(file.Data);

                return request.CreateResponse(HttpStatusCode.Created);
            }
            catch (AlreadyExistsException e)
            {
                Log.SubmitException(e);
                return request.CreateErrorResponse(HttpStatusCode.Conflict, e);
            }
            catch (InvalidOperationException e)
            {
                Log.SubmitException(e);
                return request.CreateErrorResponse(HttpStatusCode.BadRequest, e);
            }
            catch (Exception e)
            {
                Log.SubmitException(e);
                return request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
            }
        }
Beispiel #3
0
        public virtual Request GetRequest()
        {
            Request destination = new Request();

            destination.AcceptTypes = Source.AcceptTypes;
            destination.ContentEncoding = Source.ContentEncoding;
            destination.ContentLength = Source.ContentLength64;
            destination.ContentType = Source.ContentType;
            destination.Cookies = Source.Cookies;
            destination.Headers = Source.Headers;
            destination.RawUrl = Source.RawUrl;
            destination.UserAgent = Source.UserAgent;
            destination.QueryString = Source.QueryString;
            destination.LowLevelRequest = Source;
            destination.Form = new Dictionary<string, string>();
            destination.Files = new List<HttpFile>();

            if (Source.ContentLength64 < 1)
                return destination;

            Stream inputStream = Source.InputStream;
            MultipartFormDataParser parser = new MultipartFormDataParser(inputStream);

            foreach (var parameterName in parser.Parameters.Keys)
            {
                destination.Form.Add(parameterName, parser.Parameters[parameterName].Data);
            }

            foreach (var file in parser.Files)
            {
                HttpFile httpFile = new HttpFile
                {
                    ContentType = file.ContentType,
                    ContentDisposition = file.ContentDisposition,
                    FileName = file.FileName,
                    Name = file.Name,
                    Data = file.Data
                };
            }

            return destination;
        }
            /// <summary>
            ///     Validates the output of the parser against the expected outputs for
            ///     this test
            /// </summary>
            /// <param name="parser">
            ///     The parser to validate.
            /// </param>
            /// <returns>
            ///     The <see cref="bool" /> representing if this test passed.
            /// </returns>
            public bool Validate(MultipartFormDataParser parser)
            {
                // Deal with all the parameters who are only expected to have one value.
                var expectedParametersWithSingleValue = ExpectedParams
                    .GroupBy(p => p.Name)
                    .Where(g => g.Count() == 0)
                    .Select(g => g.Single());

                foreach (var expectedParameter in expectedParametersWithSingleValue)
                {
                    if (!parser.HasParameter(expectedParameter.Name))
                    {
                        return false;
                    }

                    var actualValue = parser.GetParameterValue(expectedParameter.Name);
                    var actualValueFromValues = parser.GetParameterValues(expectedParameter.Name).Single();

                    if (actualValue != actualValueFromValues)
                    {
                        Console.WriteLine("GetParameterValue vs. GetParameterValues mismatch! ({0} != {1})", actualValue, actualValueFromValues);
                        return false;
                    }

                    Console.WriteLine("Expected {0} = {1}. Found {2} = {3}", expectedParameter.Name, expectedParameter.Data, expectedParameter.Name, actualValue);

                    if (expectedParameter.Data != actualValue)
                    {
                        return false;
                    }
                }

                // Deal with the parameters who are expected to have more then one value
                var expectedParametersWithMultiValues = ExpectedParams
                    .GroupBy(p => p.Name)
                    .Where(a => a.Count() > 1);

                foreach (var expectedParameters in expectedParametersWithMultiValues)
                {
                    var key = expectedParameters.Key;
                    if (!parser.HasParameter(key))
                    {
                        return false;
                    }

                    var actualValues = parser.GetParameterValues(key);

                    Console.WriteLine("Expected {0} = {1}. Found {2} = {3}",
                        key,
                        string.Join(",", expectedParameters.Select(p => p.Data)),
                        key,
                        string.Join(",", actualValues)
                    );

                    if (actualValues.Count() != expectedParameters.Count() || actualValues.Zip(expectedParameters, Tuple.Create).Any(t => t.Item1 != t.Item2.Data))
                    {
                        return false;
                    }
                }

                // Validate files
                foreach (var filePart in ExpectedFileData)
                {
                    var foundPairMatch = false;
                    foreach (var file in parser.Files)
                    {
                        if (filePart.Name == file.Name)
                        {
                            foundPairMatch = true;

                            FilePart expectedFile = filePart;
                            FilePart actualFile = file;

                            if (expectedFile.Name != actualFile.Name || expectedFile.FileName != actualFile.FileName)
                            {
                                return false;
                            }

                            if (expectedFile.ContentType != actualFile.ContentType ||
                                expectedFile.ContentDisposition != actualFile.ContentDisposition)
                            {
                                return false;
                            }

                            // Read the data from the files and see if it's the same
                            var reader = new StreamReader(expectedFile.Data);
                            string expectedFileData = reader.ReadToEnd();

                            reader = new StreamReader(actualFile.Data);
                            string actualFileData = reader.ReadToEnd();

                            if (expectedFileData != actualFileData)
                            {
                                return false;
                            }

                            break;
                        }
                    }

                    if (!foundPairMatch)
                    {
                        return false;
                    }
                }

                return true;
            }
 public void HandlesFullPathAsFileNameWithSemicolonCorrectly()
 {
     using (Stream stream = TestUtil.StringToStream(FullPathAsFileNameWithSemicolon.Request, Encoding.UTF8))
     {
         var parser = new MultipartFormDataParser(stream, Encoding.UTF8);
         Assert.IsTrue(FullPathAsFileNameWithSemicolon.Validate(parser));
     }
 }
 public void GetParameterValueReturnsNullIfNoParameterFound()
 {
     using (Stream stream = TestUtil.StringToStream(TinyTestCase.Request, Encoding.UTF8))
     {
         var parser = new MultipartFormDataParser(stream, "boundry", Encoding.UTF8);
         Assert.Null(parser.GetParameterValue("does not exist"));
     }
 }
 public void TinyDataTest()
 {
     using (Stream stream = TestUtil.StringToStream(TinyTestCase.Request, Encoding.UTF8))
     {
         var parser = new MultipartFormDataParser(stream, "boundry", Encoding.UTF8);
         Assert.IsTrue(TinyTestCase.Validate(parser));
     }
 }
 public void MultipleFilesAndParamsTest()
 {
     using (Stream stream = TestUtil.StringToStream(MultipleParamsAndFilesTestCase.Request, Encoding.UTF8))
     {
         var parser = new MultipartFormDataParser(stream, "boundry", Encoding.UTF8, 16);
         Assert.IsTrue(MultipleParamsAndFilesTestCase.Validate(parser));
     }
 }
 public void CorrectlyHandlesCRLF()
 {
     string request = TinyTestCase.Request.Replace("\n", "\r\n");
     using (Stream stream = TestUtil.StringToStream(request, Encoding.UTF8))
     {
         var parser = new MultipartFormDataParser(stream, "boundry", Encoding.UTF8);
         Assert.IsTrue(TinyTestCase.Validate(parser));
     }
 }
 public void CanHandleFileAsLastSection()
 {
     using (Stream stream = TestUtil.StringToStream(FileIsLastTestCase.Request, Encoding.UTF8))
     {
         var parser = new MultipartFormDataParser(stream, Encoding.UTF8);
         Assert.IsTrue(FileIsLastTestCase.Validate(parser));
     }
 }
 public void CanHandleFinalDashesInSeperateBufferFromEndBinary()
 {
     using(Stream stream = TestUtil.StringToStream(ExactBufferTruncateTestCase.Request, Encoding.UTF8))
     {
         var parser = new MultipartFormDataParser(stream, "boundry", Encoding.UTF8, 16);
         Assert.IsTrue(ExactBufferTruncateTestCase.Validate(parser));
     }
 }
 public void SingleFileTest()
 {
     using (Stream stream = TestUtil.StringToStream(SingleFileTestCase.Request, Encoding.UTF8))
     {
         var parser = new MultipartFormDataParser(stream, "boundry", Encoding.UTF8, 16);
         Assert.True(SingleFileTestCase.Validate(parser));
     }
 }
        public void HandlesFileWithLastCrLfImmediatlyAfterBufferLength()
        {
            string request =
            @"------WebKitFormBoundaryphElSb1aBJGfLyAP
            Content-Disposition: form-data; name=""fileName""

            Testfile
            ------WebKitFormBoundaryphElSb1aBJGfLyAP
            Content-Disposition: form-data; name=""file""; filename=""Testfile""
            Content-Type: application/pdf

            "
            + new string('\0', 8149)
            + @"
            ------WebKitFormBoundaryphElSb1aBJGfLyAP--
            ";

            using (Stream stream = TestUtil.StringToStream(request, Encoding.UTF8))
            {
                var parser = new MultipartFormDataParser(stream, Encoding.UTF8);
            }
        }
 public void CanAutoDetectBoundary()
 {
     using (Stream stream = TestUtil.StringToStream(TinyTestCase.Request, Encoding.UTF8))
     {
         var parser = new MultipartFormDataParser(stream);
         Assert.IsTrue(TinyTestCase.Validate(parser));
     }
 }
 public void CorrectlyHandleMixedNewlineFormats()
 {
     // Replace the first '\n' with '\r\n'
     var regex = new Regex(Regex.Escape("\n"));
     string request = regex.Replace(TinyTestCase.Request, "\r\n", 1);
     using (Stream stream = TestUtil.StringToStream(request, Encoding.UTF8))
     {
         var parser = new MultipartFormDataParser(stream, "boundry", Encoding.UTF8);
         Assert.IsTrue(TinyTestCase.Validate(parser));
     }
 }
 public void CanDetectBoundariesCrossBuffer()
 {
     using (Stream stream = TestUtil.StringToStream(TinyTestCase.Request, Encoding.UTF8))
     {
         var parser = new MultipartFormDataParser(stream, "boundry", Encoding.UTF8, 16);
         Assert.IsTrue(TinyTestCase.Validate(parser));
     }
 }
 public void DoesntInfiniteLoopOnUnclosedInput()
 {
     using (Stream stream = TestUtil.StringToStream(UnclosedBoundary.Request, Encoding.UTF8))
     {
         // We expect this to throw!
         var parser = new MultipartFormDataParser(stream, Encoding.UTF8);
     }
 }
 public void CanHandleUnicodeWidthAndAsciiWidthCharacters()
 {
     using (
         var stream = TestUtil.StringToStream(MixedUnicodeWidthAndAsciiWidthCharactersTestCase.Request,
                                              Encoding.UTF8))
     {
         var parser = new MultipartFormDataParser(stream, Encoding.UTF8);
         Assert.IsTrue(MixedUnicodeWidthAndAsciiWidthCharactersTestCase.Validate(parser));
     }
 }
 public void AcceptSeveralValuesWithSameProperty()
 {
     using (Stream stream = TestUtil.StringToStream(SeveralValuesWithSameProperty.Request, Encoding.UTF8))
     {
         var parser = new MultipartFormDataParser(stream, Encoding.UTF8);
         Assert.IsTrue(SeveralValuesWithSameProperty.Validate(parser));
     }
 }
        public void CorrectlyHandlesMultilineParameter()
        {
            string request = TestUtil.TrimAllLines(
                @"-----------------------------41952539122868
                Content-Disposition: form-data; name=""multilined""

                line 1
                line 2
                line 3
                -----------------------------41952539122868--");

            using (Stream stream = TestUtil.StringToStream(request, Encoding.UTF8))
            {
                var parser = new MultipartFormDataParser(stream, Encoding.UTF8);
                Assert.AreEqual(parser.Parameters["multilined"].Data, "line 1\r\nline 2\r\nline 3");
            }
        }
 public void CanHandleMixedSingleByteAndMultiByteWidthCharacters()
 {
     using (
         Stream stream = TestUtil.StringToStream(MixedSingleByteAndMultiByteWidthTestCase.Request, Encoding.UTF8)
         )
     {
         var parser = new MultipartFormDataParser(stream, Encoding.UTF8);
         Assert.IsTrue(MixedSingleByteAndMultiByteWidthTestCase.Validate(parser));
     }
 }
        public void SmallDataTest()
        {
            using (Stream stream = TestUtil.StringToStream(SmallTestCase.Request))
            {
                // The boundry is missing the first two -- in accordance with the multipart
                // spec. (A -- is added by the parser, this boundry is what would be sent in the
                // requset header)
                var parser = new MultipartFormDataParser(stream, "---------------------------265001916915724");
                Assert.IsTrue(SmallTestCase.Validate(parser));

            }
        }
        public void DoesNotCloseTheStream()
        {
            using (Stream stream = TestUtil.StringToStream(TinyTestCase.Request, Encoding.UTF8))
            {
                var parser = new MultipartFormDataParser(stream, "boundry", Encoding.UTF8);
                Assert.IsTrue(TinyTestCase.Validate(parser));

                stream.Position = 0;
                Assert.IsTrue(true, "A closed stream would throw ObjectDisposedException");
            }
        }
            /// <summary>
            /// Validates the output of the parser against the expected outputs for 
            /// this test
            /// </summary>
            /// <param name="parser">
            /// The parser to validate.
            /// </param>
            /// <returns>
            /// The <see cref="bool"/> representing if this test passed.
            /// </returns>
            public bool Validate(MultipartFormDataParser parser)
            {
                // Validate parameters
                foreach (var pair in this.ExpectedParams)
                {
                    if (!parser.Parameters.ContainsKey(pair.Key))
                    {
                        return false;
                    }

                    ParameterPart expectedPart = pair.Value;
                    ParameterPart actualPart = parser.Parameters[pair.Key];

                    Console.WriteLine("Expected {0} = {1}, Found {2} = {3}", expectedPart.Name, expectedPart.Data, actualPart.Name, actualPart.Data);

                    if (expectedPart.Name != actualPart.Name || expectedPart.Data != actualPart.Data)
                    {
                        return false;
                    }
                }

                // Validate files
                foreach (var file in parser.Files)
                {
                    bool foundPairMatch = false;
                    foreach (var pair in this.ExpectedFileData)
                    {
                        if (pair.Key == file.Name)
                        {
                            foundPairMatch = true;

                            FilePart expectedFile = pair.Value;
                            FilePart actualFile = file;

                            if (expectedFile.Name != actualFile.Name || expectedFile.FileName != actualFile.FileName)
                            {
                                return false;
                            }

                            if (expectedFile.ContentType != actualFile.ContentType || expectedFile.ContentDisposition != actualFile.ContentDisposition)
                            {
                                return false;
                            }

                            // Read the data from the files and see if it's the same
                            var reader = new StreamReader(expectedFile.Data);
                            string expectedFileData = reader.ReadToEnd();

                            reader = new StreamReader(actualFile.Data);
                            string actualFileData = reader.ReadToEnd();

                            if (expectedFileData != actualFileData)
                            {
                                return false;
                            }

                            break;
                        }
                    }

                    if (!foundPairMatch)
                    {
                        return false;
                    }
                }

                return true;
            }
 public void CanDetectBoundriesWithNewLineInNextBuffer()
 {
     for (int i = 16; i < TinyTestCase.Request.Length; i++)
     {
         using (Stream stream = TestUtil.StringToStream(TinyTestCase.Request, Encoding.UTF8))
         {
             var parser = new MultipartFormDataParser(stream, "boundry", Encoding.UTF8, i);
             Assert.True(TinyTestCase.Validate(parser), $"Failure in buffer length {i}");
         }
     }
 }