return GetValue<DateTimeOffset?> ("Expires");
}
set {
- AddOrRemove ("Expires", value);
+ AddOrRemove ("Expires", value, Parser.DateTime.ToString);
}
}
return GetValue<DateTimeOffset?> ("Last-Modified");
}
set {
- AddOrRemove ("Last-Modified", value);
+ AddOrRemove ("Last-Modified", value, Parser.DateTime.ToString);
}
}
}
return GetValue<DateTimeOffset?> ("Date");
}
set {
- AddOrRemove ("Date", value);
+ AddOrRemove ("Date", value, Parser.DateTime.ToString);
}
}
return GetValue<DateTimeOffset?> ("If-Modified-Since");
}
set {
- AddOrRemove ("If-Modified-Since", value);
+ AddOrRemove ("If-Modified-Since", value, Parser.DateTime.ToString);
}
}
return GetValue<DateTimeOffset?> ("If-Unmodified-Since");
}
set {
- AddOrRemove ("If-Unmodified-Since", value);
+ AddOrRemove ("If-Unmodified-Since", value, Parser.DateTime.ToString);
}
}
return GetValue<DateTimeOffset?> ("Date");
}
set {
- AddOrRemove ("Date", value);
+ AddOrRemove ("Date", value, Parser.DateTime.ToString);
}
}
public static class DateTime
{
+ public new static readonly Func<object, string> ToString = l => ((DateTimeOffset) l).ToString ("r", CultureInfo.InvariantCulture);
+
public static bool TryParse (string input, out DateTimeOffset result)
{
return Lexer.TryGetDateValue (input, out result);
System.Net.Http/HttpRequestMessage.cs
System.Net.Http/HttpResponseMessage.cs
System.Net.Http/MessageProcessingHandler.cs
+System.Net.Http/MultipartContent.cs
+System.Net.Http/MultipartFormDataContent.cs
System.Net.Http/StreamContent.cs
System.Net.Http/StringContent.cs
System.Net.Http.Headers/AuthenticationHeaderValue.cs
return Task.FromResult<Stream> (new MemoryStream (content, offset, count));
}
- protected override Task SerializeToStreamAsync (Stream stream, TransportContext context)
+ protected internal override Task SerializeToStreamAsync (Stream stream, TransportContext context)
{
return stream.WriteAsync (content, offset, count);
}
public async Task<byte[]> GetByteArrayAsync (string requestUri)
{
var resp = await GetAsync (requestUri, HttpCompletionOption.ResponseContentRead).ConfigureAwait (false);
- return await resp.Content.ReadAsByteArrayAsync ();
+ return await resp.Content.ReadAsByteArrayAsync ().ConfigureAwait (false);
}
public async Task<byte[]> GetByteArrayAsync (Uri requestUri)
{
var resp = await GetAsync (requestUri, HttpCompletionOption.ResponseContentRead).ConfigureAwait (false);
- return await resp.Content.ReadAsByteArrayAsync ();
+ return await resp.Content.ReadAsByteArrayAsync ().ConfigureAwait (false);
}
public async Task<Stream> GetStreamAsync (string requestUri)
{
var resp = await GetAsync (requestUri, HttpCompletionOption.ResponseContentRead).ConfigureAwait (false);
- return await resp.Content.ReadAsStreamAsync ();
+ return await resp.Content.ReadAsStreamAsync ().ConfigureAwait (false);
}
public async Task<Stream> GetStreamAsync (Uri requestUri)
{
var resp = await GetAsync (requestUri, HttpCompletionOption.ResponseContentRead).ConfigureAwait (false);
- return await resp.Content.ReadAsStreamAsync ();
+ return await resp.Content.ReadAsStreamAsync ().ConfigureAwait (false);
}
public async Task<string> GetStringAsync (string requestUri)
{
var resp = await GetAsync (requestUri, HttpCompletionOption.ResponseContentRead).ConfigureAwait (false);
- return await resp.Content.ReadAsStringAsync ();
+ return await resp.Content.ReadAsStringAsync ().ConfigureAwait (false);
}
public async Task<string> GetStringAsync (Uri requestUri)
{
var resp = await GetAsync (requestUri, HttpCompletionOption.ResponseContentRead).ConfigureAwait (false);
- return await resp.Content.ReadAsStringAsync ();
+ return await resp.Content.ReadAsStringAsync ().ConfigureAwait (false);
}
}
}
protected async virtual Task<Stream> CreateContentReadStreamAsync ()
{
- await LoadIntoBufferAsync ();
+ await LoadIntoBufferAsync ().ConfigureAwait (false);
return buffer;
}
public async Task<string> ReadAsStringAsync ()
{
- await LoadIntoBufferAsync ();
+ await LoadIntoBufferAsync ().ConfigureAwait (false);
if (buffer.Length == 0)
return string.Empty;
return encoding.GetString (buffer.GetBuffer (), 0, (int) buffer.Length);
}
- protected abstract Task SerializeToStreamAsync (Stream stream, TransportContext context);
+ protected internal abstract Task SerializeToStreamAsync (Stream stream, TransportContext context);
protected internal abstract bool TryComputeLength (out long length);
}
}
--- /dev/null
+//
+// MultipartContent.cs
+//
+// Authors:
+// Marek Safar <marek.safar@gmail.com>
+//
+// Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System.IO;
+using System.Threading.Tasks;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Net.Http.Headers;
+using System.Linq;
+using System.Text;
+
+namespace System.Net.Http
+{
+ public class MultipartContent : HttpContent, IEnumerable<HttpContent>
+ {
+ List<HttpContent> nested_content;
+ readonly string boundary;
+
+ public MultipartContent ()
+ : this ("mixed")
+ {
+ }
+
+ public MultipartContent (string subtype)
+ : this (subtype, Guid.NewGuid ().ToString ("D", CultureInfo.InvariantCulture))
+ {
+ }
+
+ public MultipartContent (string subtype, string boundary)
+ {
+ if (string.IsNullOrWhiteSpace (subtype))
+ throw new ArgumentException ("boundary");
+
+ //
+ // The only mandatory parameter for the multipart Content-Type is the boundary parameter, which consists
+ // of 1 to 70 characters from a set of characters known to be very robust through email gateways,
+ // and NOT ending with white space
+ //
+ if (string.IsNullOrWhiteSpace (boundary))
+ throw new ArgumentException ("boundary");
+
+ if (boundary.Length > 70)
+ throw new ArgumentOutOfRangeException ("boundary");
+
+ if (boundary.Last () == ' ' || !IsValidRFC2049 (boundary))
+ throw new ArgumentException ("boundary");
+
+ this.boundary = boundary;
+ this.nested_content = new List<HttpContent> (2);
+
+ Headers.ContentType = new MediaTypeHeaderValue ("multipart/" + subtype) {
+ Parameters = { new NameValueHeaderValue ("boundary", "\"" + boundary + "\"") }
+ };
+ }
+
+ static bool IsValidRFC2049 (string s)
+ {
+ foreach (char c in s) {
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))
+ continue;
+
+ switch (c) {
+ case '\'': case '(': case ')': case '+': case ',':
+ case '-': case '.': case '/': case ':': case '=':
+ case '?':
+ continue;
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ public virtual void Add (HttpContent content)
+ {
+ if (content == null)
+ throw new ArgumentNullException ("content");
+
+ if (nested_content == null)
+ nested_content = new List<HttpContent> ();
+
+ nested_content.Add (content);
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ if (disposing) {
+ foreach (var item in nested_content) {
+ item.Dispose ();
+ }
+
+ nested_content = null;
+ }
+
+ base.Dispose (disposing);
+ }
+
+ protected internal override async Task SerializeToStreamAsync (Stream stream, TransportContext context)
+ {
+ // RFC 2046
+ //
+ // The Content-Type field for multipart entities requires one parameter,
+ // "boundary". The boundary delimiter line is then defined as a line
+ // consisting entirely of two hyphen characters ("-", decimal value 45)
+ // followed by the boundary parameter value from the Content-Type header
+ // field, optional linear whitespace, and a terminating CRLF.
+ //
+
+ byte[] buffer;
+ var sb = new StringBuilder ();
+ sb.Append ('-').Append ('-');
+ sb.Append (boundary);
+ sb.Append ('\r').Append ('\n');
+
+ for (int i = 0; i < nested_content.Count; i++) {
+ var c = nested_content [i];
+
+ foreach (var h in c.Headers) {
+ sb.Append (h.Key);
+ sb.Append (':').Append (' ');
+ foreach (var v in h.Value) {
+ sb.Append (v);
+ }
+ sb.Append ('\r').Append ('\n');
+ }
+ sb.Append ('\r').Append ('\n');
+
+ buffer = Encoding.ASCII.GetBytes (sb.ToString ());
+ sb.Clear ();
+ await stream.WriteAsync (buffer, 0, buffer.Length).ConfigureAwait (false);
+
+ await c.SerializeToStreamAsync (stream, context).ConfigureAwait (false);
+
+ if (i != nested_content.Count - 1) {
+ sb.Append ('\r').Append ('\n');
+ sb.Append ('-').Append ('-');
+ sb.Append (boundary);
+ sb.Append ('\r').Append ('\n');
+ }
+ }
+
+ sb.Append ('\r').Append ('\n');
+ sb.Append ('-').Append ('-');
+ sb.Append (boundary);
+ sb.Append ('-').Append ('-');
+ sb.Append ('\r').Append ('\n');
+
+ buffer = Encoding.ASCII.GetBytes (sb.ToString ());
+ await stream.WriteAsync (buffer, 0, buffer.Length).ConfigureAwait (false);
+ }
+
+ protected internal override bool TryComputeLength (out long length)
+ {
+ length = 12 + 2 * boundary.Length;
+
+ for (int i = 0; i < nested_content.Count; i++) {
+ var c = nested_content [i];
+ foreach (var h in c.Headers) {
+ length += h.Key.Length;
+ length += 4;
+
+ foreach (var v in h.Value) {
+ length += v.Length;
+ }
+ }
+
+ long l;
+ if (!c.TryComputeLength (out l))
+ return false;
+
+ length += 2;
+ length += l;
+
+ if (i != nested_content.Count - 1) {
+ length += 6;
+ length += boundary.Length;
+ }
+ }
+
+ return true;
+ }
+
+ public IEnumerator<HttpContent> GetEnumerator ()
+ {
+ return nested_content.GetEnumerator ();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator ()
+ {
+ return nested_content.GetEnumerator ();
+ }
+ }
+}
--- /dev/null
+//
+// MultipartFormDataContent.cs
+//
+// Authors:
+// Marek Safar <marek.safar@gmail.com>
+//
+// Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System.Net.Http.Headers;
+
+namespace System.Net.Http
+{
+ public class MultipartFormDataContent : MultipartContent
+ {
+ public MultipartFormDataContent ()
+ : base ("form-data")
+ {
+ }
+
+ public MultipartFormDataContent (string boundary)
+ : base ("form-data", boundary)
+ {
+ }
+
+ public override void Add (HttpContent content)
+ {
+ base.Add (content);
+ AddContentDisposition (content, null, null);
+ }
+
+ public void Add (HttpContent content, string name)
+ {
+ base.Add (content);
+
+ if (string.IsNullOrWhiteSpace (name))
+ throw new ArgumentException ("name");
+
+ AddContentDisposition (content, name, null);
+ }
+
+ public void Add (HttpContent content, string name, string fileName)
+ {
+ base.Add (content);
+
+ if (string.IsNullOrWhiteSpace (name))
+ throw new ArgumentException ("name");
+
+ if (string.IsNullOrWhiteSpace (fileName))
+ throw new ArgumentException ("fileName");
+
+ AddContentDisposition (content, name, fileName);
+ }
+
+ void AddContentDisposition (HttpContent content, string name, string fileName)
+ {
+ var headers = content.Headers;
+ if (headers.ContentDisposition != null)
+ return;
+
+ headers.ContentDisposition = new ContentDispositionHeaderValue ("form-data") {
+ Name = name,
+ FileName = fileName,
+ FileNameStar = fileName
+ };
+ }
+ }
+}
base.Dispose (disposing);
}
- protected override Task SerializeToStreamAsync (Stream stream, TransportContext context)
+ protected internal override Task SerializeToStreamAsync (Stream stream, TransportContext context)
{
return content.CopyToAsync (stream, bufferSize);
}
System.Net.Http/HttpMethodTest.cs
System.Net.Http/HttpRequestMessageTest.cs
System.Net.Http/HttpResponseMessageTest.cs
+System.Net.Http/MultipartContentTest.cs
+System.Net.Http/MultipartFormDataContentTest.cs
System.Net.Http/StreamContentTest.cs
System.Net.Http/StringContentTest.cs
System.Net.Http.Headers/AuthenticationHeaderValueTest.cs
--- /dev/null
+//
+// MultipartContentTest.cs
+//
+// Authors:
+// Marek Safar <marek.safar@gmail.com>
+//
+// Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using NUnit.Framework;
+using System.Net.Http;
+using System.IO;
+using System.Threading.Tasks;
+using System.Text;
+using System.Linq;
+
+namespace MonoTests.System.Net.Http
+{
+ [TestFixture]
+ public class MultipartContentTest
+ {
+ [Test]
+ public void Ctor_Invalid ()
+ {
+ try {
+ new MultipartContent (null);
+ Assert.Fail ("#1");
+ } catch (ArgumentException) {
+ }
+
+ try {
+ new MultipartContent ("v", null);
+ Assert.Fail ("#2");
+ } catch (ArgumentException) {
+ }
+
+ try {
+ new MultipartContent ("st", "[]");
+ Assert.Fail ("#3");
+ } catch (ArgumentException) {
+ }
+
+ try {
+ new MultipartContent ("st", "1234567890123456789012345678901234567890123456789012345678901234567890X");
+ Assert.Fail ("#4");
+ } catch (ArgumentException) {
+ }
+
+ try {
+ new MultipartContent ("st", "st ");
+ Assert.Fail ("#5");
+ } catch (ArgumentException) {
+ }
+
+ try {
+ new MultipartContent ("st", "@");
+ Assert.Fail ("#6");
+ } catch (ArgumentException) {
+ }
+ }
+
+ [Test]
+ public void Ctor ()
+ {
+ using (var m = new MultipartContent ("a", "b")) {
+ m.Headers.Add ("extra", "value");
+ Assert.AreEqual ("multipart/a", m.Headers.ContentType.MediaType, "#1");
+ Assert.AreEqual (14, m.Headers.ContentLength, "#2");
+ Assert.AreEqual ("--b\r\n\r\n--b--\r\n", m.ReadAsStringAsync ().Result, "#3");
+ }
+
+ using (var m = new MultipartContent ()) {
+ Assert.AreEqual ("multipart/mixed", m.Headers.ContentType.MediaType, "#11");
+ Assert.AreEqual (84, m.Headers.ContentLength, "#12");
+ }
+
+ using (var m = new MultipartContent ("ggg")) {
+ Assert.AreEqual ("multipart/ggg", m.Headers.ContentType.MediaType, "#21");
+ Assert.AreEqual (84, m.Headers.ContentLength, "#22");
+ }
+ }
+
+ [Test]
+ public void Add ()
+ {
+ var m = new MultipartContent ("a", "b");
+
+ var other = new MultipartContent ("2", "44");
+ other.Headers.Expires = new DateTimeOffset (2020, 11, 30, 19, 55, 22, TimeSpan.Zero);
+ m.Add (other);
+
+ Assert.AreEqual ("multipart/a", m.Headers.ContentType.MediaType, "#1");
+ Assert.AreEqual (114, m.Headers.ContentLength, "#2");
+ Assert.AreEqual ("--b\r\nContent-Type: multipart/2; boundary=\"44\"\r\nExpires: Mon, 30 Nov 2020 19:55:22 GMT\r\n\r\n--44\r\n\r\n--44--\r\n\r\n--b--\r\n", m.ReadAsStringAsync ().Result, "#3");
+ Assert.AreEqual (other, m.First (), "#4");
+ }
+
+ [Test]
+ public void Add_2 ()
+ {
+ var m = new MultipartContent ("a", "X");
+
+ var other = new MultipartContent ("2", "2a");
+ m.Add (other);
+ var other2 = new MultipartContent ("3", "3a");
+ other2.Headers.Add ("9", "9n");
+ m.Add (other2);
+
+ Assert.AreEqual ("multipart/a", m.Headers.ContentType.MediaType, "#1");
+ Assert.AreEqual (148, m.Headers.ContentLength, "#2");
+ Assert.AreEqual ("--X\r\nContent-Type: multipart/2; boundary=\"2a\"\r\n\r\n--2a\r\n\r\n--2a--\r\n\r\n--X\r\nContent-Type: multipart/3; boundary=\"3a\"\r\n9: 9n\r\n\r\n--3a\r\n\r\n--3a--\r\n\r\n--X--\r\n",
+ m.ReadAsStringAsync ().Result, "#3");
+ Assert.AreEqual (other, m.First (), "#4");
+ }
+
+ [Test]
+ public void Add_Resursive ()
+ {
+ var m = new MultipartContent ("1", "1a");
+
+ var other = new MultipartContent ("2", "2a");
+ m.Add (other);
+
+ var other2 = new MultipartContent ("3", "3a");
+ other.Add (other2);
+
+ Assert.AreEqual ("multipart/1", m.Headers.ContentType.MediaType, "#1");
+ Assert.AreEqual (136, m.Headers.ContentLength, "#2");
+ Assert.AreEqual ("--1a\r\nContent-Type: multipart/2; boundary=\"2a\"\r\n\r\n--2a\r\nContent-Type: multipart/3; boundary=\"3a\"\r\n\r\n--3a\r\n\r\n--3a--\r\n\r\n--2a--\r\n\r\n--1a--\r\n",
+ m.ReadAsStringAsync ().Result, "#3");
+ Assert.AreEqual (other, m.First (), "#4");
+ }
+
+ [Test]
+ public void Add_Invalid ()
+ {
+ var m = new MultipartContent ("a", "b");
+ try {
+ m.Add (null);
+ Assert.Fail ("#1");
+ } catch (ArgumentNullException) {
+ }
+ }
+ }
+}
--- /dev/null
+//
+// MultipartFormDataContentTest.cs
+//
+// Authors:
+// Marek Safar <marek.safar@gmail.com>
+//
+// Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using NUnit.Framework;
+using System.Net.Http;
+using System.IO;
+using System.Threading.Tasks;
+using System.Text;
+using System.Linq;
+using System.Net.Http.Headers;
+
+namespace MonoTests.System.Net.Http
+{
+ [TestFixture]
+ public class MultipartFormDataContentTest
+ {
+ [Test]
+ public void Ctor_Invalid ()
+ {
+ try {
+ new MultipartFormDataContent (null);
+ Assert.Fail ("#1");
+ } catch (ArgumentException) {
+ }
+
+ try {
+ new MultipartFormDataContent ("[]");
+ Assert.Fail ("#2");
+ } catch (ArgumentException) {
+ }
+
+ try {
+ new MultipartFormDataContent ("1234567890123456789012345678901234567890123456789012345678901234567890X");
+ Assert.Fail ("#3");
+ } catch (ArgumentException) {
+ }
+
+ try {
+ new MultipartFormDataContent ("st ");
+ Assert.Fail ("#4");
+ } catch (ArgumentException) {
+ }
+
+ try {
+ new MultipartFormDataContent ("@");
+ Assert.Fail ("#5");
+ } catch (ArgumentException) {
+ }
+ }
+
+ [Test]
+ public void Ctor ()
+ {
+ using (var m = new MultipartFormDataContent ("b")) {
+ m.Headers.Add ("extra", "value");
+ Assert.AreEqual ("multipart/form-data", m.Headers.ContentType.MediaType, "#1");
+ Assert.IsNull (m.Headers.ContentDisposition, "#2");
+ Assert.AreEqual (14, m.Headers.ContentLength, "#3");
+ Assert.AreEqual ("--b\r\n\r\n--b--\r\n", m.ReadAsStringAsync ().Result, "#4");
+ }
+
+ using (var m = new MultipartFormDataContent ()) {
+ Assert.AreEqual ("multipart/form-data", m.Headers.ContentType.MediaType, "#11");
+ Assert.AreEqual (84, m.Headers.ContentLength, "#12");
+ }
+
+ using (var m = new MultipartFormDataContent ("ggg")) {
+ Assert.AreEqual ("multipart/form-data", m.Headers.ContentType.MediaType, "#21");
+ Assert.AreEqual (18, m.Headers.ContentLength, "#22");
+ }
+ }
+
+ [Test]
+ public void Add ()
+ {
+ var m = new MultipartFormDataContent ("b");
+
+ var other = new MultipartFormDataContent ("44");
+ other.Headers.Expires = new DateTimeOffset (2020, 11, 30, 19, 55, 22, TimeSpan.Zero);
+ m.Add (other);
+
+ Assert.AreEqual ("multipart/form-data", m.Headers.ContentType.MediaType, "#1");
+ Assert.AreEqual (154, m.Headers.ContentLength, "#2");
+ Assert.AreEqual ("--b\r\nContent-Type: multipart/form-data; boundary=\"44\"\r\nExpires: Mon, 30 Nov 2020 19:55:22 GMT\r\nContent-Disposition: form-data\r\n\r\n--44\r\n\r\n--44--\r\n\r\n--b--\r\n", m.ReadAsStringAsync ().Result, "#3");
+ Assert.AreEqual (other, m.First (), "#4");
+ Assert.IsNull (m.Headers.ContentDisposition, "#5");
+ Assert.AreEqual ("form-data", other.Headers.ContentDisposition.ToString (), "#6");
+ }
+
+ [Test]
+ public void Add_2 ()
+ {
+ var m = new MultipartFormDataContent ("b");
+
+ var other = new MultipartFormDataContent ("44");
+ m.Add (other, "name", "fname");
+
+ Assert.AreEqual ("multipart/form-data", m.Headers.ContentType.MediaType, "#1");
+ Assert.AreEqual (165, m.Headers.ContentLength, "#2");
+ Assert.AreEqual ("--b\r\nContent-Type: multipart/form-data; boundary=\"44\"\r\nContent-Disposition: form-data; name=name; filename=fname; filename*=utf-8''fname\r\n\r\n--44\r\n\r\n--44--\r\n\r\n--b--\r\n", m.ReadAsStringAsync ().Result, "#3");
+ Assert.AreEqual (other, m.First (), "#4");
+ Assert.IsNull (m.Headers.ContentDisposition, "#5");
+ Assert.AreEqual ("form-data; name=name; filename=fname; filename*=utf-8''fname", other.Headers.ContentDisposition.ToString (), "#6");
+ }
+
+ [Test]
+ public void Add_3 ()
+ {
+ var m = new MultipartFormDataContent ("b");
+
+ var other = new MultipartFormDataContent ("44");
+ other.Headers.ContentDisposition = new ContentDispositionHeaderValue ("dt");
+ m.Add (other, "name", "fname");
+
+ Assert.AreEqual ("multipart/form-data", m.Headers.ContentType.MediaType, "#1");
+ Assert.AreEqual (107, m.Headers.ContentLength, "#2");
+ Assert.AreEqual ("--b\r\nContent-Type: multipart/form-data; boundary=\"44\"\r\nContent-Disposition: dt\r\n\r\n--44\r\n\r\n--44--\r\n\r\n--b--\r\n", m.ReadAsStringAsync ().Result, "#3");
+ Assert.AreEqual (other, m.First (), "#4");
+ Assert.IsNull (m.Headers.ContentDisposition, "#5");
+ Assert.AreEqual ("dt", other.Headers.ContentDisposition.ToString (), "#6");
+ }
+
+ [Test]
+ public void Add_Invalid ()
+ {
+ var m = new MultipartFormDataContent ("a");
+ try {
+ m.Add (null);
+ Assert.Fail ("#1");
+ } catch (ArgumentNullException) {
+ }
+
+ try {
+ m.Add (new MultipartFormDataContent ("44"), null);
+ Assert.Fail ("#2");
+ } catch (ArgumentException) {
+ }
+
+ try {
+ m.Add (new MultipartFormDataContent ("44"), "s", null);
+ Assert.Fail ("#3");
+ } catch (ArgumentException) {
+ }
+
+ try {
+ m.Add (new MultipartFormDataContent ("44"), "s", " ");
+ Assert.Fail ("#4");
+ } catch (ArgumentException) {
+ }
+ }
+ }
+}