/// <summary> /// Binds the model. /// </summary> /// <typeparam name="T">Model type</typeparam> /// <param name="args">The <see cref="ModelBinderEventArgs{T}" /> instance containing the event data.</param> public async Task BindAsync <T>(ModelBinderEventArgs <T> args) { if (!args.Context.Request.ContentType.Contains("multipart/form-data")) { return; } var multipartModelType = typeof(MultipartViewModel); if (typeof(T) != multipartModelType) { throw new ModelBindingException("For HTTP multipart form data model type should be: " + multipartModelType.Name); } var parser = await MultipartFormDataParser.ParseAsync(args.Context.Request.Body); var obj = Activator.CreateInstance <T>(); var model = (MultipartViewModel)(object)obj; model.Files = parser.Files; model.Parameters = parser.Parameters; args.SetModel(obj); }
public async Task DoesntInfiniteLoopOnUnclosedInputAsync() { using (Stream stream = TestUtil.StringToStream(_testCase.Request, Encoding.UTF8)) { // We expect this to throw! await Assert.ThrowsAsync <MultipartParseException>(() => MultipartFormDataParser.ParseAsync(stream, Encoding.UTF8)).ConfigureAwait(false); } }
public async Task HandlesFullPathAsFileNameWithSemicolonCorrectlyAsync() { using (Stream stream = TestUtil.StringToStream(_testCase.Request, Encoding.UTF8)) { var parser = await MultipartFormDataParser.ParseAsync(stream, Encoding.UTF8).ConfigureAwait(false); Assert.True(_testCase.Validate(parser)); } }
/// <summary> /// Parses the inbound email webhook asynchronously. /// </summary> /// <param name="stream">The stream.</param> /// <returns>The <see cref="InboundEmail"/>.</returns> public async Task <InboundEmail> ParseInboundEmailWebhookAsync(Stream stream) { // We need to be able to rewind the stream. // Therefore, we must make a copy of the stream if it doesn't allow changing the position if (!stream.CanSeek) { using (var ms = Utils.MemoryStreamManager.GetStream()) { await stream.CopyToAsync(ms).ConfigureAwait(false); return(await ParseInboundEmailWebhookAsync(ms).ConfigureAwait(false)); } } // It's important to rewind the stream stream.Position = 0; // Asynchronously parse the multipart content received from SendGrid var parser = await MultipartFormDataParser.ParseAsync(stream, Encoding.UTF8).ConfigureAwait(false); // Convert the 'charset' from a string into array of KeyValuePair var charsetsAsJObject = JObject.Parse(parser.GetParameterValue("charsets", "{}")); var charsets = charsetsAsJObject .Properties() .Select(prop => { var key = prop.Name; var value = Encoding.GetEncoding(prop.Value.ToString()); return(new KeyValuePair <string, Encoding>(key, value)); }).ToArray(); // Create a dictionary of parsers, one parser for each desired encoding. // This is necessary because MultipartFormDataParser can only handle one // encoding and SendGrid can use different encodings for parameters such // as "from", "to", "text" and "html". var encodedParsers = charsets .Where(c => c.Value != Encoding.UTF8) .Select(c => c.Value) .Distinct() .Select(async encoding => { stream.Position = 0; // It's important to rewind the stream return(new { Encoding = encoding, Parser = await MultipartFormDataParser.ParseAsync(stream, encoding).ConfigureAwait(false) }); }) .Select(r => r.Result) .Union(new[] { new { Encoding = Encoding.UTF8, Parser = parser } }) .ToDictionary(ep => ep.Encoding, ep => ep.Parser); return(ParseInboundEmail(encodedParsers, charsets)); }
public async Task CanHandleFileAsLastSectionAsync() { using (Stream stream = TestUtil.StringToStream(_testCase.Request, Encoding.UTF8)) { var parser = await MultipartFormDataParser.ParseAsync(stream, Encoding.UTF8).ConfigureAwait(false); Assert.True(_testCase.Validate(parser)); } }
public async Task AcceptSeveralValuesWithSamePropertyAsync() { using (Stream stream = TestUtil.StringToStream(_testCase.Request, Encoding.UTF8)) { var parser = await MultipartFormDataParser.ParseAsync(stream, Encoding.UTF8).ConfigureAwait(false); Assert.True(_testCase.Validate(parser)); } }
public async Task MjpegStreamTest_Async() { using (Stream stream = TestUtil.StringToStream(_testCase.Request, Encoding.UTF8)) { var parser = await MultipartFormDataParser.ParseAsync(stream, "MOBOTIX_Fast_Serverpush", Encoding.UTF8, 32).ConfigureAwait(false); Assert.True(_testCase.Validate(parser)); } }
public async Task CanDetectBoundariesCrossBufferAsync() { using (Stream stream = TestUtil.StringToStream(_testCase.Request, Encoding.UTF8)) { var parser = await MultipartFormDataParser.ParseAsync(stream, "boundry", Encoding.UTF8, 16).ConfigureAwait(false); Assert.True(_testCase.Validate(parser)); } }
public async Task Parse_empty_form_boundary_specified_async() { using (Stream stream = TestUtil.StringToStream(_testCase.Request, Encoding.UTF8)) { var parser = await MultipartFormDataParser.ParseAsync(stream, "----WebKitFormBoundaryb4SfPlH9Bv7c2PKS").ConfigureAwait(false); Assert.True(_testCase.Validate(parser)); } }
public async Task GetParameterValueReturnsNullIfNoParameterFoundAsync() { using (Stream stream = TestUtil.StringToStream(_testCase.Request, Encoding.UTF8)) { var parser = await MultipartFormDataParser.ParseAsync(stream, "boundry", Encoding.UTF8).ConfigureAwait(false); Assert.Null(parser.GetParameterValue("does not exist")); } }
public async Task FileWithAdditionalParameterTest_Async() { using (Stream stream = TestUtil.StringToStream(_testCase.Request, Encoding.UTF8)) { var parser = await MultipartFormDataParser.ParseAsync(stream, Encoding.UTF8).ConfigureAwait(false); Assert.True(_testCase.Validate(parser)); } }
public async Task CanHandleFinalDashesInSeperateBufferFromEndBinaryAsync() { using (Stream stream = TestUtil.StringToStream(_testCase.Request, Encoding.UTF8)) { var parser = await MultipartFormDataParser.ParseAsync(stream, "boundry", Encoding.UTF8, 16).ConfigureAwait(false); Assert.True(_testCase.Validate(parser)); } }
public async Task MultipleFilesAndParamsTestAsync() { using (Stream stream = TestUtil.StringToStream(_testCase.Request, Encoding.UTF8)) { var parser = await MultipartFormDataParser.ParseAsync(stream, "boundry", Encoding.UTF8, 16).ConfigureAwait(false); Assert.True(_testCase.Validate(parser)); } }
public async Task Parse_empty_form_boundary_omitted_async() { using (Stream stream = TestUtil.StringToStream(_testCase.Request, Encoding.UTF8)) { var parser = await MultipartFormDataParser.ParseAsync(stream).ConfigureAwait(false); Assert.True(_testCase.Validate(parser)); } }
public async Task CanHandleUnicodeWidthAndAsciiWidthCharactersAsync() { using ( Stream stream = TestUtil.StringToStream(_testCase.Request, Encoding.UTF8)) { var parser = await MultipartFormDataParser.ParseAsync(stream, Encoding.UTF8).ConfigureAwait(false); Assert.True(_testCase.Validate(parser)); } }
public async Task CorrectlyHandlesCRLFAsync() { string request = _testCase.Request.Replace("\n", "\r\n"); using (Stream stream = TestUtil.StringToStream(request, Encoding.UTF8)) { var parser = await MultipartFormDataParser.ParseAsync(stream, "boundry", Encoding.UTF8).ConfigureAwait(false); Assert.True(_testCase.Validate(parser)); } }
public async Task DoesNotCloseTheStreamAsync() { using (Stream stream = TestUtil.StringToStream(_testCase.Request, Encoding.UTF8)) { var parser = await MultipartFormDataParser.ParseAsync(stream, "boundry", Encoding.UTF8).ConfigureAwait(false); Assert.True(_testCase.Validate(parser)); stream.Position = 0; Assert.True(true, "A closed stream would throw ObjectDisposedException"); } }
public async Task SmallDataTestAsync() { using (Stream stream = TestUtil.StringToStream(_testCase.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 = await MultipartFormDataParser.ParseAsync(stream, "---------------------------265001916915724").ConfigureAwait(false); Assert.True(_testCase.Validate(parser)); } }
public async Task CanDetectBoundriesWithNewLineInNextBufferAsync() { for (int i = 16; i < _testCase.Request.Length; i++) { using (Stream stream = TestUtil.StringToStream(_testCase.Request, Encoding.UTF8)) { var parser = await MultipartFormDataParser.ParseAsync(stream, "boundry", Encoding.UTF8, i).ConfigureAwait(false); Assert.True(_testCase.Validate(parser), $"Failure in buffer length {i}"); } } }
public async Task CorrectlyHandleMixedNewlineFormatsAsync() { // Replace the first '\n' with '\r\n' var regex = new Regex(Regex.Escape("\n")); string request = regex.Replace(_testCase.Request, "\r\n", 1); using (Stream stream = TestUtil.StringToStream(request, Encoding.UTF8)) { var parser = await MultipartFormDataParser.ParseAsync(stream, "boundry", Encoding.UTF8).ConfigureAwait(false); Assert.True(_testCase.Validate(parser)); } }
public async Task <FileResponse> SaveAsync() { var parser = await MultipartFormDataParser.ParseAsync(Request.InputStream); if (parser?.Files is null) { throw HttpException.NotFound("No file uploaded"); } if (parser?.Files.Count != 1) { throw HttpException.BadRequest("Only 1 file can be uploaded"); } return(await _filesHandler.SaveAsync(parser.Files[0].Data, parser.Files[0].FileName)); }
private static async Task <(UploadFileModel model, Stream?file)> ReadBodyAsFormDataAsync(HttpRequestData req) { var result = await MultipartFormDataParser.ParseAsync(req.Body); var model = new UploadFileModel() { Name = result.Parameters.FirstOrDefault(x => x.Name == nameof(UploadFileModel.Name))?.Data !, Size = long.Parse(result.Parameters.FirstOrDefault(x => x.Name == nameof(UploadFileModel.Size))?.Data !), Type = result.Parameters.FirstOrDefault(x => x.Name == nameof(UploadFileModel.Type))?.Data !, LastModified = long.Parse(result.Parameters.FirstOrDefault(x => x.Name == nameof(UploadFileModel.LastModified))?.Data !) }; Validator.ValidateObject(model, new ValidationContext(model)); return(model, result.Files.FirstOrDefault(x => x.Name == "file")?.Data); } }
public async Task CorrectlyHandlesMultilineParameterAsync() { 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 = await MultipartFormDataParser.ParseAsync(stream, Encoding.UTF8).ConfigureAwait(false); Assert.Equal("line 1\r\nline 2\r\nline 3", parser.GetParameterValue("multilined")); Assert.Equal("line 1\r\nline 2\r\nline 3", parser.GetParameterValues("multilined").First()); } }
private static async Task <(UploadFileModel model, Stream?file)> ReadBodyAsFormDataAsync(HttpRequestData req) { var base64Bytes = JsonConvert.DeserializeObject <BytesRequestWrapper>(req.Body).Bytes; using var memoryStream = new MemoryStream(Convert.FromBase64String(base64Bytes)); var result = await MultipartFormDataParser.ParseAsync(memoryStream); var model = new UploadFileModel() { Name = result.Parameters.FirstOrDefault(x => x.Name == nameof(UploadFileModel.Name))?.Data !, Size = long.Parse(result.Parameters.FirstOrDefault(x => x.Name == nameof(UploadFileModel.Size))?.Data !), Type = result.Parameters.FirstOrDefault(x => x.Name == nameof(UploadFileModel.Type))?.Data !, LastModified = long.Parse(result.Parameters.FirstOrDefault(x => x.Name == nameof(UploadFileModel.LastModified))?.Data !) }; Validator.ValidateObject(model, new ValidationContext(model)); return(model, result.Files.FirstOrDefault(x => x.Name == "file")?.Data); } }
public async Task HandlesFileWithLastCrLfImmediatlyAfterBufferLengthAsync() { 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 = await MultipartFormDataParser.ParseAsync(stream, Encoding.UTF8).ConfigureAwait(false); } }
/// <summary> /// A frends task form parsing multipart/form-data requests. /// Documentation: https://github.com/CommunityHiQ/Frends.Community.Multipart /// </summary> /// <param name="input">What to repeat.</param> /// <param name="cancellationToken"></param> /// <returns>Object { List<{ string Name, string Value }> Parameters, List<{ string Name, byte[] Contents }> Files }</returns> public static async Task <Result> ParseMultipartRequest(Input input, CancellationToken cancellationToken) { var ret = new List <File>(); var ret2 = new List <Parameter>(); using (var stream = new MemoryStream(input.ByteArray)) { var parser = await MultipartFormDataParser.ParseAsync(stream).ConfigureAwait(false); foreach (var file in parser.Files) { cancellationToken.ThrowIfCancellationRequested(); var tmp = new File { Name = file.FileName, Contents = ((MemoryStream)file.Data).ToArray() }; ret.Add(tmp); } foreach (var param in parser.Parameters) { cancellationToken.ThrowIfCancellationRequested(); var retParam = new Parameter { Name = param.Name, Value = param.Data }; ret2.Add(retParam); } } var output = new Result { Files = ret, Parameters = ret2 }; return(output); }
public async Task <Stream> Download(string fileName, CancellationToken cancellationToken) { await RefreshAccessTokenIfRequired(cancellationToken).ConfigureAwait(false); // Check and search file var fileList = await GetGoogleDriveFileList(cancellationToken).ConfigureAwait(false); var targetFile = fileList.Files.Find(f => f.Name == fileName); if (targetFile is null) { return(null); } var client = _httpClientFactory.CreateClient(); using var getFileStreamRM = GetAuthHttpRequestMessage(); getFileStreamRM.Method = HttpMethod.Get; getFileStreamRM.RequestUri = new System.Uri($"https://www.googleapis.com/drive/v3/files/{targetFile.Id}?alt=media"); var fileResponse = await client.SendAsync(getFileStreamRM, cancellationToken).ConfigureAwait(false); var fileContent = fileResponse.Content; using var fullStream = await fileContent.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); var multipart = await MultipartFormDataParser.ParseAsync(fullStream).ConfigureAwait(false); foreach (var file in multipart.Files) { if (file.ContentType == MediaTypeNames.Application.Octet) { return(file.Data); } } return(null); }
public async Task HandlesFileWithoutFilename() { string request = @"------WebKitFormBoundaryphElSb1aBJGfLyAP Content-Disposition: form-data; name=""fileName"" Testfile ------WebKitFormBoundaryphElSb1aBJGfLyAP Content-Disposition: form-data; name=""file"" Content-Type: application/octet-stream " + new string('\0', 8147) + @" ------WebKitFormBoundaryphElSb1aBJGfLyAP-- "; using (Stream stream = TestUtil.StringToStream(request, Encoding.UTF8)) { var parser = await MultipartFormDataParser.ParseAsync(stream, Encoding.UTF8).ConfigureAwait(false); Assert.Single(parser.Files); } }
public async Task ConstructingWithNullStreamFailsAsync() { await Assert.ThrowsAsync <ArgumentNullException>(() => MultipartFormDataParser.ParseAsync(Stream.Null)).ConfigureAwait(false); }
/// <summary> /// Parses the inbound email webhook asynchronously. /// </summary> /// <param name="stream">The stream.</param> /// <returns>The <see cref="InboundEmail"/>.</returns> public async Task <InboundEmail> ParseInboundEmailWebhookAsync(Stream stream) { // We need to be able to rewind the stream. // Therefore, we must make a copy of the stream if it doesn't allow changing the position if (!stream.CanSeek) { using (var ms = Utils.MemoryStreamManager.GetStream()) { await stream.CopyToAsync(ms).ConfigureAwait(false); return(await ParseInboundEmailWebhookAsync(ms).ConfigureAwait(false)); } } // It's important to rewind the stream stream.Position = 0; // Asynchronously parse the multipart content received from SendGrid var parser = await MultipartFormDataParser.ParseAsync(stream, Encoding.UTF8).ConfigureAwait(false); // Convert the 'charset' from a string into array of KeyValuePair var charsetsAsJObject = JObject.Parse(parser.GetParameterValue("charsets", "{}")); var charsets = charsetsAsJObject .Properties() .Select(prop => { var key = prop.Name; var encodingName = prop.Value.ToString(); try { var encoding = Encoding.GetEncoding(encodingName); return(new KeyValuePair <string, Encoding>(key, encoding)); } catch (ArgumentException) { // ArgumentException is thrown when an "unusual" code page was used to encode a section of the email // For example: {"to":"UTF-8","subject":"UTF-8","from":"UTF-8","text":"iso-8859-10"} // We can see that 'iso-8859-10' was used to encode the "Text" but this encoding is not supported in // .net (neither dotnet full nor dotnet core). Therefore we fallback on UTF-8. This is obviously not // perfect because UTF-8 may or may not be able to handle all the encoded characters, but it's better // than simply erroring out. // See https://github.com/Jericho/StrongGrid/issues/341 for discussion. return(new KeyValuePair <string, Encoding>(key, Encoding.UTF8)); } }).ToArray(); // Create a dictionary of parsers, one parser for each desired encoding. // This is necessary because MultipartFormDataParser can only handle one // encoding and SendGrid can use different encodings for parameters such // as "from", "to", "text" and "html". var encodedParsers = charsets .Where(c => !c.Value.Equals(Encoding.UTF8)) .Select(c => c.Value) .Distinct() .Select(async encoding => { stream.Position = 0; // It's important to rewind the stream return(new { Encoding = encoding, Parser = await MultipartFormDataParser.ParseAsync(stream, encoding).ConfigureAwait(false) }); }) .Select(r => r.Result) .Union(new[] { new { Encoding = Encoding.UTF8, Parser = parser } }) .ToDictionary(ep => ep.Encoding, ep => ep.Parser); return(ParseInboundEmail(encodedParsers, charsets)); }