();
+ foreach (var file in collection.Files)
+ {
+ string result = string.Empty;
+ if (file.Length > 0)
+ {
+ using (var reader = new StreamReader(file.OpenReadStream()))
+ {
+ result = reader.ReadToEnd();
+ }
+ }
+ Hashtable fileHash = new Hashtable
+ {
+ {"ContentDisposition" , file.ContentDisposition},
+ {"ContentType" , file.ContentType},
+ {"FileName" , file.FileName},
+ {"Length" , file.Length},
+ {"Name" , file.Name},
+ {"Content" , result},
+ {"Headers" , file.Headers}
+ };
+ fileList.Add(fileHash);
+ }
+ Hashtable itemsHash = new Hashtable();
+ foreach (var key in collection.Keys)
+ {
+ itemsHash.Add(key,collection[key]);
+ }
+ MediaTypeHeaderValue mediaContentType = MediaTypeHeaderValue.Parse(Request.ContentType);
+ Hashtable headers = new Hashtable();
+ foreach (var key in Request.Headers.Keys)
+ {
+ headers.Add(key, String.Join(Constants.HeaderSeparator, Request.Headers[key]));
+ }
+ Hashtable output = new Hashtable
+ {
+ {"Files" , fileList},
+ {"Items" , itemsHash},
+ {"Boundary", HeaderUtilities.RemoveQuotes(mediaContentType.Boundary).Value},
+ {"Headers" , headers}
+ };
+ return Json(output);
+ }
+
+ public IActionResult Error()
+ {
+ return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
+ }
+ }
+}
diff --git a/test/tools/WebListener/README.md b/test/tools/WebListener/README.md
index 9db5f8dc07..edf9bc1872 100644
--- a/test/tools/WebListener/README.md
+++ b/test/tools/WebListener/README.md
@@ -82,3 +82,47 @@ Invoke-WebRequest -Uri 'http://localhost:8083/Get/' -Body @{TestField = 'TestVal
"origin": "127.0.0.1"
}
```
+
+## /Multipart/
+
+### GET
+Provides an HTML form for `multipart/form-data` submission.
+
+### POST
+Accepts a `multipart/form-data` submission and returns a JSON object containing information about the submission including the items and files submitted.
+
+```json
+{
+ "Files": [
+ {
+ "ContentDisposition": "form-data; name=fileData; filename=test.txt",
+ "Headers": {
+ "Content-Disposition": [
+ "form-data; name=fileData; filename=test.txt"
+ ],
+ "Content-Type": [
+ "text/plain"
+ ]
+ },
+ "FileName": "test.txt",
+ "Length": 15,
+ "ContentType": "text/plain",
+ "Name": "fileData",
+ "Content": "Test Contents\r\n"
+ }
+ ],
+ "Items": {
+ "stringData": [
+ "TestValue"
+ ]
+ },
+ "Boundary": "83027bde-fd9b-4ea0-b1ca-a1f661d01ada",
+ "Headers": {
+ "Content-Type": "multipart/form-data; boundary=\"83027bde-fd9b-4ea0-b1ca-a1f661d01ada\"",
+ "Connection": "Keep-Alive",
+ "Content-Length": "336",
+ "Host": "localhost:8083",
+ "User-Agent": "Mozilla/5.0 (Windows NT; Microsoft Windows 10.0.15063 ; en-US) WindowsPowerShell/6.0.0"
+ }
+}
+```
diff --git a/test/tools/WebListener/Views/Home/Index.cshtml b/test/tools/WebListener/Views/Home/Index.cshtml
index 667c251276..3a13c61471 100644
--- a/test/tools/WebListener/Views/Home/Index.cshtml
+++ b/test/tools/WebListener/Views/Home/Index.cshtml
@@ -3,4 +3,5 @@
/ - This page
/Cert/ - Client Certificate Details
/Get/ - Emulates functionality of https://httpbin.org/get by returning GET headers, Arguments, and Request URL
+ /Multipart/ - Multipart/form-data submission testing
diff --git a/test/tools/WebListener/Views/Multipart/Index.cshtml b/test/tools/WebListener/Views/Multipart/Index.cshtml
new file mode 100644
index 0000000000..01c6f69016
--- /dev/null
+++ b/test/tools/WebListener/Views/Multipart/Index.cshtml
@@ -0,0 +1,13 @@
+