PageRenderTime 26ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/test/System.Net.Http.Formatting.Test.Unit/Formatting/Parsers/MimeMultipartParserTests.cs

https://bitbucket.org/mdavid/aspnetwebstack
C# | 483 lines | 402 code | 80 blank | 1 comment | 26 complexity | a72b7accebad026d2f6bbe4770f1179c MD5 | raw file
  1. using System.Collections.Generic;
  2. using System.IO;
  3. using System.Text;
  4. using Microsoft.TestCommon;
  5. using Xunit;
  6. using Xunit.Extensions;
  7. using Assert = Microsoft.TestCommon.AssertEx;
  8. namespace System.Net.Http.Formatting.Parsers
  9. {
  10. public class MimeMultipartParserTests
  11. {
  12. [Fact]
  13. public void MimeMultipartParserTypeIsCorrect()
  14. {
  15. Assert.Type.HasProperties<InternetMessageFormatHeaderParser>(TypeAssert.TypeProperties.IsClass);
  16. }
  17. private static MimeMultipartParser CreateMimeMultipartParser(int maximumHeaderLength, string boundary)
  18. {
  19. return new MimeMultipartParser(boundary, maximumHeaderLength);
  20. }
  21. internal static byte[] CreateBuffer(string boundary, params string[] bodyparts)
  22. {
  23. return CreateBuffer(boundary, false, bodyparts);
  24. }
  25. internal static string CreateNestedBuffer(int count)
  26. {
  27. StringBuilder buffer = new StringBuilder("content");
  28. for (var cnt = 0; cnt < count; cnt++)
  29. {
  30. byte[] nested = CreateBuffer("N" + cnt.ToString(), buffer.ToString());
  31. var message = Encoding.UTF8.GetString(nested);
  32. buffer.Length = 0;
  33. buffer.AppendLine(message);
  34. }
  35. return buffer.ToString();
  36. }
  37. private static byte[] CreateBuffer(string boundary, bool withLws, params string[] bodyparts)
  38. {
  39. const string SP = " ";
  40. const string HTAB = "\t";
  41. const string CRLF = "\r\n";
  42. const string DashDash = "--";
  43. string lws = String.Empty;
  44. if (withLws)
  45. {
  46. lws = SP + SP + HTAB + SP;
  47. }
  48. StringBuilder message = new StringBuilder();
  49. message.Append(DashDash + boundary + lws + CRLF);
  50. for (var cnt = 0; cnt < bodyparts.Length; cnt++)
  51. {
  52. message.Append(bodyparts[cnt]);
  53. if (cnt < bodyparts.Length - 1)
  54. {
  55. message.Append(CRLF + DashDash + boundary + lws + CRLF);
  56. }
  57. }
  58. // Note: We rely on a final CRLF even though it is not required by the BNF existing application do send it
  59. message.Append(CRLF + DashDash + boundary + DashDash + lws + CRLF);
  60. return Encoding.UTF8.GetBytes(message.ToString());
  61. }
  62. private static MimeMultipartParser.State ParseBufferInSteps(MimeMultipartParser parser, byte[] buffer, int readsize, out List<string> bodyParts, out int totalBytesConsumed)
  63. {
  64. MimeMultipartParser.State state = MimeMultipartParser.State.Invalid;
  65. totalBytesConsumed = 0;
  66. bodyParts = new List<string>();
  67. bool isFinal = false;
  68. byte[] currentBodyPart = new byte[32 * 1024];
  69. int currentBodyLength = 0;
  70. while (totalBytesConsumed <= buffer.Length)
  71. {
  72. int size = Math.Min(buffer.Length - totalBytesConsumed, readsize);
  73. byte[] parseBuffer = new byte[size];
  74. Buffer.BlockCopy(buffer, totalBytesConsumed, parseBuffer, 0, size);
  75. int bytesConsumed = 0;
  76. ArraySegment<byte> out1;
  77. ArraySegment<byte> out2;
  78. state = parser.ParseBuffer(parseBuffer, parseBuffer.Length, ref bytesConsumed, out out1, out out2, out isFinal);
  79. totalBytesConsumed += bytesConsumed;
  80. Buffer.BlockCopy(out1.Array, out1.Offset, currentBodyPart, currentBodyLength, out1.Count);
  81. currentBodyLength += out1.Count;
  82. Buffer.BlockCopy(out2.Array, out2.Offset, currentBodyPart, currentBodyLength, out2.Count);
  83. currentBodyLength += out2.Count;
  84. if (state == MimeMultipartParser.State.BodyPartCompleted)
  85. {
  86. var bPart = new byte[currentBodyLength];
  87. Buffer.BlockCopy(currentBodyPart, 0, bPart, 0, currentBodyLength);
  88. bodyParts.Add(Encoding.UTF8.GetString(bPart));
  89. currentBodyLength = 0;
  90. if (isFinal)
  91. {
  92. break;
  93. }
  94. }
  95. else if (state != MimeMultipartParser.State.NeedMoreData)
  96. {
  97. return state;
  98. }
  99. }
  100. Assert.True(isFinal);
  101. return state;
  102. }
  103. public static IEnumerable<object[]> Boundaries
  104. {
  105. get { return ParserData.Boundaries; }
  106. }
  107. [Theory]
  108. [PropertyData("Boundaries")]
  109. public void MimeMultipartParserConstructorTest(string boundary)
  110. {
  111. MimeMultipartParser parser = new MimeMultipartParser(boundary, ParserData.MinMessageSize);
  112. Assert.NotNull(parser);
  113. Assert.ThrowsArgumentGreaterThanOrEqualTo(() => new MimeMultipartParser("-", ParserData.MinMessageSize - 1),
  114. "maxMessageSize", ParserData.MinMessageSize.ToString(), ParserData.MinMessageSize - 1);
  115. foreach (string empty in TestData.EmptyStrings)
  116. {
  117. Assert.ThrowsArgument(() => { new MimeMultipartParser(empty, ParserData.MinMessageSize); }, "boundary", allowDerivedExceptions: true);
  118. }
  119. Assert.ThrowsArgument(() => { new MimeMultipartParser("trailingspace ", ParserData.MinMessageSize); }, "boundary");
  120. Assert.ThrowsArgumentNull(() => { new MimeMultipartParser(null, ParserData.MinMessageSize); }, "boundary");
  121. }
  122. [Fact]
  123. public void MultipartParserNullBuffer()
  124. {
  125. MimeMultipartParser parser = CreateMimeMultipartParser(128, "-");
  126. Assert.NotNull(parser);
  127. int bytesConsumed = 0;
  128. ArraySegment<byte> out1;
  129. ArraySegment<byte> out2;
  130. bool isFinal;
  131. Assert.ThrowsArgumentNull(() => { parser.ParseBuffer(null, 0, ref bytesConsumed, out out1, out out2, out isFinal); }, "buffer");
  132. }
  133. [Theory]
  134. [PropertyData("Boundaries")]
  135. public void MultipartParserEmptyBuffer(string boundary)
  136. {
  137. byte[] data = CreateBuffer(boundary);
  138. for (var cnt = 1; cnt <= data.Length; cnt++)
  139. {
  140. MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);
  141. Assert.NotNull(parser);
  142. int totalBytesConsumed;
  143. List<string> bodyParts;
  144. MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
  145. Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
  146. Assert.Equal(data.Length, totalBytesConsumed);
  147. Assert.Equal(2, bodyParts.Count);
  148. Assert.Equal(0, bodyParts[0].Length);
  149. Assert.Equal(0, bodyParts[1].Length);
  150. }
  151. }
  152. [Theory]
  153. [PropertyData("Boundaries")]
  154. public void MultipartParserSingleShortBodyPart(string boundary)
  155. {
  156. byte[] data = CreateBuffer(boundary, "A");
  157. for (var cnt = 1; cnt <= data.Length; cnt++)
  158. {
  159. MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);
  160. Assert.NotNull(parser);
  161. int totalBytesConsumed;
  162. List<string> bodyParts;
  163. MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
  164. Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
  165. Assert.Equal(data.Length, totalBytesConsumed);
  166. Assert.Equal(2, bodyParts.Count);
  167. Assert.Equal(0, bodyParts[0].Length);
  168. Assert.Equal(1, bodyParts[1].Length);
  169. Assert.Equal("A", bodyParts[1]);
  170. }
  171. }
  172. [Theory]
  173. [PropertyData("Boundaries")]
  174. public void MultipartParserMultipleShortBodyParts(string boundary)
  175. {
  176. string[] text = new string[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
  177. byte[] data = CreateBuffer(boundary, text);
  178. for (var cnt = 1; cnt <= data.Length; cnt++)
  179. {
  180. MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);
  181. Assert.NotNull(parser);
  182. int totalBytesConsumed;
  183. List<string> bodyParts;
  184. MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
  185. Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
  186. Assert.Equal(data.Length, totalBytesConsumed);
  187. Assert.Equal(text.Length + 1, bodyParts.Count);
  188. Assert.Equal(0, bodyParts[0].Length);
  189. for (var check = 0; check < text.Length; check++)
  190. {
  191. Assert.Equal(1, bodyParts[check + 1].Length);
  192. Assert.Equal(text[check], bodyParts[check + 1]);
  193. }
  194. }
  195. }
  196. [Theory]
  197. [PropertyData("Boundaries")]
  198. public void MultipartParserMultipleShortBodyPartsWithLws(string boundary)
  199. {
  200. string[] text = new string[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
  201. byte[] data = CreateBuffer(boundary, true, text);
  202. for (var cnt = 1; cnt <= data.Length; cnt++)
  203. {
  204. MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);
  205. Assert.NotNull(parser);
  206. int totalBytesConsumed;
  207. List<string> bodyParts;
  208. MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
  209. Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
  210. Assert.Equal(data.Length, totalBytesConsumed);
  211. Assert.Equal(text.Length + 1, bodyParts.Count);
  212. Assert.Equal(0, bodyParts[0].Length);
  213. for (var check = 0; check < text.Length; check++)
  214. {
  215. Assert.Equal(1, bodyParts[check + 1].Length);
  216. Assert.Equal(text[check], bodyParts[check + 1]);
  217. }
  218. }
  219. }
  220. [Theory]
  221. [PropertyData("Boundaries")]
  222. public void MultipartParserSingleLongBodyPart(string boundary)
  223. {
  224. const string text = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
  225. byte[] data = CreateBuffer(boundary, text);
  226. for (var cnt = 1; cnt <= data.Length; cnt++)
  227. {
  228. MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);
  229. Assert.NotNull(parser);
  230. int totalBytesConsumed;
  231. List<string> bodyParts;
  232. MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
  233. Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
  234. Assert.Equal(data.Length, totalBytesConsumed);
  235. Assert.Equal(2, bodyParts.Count);
  236. Assert.Equal(0, bodyParts[0].Length);
  237. Assert.Equal(text.Length, bodyParts[1].Length);
  238. Assert.Equal(text, bodyParts[1]);
  239. }
  240. }
  241. [Theory]
  242. [PropertyData("Boundaries")]
  243. public void MultipartParserMultipleLongBodyParts(string boundary)
  244. {
  245. const string middleText = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
  246. string[] text = new string[] {
  247. "A" + middleText + "A",
  248. "B" + middleText + "B",
  249. "C" + middleText + "C",
  250. "D" + middleText + "D",
  251. "E" + middleText + "E",
  252. "F" + middleText + "F",
  253. "G" + middleText + "G",
  254. "H" + middleText + "H",
  255. "I" + middleText + "I",
  256. "J" + middleText + "J",
  257. "K" + middleText + "K",
  258. "L" + middleText + "L",
  259. "M" + middleText + "M",
  260. "N" + middleText + "N",
  261. "O" + middleText + "O",
  262. "P" + middleText + "P",
  263. "Q" + middleText + "Q",
  264. "R" + middleText + "R",
  265. "S" + middleText + "S",
  266. "T" + middleText + "T",
  267. "U" + middleText + "U",
  268. "V" + middleText + "V",
  269. "W" + middleText + "W",
  270. "X" + middleText + "X",
  271. "Y" + middleText + "Y",
  272. "Z" + middleText + "Z"};
  273. byte[] data = CreateBuffer(boundary, text);
  274. for (var cnt = 1; cnt <= data.Length; cnt++)
  275. {
  276. MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);
  277. Assert.NotNull(parser);
  278. int totalBytesConsumed;
  279. List<string> bodyParts;
  280. MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
  281. Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
  282. Assert.Equal(data.Length, totalBytesConsumed);
  283. Assert.Equal(text.Length + 1, bodyParts.Count);
  284. Assert.Equal(0, bodyParts[0].Length);
  285. for (var check = 0; check < text.Length; check++)
  286. {
  287. Assert.Equal(text[check].Length, bodyParts[check + 1].Length);
  288. Assert.Equal(text[check], bodyParts[check + 1]);
  289. }
  290. }
  291. }
  292. [Theory]
  293. [PropertyData("Boundaries")]
  294. public void MultipartParserNearMatches(string boundary)
  295. {
  296. const string CR = "\r";
  297. const string CRLF = "\r\n";
  298. const string Dash = "-";
  299. const string DashDash = "--";
  300. string[] text = new string[] {
  301. CR + Dash + "AAA",
  302. CRLF + Dash + "AAA",
  303. CRLF + DashDash + "AAA" + CR + "AAA",
  304. CRLF,
  305. "AAA",
  306. "AAA" + CRLF,
  307. CRLF + CRLF,
  308. CRLF + CRLF + CRLF,
  309. "AAA" + DashDash + "AAA",
  310. CRLF + "AAA" + DashDash + "AAA" + DashDash,
  311. CRLF + DashDash + "AAA" + CRLF,
  312. CRLF + DashDash + "AAA" + CRLF + CRLF,
  313. CRLF + DashDash + "AAA" + DashDash + CRLF,
  314. CRLF + DashDash + "AAA" + DashDash + CRLF + CRLF
  315. };
  316. byte[] data = CreateBuffer(boundary, text);
  317. for (var cnt = 1; cnt <= data.Length; cnt++)
  318. {
  319. MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);
  320. Assert.NotNull(parser);
  321. int totalBytesConsumed;
  322. List<string> bodyParts;
  323. MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
  324. Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
  325. Assert.Equal(data.Length, totalBytesConsumed);
  326. Assert.Equal(text.Length + 1, bodyParts.Count);
  327. Assert.Equal(0, bodyParts[0].Length);
  328. for (var check = 0; check < text.Length; check++)
  329. {
  330. Assert.Equal(text[check].Length, bodyParts[check + 1].Length);
  331. Assert.Equal(text[check], bodyParts[check + 1]);
  332. }
  333. }
  334. }
  335. [Theory]
  336. [PropertyData("Boundaries")]
  337. public void MultipartParserNesting(string boundary)
  338. {
  339. for (var nesting = 0; nesting < 16; nesting++)
  340. {
  341. string nested = CreateNestedBuffer(nesting);
  342. byte[] data = CreateBuffer(boundary, nested);
  343. for (var cnt = 1; cnt <= data.Length; cnt++)
  344. {
  345. MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);
  346. Assert.NotNull(parser);
  347. int totalBytesConsumed;
  348. List<string> bodyParts;
  349. MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
  350. Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
  351. Assert.Equal(data.Length, totalBytesConsumed);
  352. Assert.Equal(2, bodyParts.Count);
  353. Assert.Equal(0, bodyParts[0].Length);
  354. Assert.Equal(nested.Length, bodyParts[1].Length);
  355. }
  356. }
  357. }
  358. [Theory]
  359. [PropertyData("Boundaries")]
  360. public void MimeMultipartParserTestDataTooBig(string boundary)
  361. {
  362. byte[] data = CreateBuffer(boundary);
  363. for (var cnt = 1; cnt <= data.Length; cnt++)
  364. {
  365. MimeMultipartParser parser = CreateMimeMultipartParser(ParserData.MinMessageSize, boundary);
  366. Assert.NotNull(parser);
  367. int totalBytesConsumed;
  368. List<string> bodyParts;
  369. MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
  370. Assert.Equal(MimeMultipartParser.State.DataTooBig, state);
  371. Assert.Equal(ParserData.MinMessageSize, totalBytesConsumed);
  372. }
  373. }
  374. [Theory]
  375. [PropertyData("Boundaries")]
  376. public void MimeMultipartParserTestMultipartContent(string boundary)
  377. {
  378. MultipartContent content = new MultipartContent("mixed", boundary);
  379. content.Add(new StringContent("A"));
  380. content.Add(new StringContent("B"));
  381. content.Add(new StringContent("C"));
  382. MemoryStream memStream = new MemoryStream();
  383. content.CopyToAsync(memStream).Wait();
  384. memStream.Position = 0;
  385. byte[] data = memStream.ToArray();
  386. for (var cnt = 1; cnt <= data.Length; cnt++)
  387. {
  388. MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);
  389. Assert.NotNull(parser);
  390. int totalBytesConsumed;
  391. List<string> bodyParts;
  392. MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
  393. Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
  394. Assert.Equal(data.Length, totalBytesConsumed);
  395. Assert.Equal(4, bodyParts.Count);
  396. Assert.Empty(bodyParts[0]);
  397. Assert.True(bodyParts[1].EndsWith("A"));
  398. Assert.True(bodyParts[2].EndsWith("B"));
  399. Assert.True(bodyParts[3].EndsWith("C"));
  400. }
  401. }
  402. }
  403. }