public async Task ResetPipeWorks() { // Same test that is in the benchmark var pipe = new Pipe(); var bytes = Encoding.UTF8.GetBytes("foo=bar&baz=boo"); for (var i = 0; i < 1000; i++) { pipe.Writer.Write(bytes); pipe.Writer.Complete(); var formReader = new FormPipeReader(pipe.Reader); await formReader.ReadFormAsync(); pipe.Reader.Complete(); pipe.Reset(); } }
public void TryParseFormValues_DecodedPlusesWorks(Encoding encoding) { var readOnlySequence = ReadOnlySequenceFactory.SingleSegmentFactory.CreateWithContent(encoding.GetBytes("++%2B=+++%2B&++++=++++&+=")); KeyValueAccumulator accumulator = default; var formReader = new FormPipeReader(null, encoding); formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: true); Assert.Equal(3, accumulator.KeyCount); var dict = accumulator.GetResults(); Assert.Equal(" ", dict[" "]); Assert.Equal(" +", dict[" +"]); Assert.Equal("", dict[" "]); }
public void TryParseFormValues_MultiSegmentSplitAcrossSegmentsWorks(Encoding encoding) { var readOnlySequence = ReadOnlySequenceFactory.SingleSegmentFactory.CreateWithContent(encoding.GetBytes("foo=bar&baz=boo&t=")); KeyValueAccumulator accumulator = default; var formReader = new FormPipeReader(null, encoding); formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: true); Assert.Equal(3, accumulator.KeyCount); var dict = accumulator.GetResults(); Assert.Equal("bar", dict["foo"]); Assert.Equal("boo", dict["baz"]); Assert.Equal("", dict["t"]); }
internal virtual Task <Dictionary <string, StringValues> > ReadFormAsync(FormPipeReader reader) { return(reader.ReadFormAsync()); }
private async Task <IFormCollection> InnerReadFormAsync(CancellationToken cancellationToken) { if (!HasFormContentType) { throw new InvalidOperationException("Incorrect Content-Type: " + _request.ContentType); } cancellationToken.ThrowIfCancellationRequested(); if (_request.ContentLength == 0) { return(FormCollection.Empty); } if (_options.BufferBody) { _request.EnableRewind(_options.MemoryBufferThreshold, _options.BufferBodyLengthLimit); } FormCollection formFields = null; FormFileCollection files = null; // Some of these code paths use StreamReader which does not support cancellation tokens. using (cancellationToken.Register((state) => ((HttpContext)state).Abort(), _request.HttpContext)) { var contentType = ContentType; // Check the content-type if (HasApplicationFormContentType(contentType)) { var encoding = FilterEncoding(contentType.Encoding); var formReader = new FormPipeReader(_request.BodyReader, encoding) { ValueCountLimit = _options.ValueCountLimit, KeyLengthLimit = _options.KeyLengthLimit, ValueLengthLimit = _options.ValueLengthLimit, }; formFields = new FormCollection(await formReader.ReadFormAsync(cancellationToken)); } else if (HasMultipartFormContentType(contentType)) { var formAccumulator = new KeyValueAccumulator(); var boundary = GetBoundary(contentType, _options.MultipartBoundaryLengthLimit); var multipartReader = new MultipartReader(boundary, _request.Body) { HeadersCountLimit = _options.MultipartHeadersCountLimit, HeadersLengthLimit = _options.MultipartHeadersLengthLimit, BodyLengthLimit = _options.MultipartBodyLengthLimit, }; var section = await multipartReader.ReadNextSectionAsync(cancellationToken); while (section != null) { // Parse the content disposition here and pass it further to avoid reparsings if (!ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition)) { throw new InvalidDataException("Form section has invalid Content-Disposition value: " + section.ContentDisposition); } if (contentDisposition.IsFileDisposition()) { var fileSection = new FileMultipartSection(section, contentDisposition); // Enable buffering for the file if not already done for the full body section.EnableRewind( _request.HttpContext.Response.RegisterForDispose, _options.MemoryBufferThreshold, _options.MultipartBodyLengthLimit); // Find the end await section.Body.DrainAsync(cancellationToken); var name = fileSection.Name; var fileName = fileSection.FileName; FormFile file; if (section.BaseStreamOffset.HasValue) { // Relative reference to buffered request body file = new FormFile(_request.Body, section.BaseStreamOffset.GetValueOrDefault(), section.Body.Length, name, fileName); } else { // Individually buffered file body file = new FormFile(section.Body, 0, section.Body.Length, name, fileName); } file.Headers = new HeaderDictionary(section.Headers); if (files == null) { files = new FormFileCollection(); } if (files.Count >= _options.ValueCountLimit) { throw new InvalidDataException($"Form value count limit {_options.ValueCountLimit} exceeded."); } files.Add(file); } else if (contentDisposition.IsFormDisposition()) { var formDataSection = new FormMultipartSection(section, contentDisposition); // Content-Disposition: form-data; name="key" // // value // Do not limit the key name length here because the multipart headers length limit is already in effect. var key = formDataSection.Name; var value = await formDataSection.GetValueAsync(); formAccumulator.Append(key, value); if (formAccumulator.ValueCount > _options.ValueCountLimit) { throw new InvalidDataException($"Form value count limit {_options.ValueCountLimit} exceeded."); } } else { System.Diagnostics.Debug.Assert(false, "Unrecognized content-disposition for this section: " + section.ContentDisposition); } section = await multipartReader.ReadNextSectionAsync(cancellationToken); } if (formAccumulator.HasValues) { formFields = new FormCollection(formAccumulator.GetResults(), files); } } } // Rewind so later readers don't have to. if (_request.Body.CanSeek) { _request.Body.Seek(0, SeekOrigin.Begin); } if (formFields != null) { Form = formFields; } else if (files != null) { Form = new FormCollection(null, files); } else { Form = FormCollection.Empty; } return(Form); }
public void Setup() { _formPipeReader = new FormPipeReader(null); }