public static Task<FormData> ParseMultipart(Stream stream, string boundary) { if (stream == null) { throw new ArgumentNullException("stream"); } if (boundary == null) { throw new ArgumentNullException("boundary"); } if (boundary.Length == 0) { throw new ArgumentException("Boundary cannot be empty", "boundary"); } var form = new FormData(); boundary = "--" + boundary; var boundaryBytes = Encoding.UTF8.GetBytes("\r\n" + boundary); string currentLine = stream.ReadLine(Encoding.UTF8); if (currentLine != boundary) { form.IsValid = false; return TaskHelper.Completed(form); } while (true) { currentLine = stream.ReadLine(Encoding.UTF8); // parse ContentDisposition line var match = ContentDispositionFormDataRegex.Match(currentLine); if (!match.Success) { form.IsValid = false; return TaskHelper.Completed(form); } string fieldName = match.Groups[1].Value; string fileName = match.Groups[2].Success ? match.Groups[3].Value : null; if (fileName != null) { if (!ParseMultipartFile(stream, form, fieldName, fileName, boundaryBytes)) { form.IsValid = false; return TaskHelper.Completed(form); } } else { if (!ParseMultipartField(stream, form, fieldName, boundaryBytes)) { form.IsValid = false; return TaskHelper.Completed(form); } } // check end or next currentLine = stream.ReadLine(Encoding.UTF8); // --boundary-- end if (currentLine == "--") { break; } // --boundary between if (currentLine != string.Empty) { form.IsValid = false; return TaskHelper.Completed(form); } } return TaskHelper.Completed(form); }
public static Task <FormData> ParseMultipart(Stream stream, string boundary) { if (stream == null) { throw new ArgumentNullException("stream"); } if (boundary == null) { throw new ArgumentNullException("boundary"); } if (boundary.Length == 0) { throw new ArgumentException("Boundary cannot be empty", "boundary"); } var form = new FormData(); boundary = "--" + boundary; var boundaryBytes = Encoding.UTF8.GetBytes("\r\n" + boundary); string currentLine = stream.ReadLine(Encoding.UTF8); if (currentLine != boundary) { form.IsValid = false; return(TaskHelper.Completed(form)); } while (true) { currentLine = stream.ReadLine(Encoding.UTF8); // parse ContentDisposition line var match = ContentDispositionFormDataRegex.Match(currentLine); if (!match.Success) { form.IsValid = false; return(TaskHelper.Completed(form)); } string fieldName = match.Groups[1].Value; string fileName = match.Groups[2].Success ? match.Groups[3].Value : null; if (fileName != null) { if (!ParseMultipartFile(stream, form, fieldName, fileName, boundaryBytes)) { form.IsValid = false; return(TaskHelper.Completed(form)); } } else { if (!ParseMultipartField(stream, form, fieldName, boundaryBytes)) { form.IsValid = false; return(TaskHelper.Completed(form)); } } // check end or next currentLine = stream.ReadLine(Encoding.UTF8); // --boundary-- end if (currentLine == "--") { break; } // --boundary between if (currentLine != string.Empty) { form.IsValid = false; return(TaskHelper.Completed(form)); } } return(TaskHelper.Completed(form)); }
private static bool ParseMultipartField(Stream stream, FormData form, string fieldName, byte[] boundaryBytes) { string contentType = null; string headerLine; Match match; while ((headerLine = stream.ReadLine(Encoding.UTF8)) != string.Empty) { // parse 'Content-" headers match = ContentTypeFormDataRegex.Match(headerLine); if (match.Success) { // nested: Content-Type: multipart/mixed; boundary=BbC04y contentType = match.Groups[1].Value.Trim(); if (match.Groups[2].Success) { string fileBoundary = match.Groups[4].Value; byte[] fileBoundaryBytes = Encoding.UTF8.GetBytes("\r\n--" + fileBoundary); byte[] temp; if (!stream.ReadTo(fileBoundaryBytes, out temp)) { return(false); } if (stream.ReadLine(Encoding.UTF8) != string.Empty) { return(false); } bool moreFiles = true; while (moreFiles) { string line = stream.ReadLine(Encoding.UTF8); match = ContentDispositionFileRegex.Match(line); if (!match.Success) { return(false); } string filename = match.Groups[1].Value; if (!ParseMultipartFile(stream, form, fieldName, filename, fileBoundaryBytes)) { return(false); } line = stream.ReadLine(Encoding.UTF8); if (line == "--") { moreFiles = false; } else if (line != string.Empty) { return(false); } } // NB: CrLf already ripped here var boundaryNoCrLf = new byte[boundaryBytes.Length - 2]; Array.Copy(boundaryBytes, 2, boundaryNoCrLf, 0, boundaryBytes.Length - 2); if (!stream.ReadTo(boundaryNoCrLf, out temp)) { return(false); } if (temp.Length != 0) { return(false); } return(true); } } } if (contentType == null) { contentType = "text/plain"; } byte[] value; if (!stream.ReadTo(boundaryBytes, out value)) { return(false); } // handle charset: content-type: text/plain;charset=windows-1250 match = CharsetRegex.Match(contentType); Encoding encoding = match.Success ? Encoding.GetEncoding(match.Groups[2].Value) : Encoding.UTF8; form[fieldName] = encoding.GetString(value); return(true); }
private static bool ParseMultipartFile(Stream stream, FormData form, string fieldName, string fileName, byte[] boundaryBytes) { string contentType = null; string headerLine; while ((headerLine = stream.ReadLine(Encoding.UTF8)) != string.Empty) { // parse 'Content-" headers var match = ContentTypeFileRegex.Match(headerLine); if (match.Success) { contentType = match.Groups[1].Value.Trim(); } } if (contentType == null) { //todo: infer from file type (extension) contentType = "application/octet-stream"; } byte[] data; if (!stream.ReadTo(boundaryBytes, out data)) { return false; } form.Files.Add(new PostedFile(fieldName, fileName, data, contentType)); return true; }
private static bool ParseMultipartField(Stream stream, FormData form, string fieldName, byte[] boundaryBytes) { string contentType = null; string headerLine; Match match; while ((headerLine = stream.ReadLine(Encoding.UTF8)) != string.Empty) { // parse 'Content-" headers match = ContentTypeFormDataRegex.Match(headerLine); if (match.Success) { // nested: Content-Type: multipart/mixed; boundary=BbC04y contentType = match.Groups[1].Value.Trim(); if (match.Groups[2].Success) { string fileBoundary = match.Groups[4].Value; byte[] fileBoundaryBytes = Encoding.UTF8.GetBytes("\r\n--" + fileBoundary); byte[] temp; if (!stream.ReadTo(fileBoundaryBytes, out temp)) { return false; } if (stream.ReadLine(Encoding.UTF8) != string.Empty) { return false; } bool moreFiles = true; while (moreFiles) { string line = stream.ReadLine(Encoding.UTF8); match = ContentDispositionFileRegex.Match(line); if (!match.Success) { return false; } string filename = match.Groups[1].Value; if (!ParseMultipartFile(stream, form, fieldName, filename, fileBoundaryBytes)) { return false; } line = stream.ReadLine(Encoding.UTF8); if (line == "--") { moreFiles = false; } else if (line != string.Empty) { return false; } } // NB: CrLf already ripped here var boundaryNoCrLf = new byte[boundaryBytes.Length - 2]; Array.Copy(boundaryBytes, 2, boundaryNoCrLf, 0, boundaryBytes.Length - 2); if (!stream.ReadTo(boundaryNoCrLf, out temp)) { return false; } if (temp.Length != 0) { return false; } return true; } } } if (contentType == null) { contentType = "text/plain"; } byte[] value; if (!stream.ReadTo(boundaryBytes, out value)) { return false; } // handle charset: content-type: text/plain;charset=windows-1250 match = CharsetRegex.Match(contentType); Encoding encoding = match.Success ? Encoding.GetEncoding(match.Groups[2].Value) : Encoding.UTF8; form[fieldName] = encoding.GetString(value); return true; }
/// <summary> /// Builds a FormData from the specified input stream, /// which is assumed to be in x-www-form-urlencoded format. /// </summary> /// <param name="stream">the input stream</param> /// <returns>a populated FormData object</returns> public static Task<FormData> ParseUrlEncoded(Stream stream) { var form = new FormData(); string input = stream.ReadAll(); var pairs = input.Split(UrlSplitTokens, StringSplitOptions.RemoveEmptyEntries); foreach (var pair in pairs) { var nameValue = pair.Split('='); form[nameValue[0]] = UrlHelper.Decode(nameValue[1]); } return TaskHelper.Completed(form); }
public void HandleMultipartData() { string httpContent = @"--AaB03x Content-Disposition: form-data; name=""SingleFile""; filename=""single.txt"" Content-Type: text/plain Just a text file. --AaB03x Content-Disposition: form-data; name=""Test"" Pass --AaB03x Content-Disposition: form-data; name=""MultipleFiles"" Content-Type: multipart/mixed; boundary=BbC04y --BbC04y Content-Disposition: file; filename=""file1.txt"" Content-Type: text/plain ...contents of file1.txt... --BbC04y Content-Disposition: file; filename=""file2.gif"" Content-Type: image/gif Content-Transfer-Encoding: binary ...contents of file2.gif... --BbC04y-- --AaB03x--"; var host = new TestHostAndServer(FormsMiddleware.ParseFormData, Pipeline.ReturnDone); var request = TestRequest.Post("/") .WithContentType(FormData.GetMultipartContentType("AaB03x")) .WithContent(httpContent); IContext context = host.Process(request); var form = context.Request.FormData; form.IsValid.ShouldBeTrue(); form.Files.Count.ShouldEqual(3); var file = form.Files[0]; file.FieldName.ShouldEqual("SingleFile"); file.FileName.ShouldEqual("single.txt"); file.ContentType.ShouldEqual("text/plain"); file.ContentLength.ShouldEqual(17); file.InputStream.ReadAll() .ShouldEqual("Just a text file."); file = form.Files[1]; file.FieldName.ShouldEqual("MultipleFiles"); file.FileName.ShouldEqual("file1.txt"); file.ContentType.ShouldEqual("text/plain"); file.ContentLength.ShouldEqual(27); file.InputStream.ReadAll() .ShouldEqual("...contents of file1.txt..."); file = form.Files[2]; file.FieldName.ShouldEqual("MultipleFiles"); file.FileName.ShouldEqual("file2.gif"); file.ContentType.ShouldEqual("image/gif"); file.ContentLength.ShouldEqual(27); file.InputStream.ReadAll() .ShouldEqual("...contents of file2.gif..."); form.Values.Count.ShouldEqual(1); form["Test"].ShouldEqual("Pass"); }