summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs30
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/MultiPartPostPartTests.cs99
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs9
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingUtilities.cs14
-rw-r--r--src/DotNetOpenAuth/Messaging/MultipartPostPart.cs10
5 files changed, 57 insertions, 105 deletions
diff --git a/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs b/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs
index 26ce4cd..f445f01 100644
--- a/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs
+++ b/src/DotNetOpenAuth.Test/Messaging/MessagingUtilitiesTests.cs
@@ -11,8 +11,10 @@ namespace DotNetOpenAuth.Test.Messaging
using System.Collections.Specialized;
using System.IO;
using System.Net;
+ using System.Text.RegularExpressions;
using System.Web;
using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Test.Mocks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
@@ -139,5 +141,33 @@ namespace DotNetOpenAuth.Test.Messaging
Assert.AreEqual("%C2%80", MessagingUtilities.EscapeUriDataStringRfc3986("\u0080"));
Assert.AreEqual("%E3%80%81", MessagingUtilities.EscapeUriDataStringRfc3986("\u3001"));
}
+
+ /// <summary>
+ /// Verifies the overall format of the multipart POST is correct.
+ /// </summary>
+ [TestMethod]
+ public void PostMultipart() {
+ var httpHandler = new TestWebRequestHandler();
+ bool callbackTriggered = false;
+ httpHandler.Callback = req => {
+ Match m = Regex.Match(req.ContentType, "multipart/form-data; boundary=(.+)");
+ Assert.IsTrue(m.Success, "Content-Type HTTP header not set correctly.");
+ string boundary = m.Groups[1].Value;
+ string expectedEntity = "--{0}\r\nContent-Disposition: form-data; name=\"a\"\r\n\r\nb\r\n--{0}--\r\n";
+ expectedEntity = string.Format(expectedEntity, boundary);
+ string actualEntity = httpHandler.RequestEntityAsString;
+ Assert.AreEqual(expectedEntity, actualEntity);
+ callbackTriggered = true;
+ Assert.AreEqual(req.ContentLength, actualEntity.Length);
+ IncomingWebResponse resp = new CachedDirectWebResponse();
+ return resp;
+ };
+ var request = (HttpWebRequest)WebRequest.Create("http://someserver");
+ var parts = new[] {
+ MultipartPostPart.CreateFormPart("a", "b"),
+ };
+ request.PostMultipart(httpHandler, parts);
+ Assert.IsTrue(callbackTriggered);
+ }
}
}
diff --git a/src/DotNetOpenAuth.Test/Messaging/MultiPartPostPartTests.cs b/src/DotNetOpenAuth.Test/Messaging/MultiPartPostPartTests.cs
deleted file mode 100644
index f87ae59..0000000
--- a/src/DotNetOpenAuth.Test/Messaging/MultiPartPostPartTests.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="MultipartPostPartTests.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.Messaging {
- using System.CodeDom.Compiler;
- using System.Collections.Generic;
- using System.Diagnostics.Contracts;
- using System.IO;
- using System.Net;
- using DotNetOpenAuth.Messaging;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
-
- [TestClass]
- public class MultipartPostPartTests : TestBase {
- /// <summary>
- /// Verifies that the Length property matches the length actually serialized.
- /// </summary>
- [TestMethod]
- public void FormDataSerializeMatchesLength() {
- var part = MultipartPostPart.CreateFormPart("a", "b");
- VerifyLength(part);
- }
-
- /// <summary>
- /// Verifies that the length property matches the length actually serialized.
- /// </summary>
- [TestMethod]
- public void FileSerializeMatchesLength() {
- using (TempFileCollection tfc = new TempFileCollection()) {
- string file = tfc.AddExtension(".txt");
- File.WriteAllText(file, "sometext");
- var part = MultipartPostPart.CreateFormFilePart("someformname", file, "text/plain");
- VerifyLength(part);
- }
- }
-
- /// <summary>
- /// Verifies MultiPartPost sends the right number of bytes.
- /// </summary>
- [TestMethod]
- public void MultiPartPostAscii() {
- using (TempFileCollection tfc = new TempFileCollection()) {
- string file = tfc.AddExtension("txt");
- File.WriteAllText(file, "sometext");
- this.VerifyFullPost(new List<MultipartPostPart> {
- MultipartPostPart.CreateFormPart("a", "b"),
- MultipartPostPart.CreateFormFilePart("SomeFormField", file, "text/plain"),
- });
- }
- }
-
- /// <summary>
- /// Verifies MultiPartPost sends the right number of bytes.
- /// </summary>
- [TestMethod]
- public void MultiPartPostMultiByteCharacters() {
- using (TempFileCollection tfc = new TempFileCollection()) {
- string file = tfc.AddExtension("txt");
- File.WriteAllText(file, "\x1020\x818");
- this.VerifyFullPost(new List<MultipartPostPart> {
- MultipartPostPart.CreateFormPart("a", "\x987"),
- MultipartPostPart.CreateFormFilePart("SomeFormField", file, "text/plain"),
- });
- }
- }
-
- private static void VerifyLength(MultipartPostPart part) {
- Contract.Requires(part != null);
-
- var expectedLength = part.Length;
- var ms = new MemoryStream();
- var sw = new StreamWriter(ms);
- part.Serialize(sw);
- sw.Flush();
- var actualLength = ms.Length;
- Assert.AreEqual(expectedLength, actualLength);
- }
-
- private void VerifyFullPost(List<MultipartPostPart> parts) {
- var request = (HttpWebRequest)WebRequest.Create("http://localhost");
- var handler = new Mocks.TestWebRequestHandler();
- bool posted = false;
- handler.Callback = req => {
- foreach (string header in req.Headers) {
- TestContext.WriteLine("{0}: {1}", header, req.Headers[header]);
- }
- TestContext.WriteLine(handler.RequestEntityAsString);
- Assert.AreEqual(req.ContentLength, handler.RequestEntityStream.Length);
- posted = true;
- return null;
- };
- request.PostMultipart(handler, parts);
- Assert.IsTrue(posted, "HTTP POST never sent.");
- }
- }
-}
diff --git a/src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs b/src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs
index f87ae59..57614ba 100644
--- a/src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs
+++ b/src/DotNetOpenAuth.Test/Messaging/MultipartPostPartTests.cs
@@ -38,6 +38,15 @@ namespace DotNetOpenAuth.Test.Messaging {
}
/// <summary>
+ /// Verifies file multiparts identify themselves as files and not merely form-data.
+ /// </summary>
+ [TestMethod]
+ public void FilePartAsFile() {
+ var part = MultipartPostPart.CreateFormFilePart("somename", "somefile", "plain/text", new MemoryStream());
+ Assert.AreEqual("file", part.ContentDisposition);
+ }
+
+ /// <summary>
/// Verifies MultiPartPost sends the right number of bytes.
/// </summary>
[TestMethod]
diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
index e545f8d..1c50c77 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
+++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
@@ -141,7 +141,7 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
- /// Sends an multipart HTTP POST request (useful for posting files).
+ /// Sends a multipart HTTP POST request (useful for posting files).
/// </summary>
/// <param name="request">The HTTP request.</param>
/// <param name="requestHandler">The request handler.</param>
@@ -193,13 +193,19 @@ namespace DotNetOpenAuth.Messaging {
Contract.Requires<ArgumentNullException>(parts != null);
Reporting.RecordFeatureUse("MessagingUtilities.PostMultipart");
+ parts = parts.CacheGeneratedResults();
string boundary = Guid.NewGuid().ToString();
+ string initialPartLeadingBoundary = string.Format(CultureInfo.InvariantCulture, "--{0}\r\n", boundary);
string partLeadingBoundary = string.Format(CultureInfo.InvariantCulture, "\r\n--{0}\r\n", boundary);
string finalTrailingBoundary = string.Format(CultureInfo.InvariantCulture, "\r\n--{0}--\r\n", boundary);
request.Method = "POST";
request.ContentType = "multipart/form-data; boundary=" + boundary;
- request.ContentLength = parts.Sum(p => partLeadingBoundary.Length + p.Length) + finalTrailingBoundary.Length;
+ long contentLength = parts.Sum(p => partLeadingBoundary.Length + p.Length) + finalTrailingBoundary.Length;
+ if (parts.Any()) {
+ contentLength -= 2; // the initial part leading boundary has no leading \r\n
+ }
+ request.ContentLength = contentLength;
// Setting the content-encoding to "utf-8" causes Google to reply
// with a 415 UnsupportedMediaType. But adding it doesn't buy us
@@ -209,8 +215,10 @@ namespace DotNetOpenAuth.Messaging {
var requestStream = requestHandler.GetRequestStream(request);
try {
StreamWriter writer = new StreamWriter(requestStream, Channel.PostEntityEncoding);
+ bool firstPart = true;
foreach (var part in parts) {
- writer.Write(partLeadingBoundary);
+ writer.Write(firstPart ? initialPartLeadingBoundary : partLeadingBoundary);
+ firstPart = false;
part.Serialize(writer);
part.Dispose();
}
diff --git a/src/DotNetOpenAuth/Messaging/MultipartPostPart.cs b/src/DotNetOpenAuth/Messaging/MultipartPostPart.cs
index 7ef89a4..f9a5988 100644
--- a/src/DotNetOpenAuth/Messaging/MultipartPostPart.cs
+++ b/src/DotNetOpenAuth/Messaging/MultipartPostPart.cs
@@ -116,6 +116,10 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="contentType">Type of the content in HTTP Content-Type format.</param>
/// <returns>The constructed part.</returns>
public static MultipartPostPart CreateFormFilePart(string name, string filePath, string contentType) {
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(name));
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(filePath));
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(contentType));
+
string fileName = Path.GetFileName(filePath);
return CreateFormFilePart(name, fileName, contentType, File.OpenRead(filePath));
}
@@ -130,11 +134,11 @@ namespace DotNetOpenAuth.Messaging {
/// <returns>The constructed part.</returns>
public static MultipartPostPart CreateFormFilePart(string name, string fileName, string contentType, Stream content) {
Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(name));
- Contract.Requires<ArgumentException>(fileName != null);
- Contract.Requires<ArgumentException>(contentType != null);
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(fileName));
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(contentType));
Contract.Requires<ArgumentException>(content != null);
- var part = new MultipartPostPart("form-data");
+ var part = new MultipartPostPart("file");
part.ContentAttributes["name"] = name;
part.ContentAttributes["filename"] = fileName;
part.PartHeaders[HttpRequestHeader.ContentType] = contentType;