1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System; 6 using System.Collections.Generic; 7 using System.Linq; 8 using System.Net.Http.Headers; 9 using System.Text; 10 11 using Xunit; 12 13 namespace System.Net.Http.Tests 14 { 15 public class ContentDispositionHeaderValueTest 16 { 17 [Fact] Ctor_ContentDispositionNull_Throw()18 public void Ctor_ContentDispositionNull_Throw() 19 { 20 AssertExtensions.Throws<ArgumentException>("dispositionType", () => { new ContentDispositionHeaderValue(null); }); 21 } 22 23 [Fact] Ctor_ContentDispositionEmpty_Throw()24 public void Ctor_ContentDispositionEmpty_Throw() 25 { 26 // null and empty should be treated the same. So we also throw for empty strings. 27 AssertExtensions.Throws<ArgumentException>("dispositionType", () => { new ContentDispositionHeaderValue(string.Empty); }); 28 } 29 30 [Fact] Ctor_ContentDispositionInvalidFormat_ThrowFormatException()31 public void Ctor_ContentDispositionInvalidFormat_ThrowFormatException() 32 { 33 // When adding values using strongly typed objects, no leading/trailing LWS (whitespace) are allowed. 34 AssertFormatException(" inline "); 35 AssertFormatException(" inline"); 36 AssertFormatException("inline "); 37 AssertFormatException("\"inline\""); 38 AssertFormatException("te xt"); 39 AssertFormatException("te=xt"); 40 AssertFormatException("te\u00E4xt"); 41 AssertFormatException("text;"); 42 AssertFormatException("te/xt;"); 43 AssertFormatException("inline; name=someName; "); 44 AssertFormatException("text;name=someName"); // ctor takes only disposition-type name, no parameters 45 } 46 47 [Fact] Ctor_ContentDispositionValidFormat_SuccessfullyCreated()48 public void Ctor_ContentDispositionValidFormat_SuccessfullyCreated() 49 { 50 ContentDispositionHeaderValue contentDisposition = new ContentDispositionHeaderValue("inline"); 51 Assert.Equal("inline", contentDisposition.DispositionType); 52 Assert.Equal(0, contentDisposition.Parameters.Count); 53 Assert.Null(contentDisposition.Name); 54 Assert.Null(contentDisposition.FileName); 55 Assert.Null(contentDisposition.CreationDate); 56 Assert.Null(contentDisposition.ModificationDate); 57 Assert.Null(contentDisposition.ReadDate); 58 Assert.Null(contentDisposition.Size); 59 } 60 61 [Fact] Parameters_AddNull_Throw()62 public void Parameters_AddNull_Throw() 63 { 64 ContentDispositionHeaderValue contentDisposition = new ContentDispositionHeaderValue("inline"); 65 Assert.Throws<ArgumentNullException>(() => { contentDisposition.Parameters.Add(null); }); 66 } 67 68 [Fact] ContentDisposition_SetAndGetContentDisposition_MatchExpectations()69 public void ContentDisposition_SetAndGetContentDisposition_MatchExpectations() 70 { 71 ContentDispositionHeaderValue contentDisposition = new ContentDispositionHeaderValue("inline"); 72 Assert.Equal("inline", contentDisposition.DispositionType); 73 74 contentDisposition.DispositionType = "attachment"; 75 Assert.Equal("attachment", contentDisposition.DispositionType); 76 } 77 78 [Fact] Name_SetNameAndValidateObject_ParametersEntryForNameAdded()79 public void Name_SetNameAndValidateObject_ParametersEntryForNameAdded() 80 { 81 ContentDispositionHeaderValue contentDisposition = new ContentDispositionHeaderValue("inline"); 82 contentDisposition.Name = "myname"; 83 Assert.Equal("myname", contentDisposition.Name); 84 Assert.Equal(1, contentDisposition.Parameters.Count); 85 Assert.Equal("name", contentDisposition.Parameters.First().Name); 86 87 contentDisposition.Name = null; 88 Assert.Null(contentDisposition.Name); 89 Assert.Equal(0, contentDisposition.Parameters.Count); 90 contentDisposition.Name = null; // It's OK to set it again to null; no exception. 91 } 92 93 [Fact] Name_AddNameParameterThenUseProperty_ParametersEntryIsOverwritten()94 public void Name_AddNameParameterThenUseProperty_ParametersEntryIsOverwritten() 95 { 96 ContentDispositionHeaderValue contentDisposition = new ContentDispositionHeaderValue("inline"); 97 98 // Note that uppercase letters are used. Comparison should happen case-insensitive. 99 NameValueHeaderValue name = new NameValueHeaderValue("NAME", "old_name"); 100 contentDisposition.Parameters.Add(name); 101 Assert.Equal(1, contentDisposition.Parameters.Count); 102 Assert.Equal("NAME", contentDisposition.Parameters.First().Name); 103 104 contentDisposition.Name = "new_name"; 105 Assert.Equal("new_name", contentDisposition.Name); 106 Assert.Equal(1, contentDisposition.Parameters.Count); 107 Assert.Equal("NAME", contentDisposition.Parameters.First().Name); 108 109 contentDisposition.Parameters.Remove(name); 110 Assert.Null(contentDisposition.Name); 111 } 112 113 [Fact] FileName_AddNameParameterThenUseProperty_ParametersEntryIsOverwritten()114 public void FileName_AddNameParameterThenUseProperty_ParametersEntryIsOverwritten() 115 { 116 ContentDispositionHeaderValue contentDisposition = new ContentDispositionHeaderValue("inline"); 117 118 // Note that uppercase letters are used. Comparison should happen case-insensitive. 119 NameValueHeaderValue fileName = new NameValueHeaderValue("FILENAME", "old_name"); 120 contentDisposition.Parameters.Add(fileName); 121 Assert.Equal(1, contentDisposition.Parameters.Count); 122 Assert.Equal("FILENAME", contentDisposition.Parameters.First().Name); 123 124 contentDisposition.FileName = "new_name"; 125 Assert.Equal("new_name", contentDisposition.FileName); 126 Assert.Equal(1, contentDisposition.Parameters.Count); 127 Assert.Equal("FILENAME", contentDisposition.Parameters.First().Name); 128 129 contentDisposition.Parameters.Remove(fileName); 130 Assert.Null(contentDisposition.FileName); 131 } 132 133 [Fact] FileName_NeedsEncoding_EncodedAndDecodedCorrectly()134 public void FileName_NeedsEncoding_EncodedAndDecodedCorrectly() 135 { 136 ContentDispositionHeaderValue contentDisposition = new ContentDispositionHeaderValue("inline"); 137 138 contentDisposition.FileName = "File\u00C3Name.bat"; 139 Assert.Equal("File\u00C3Name.bat", contentDisposition.FileName); 140 Assert.Equal(1, contentDisposition.Parameters.Count); 141 Assert.Equal("filename", contentDisposition.Parameters.First().Name); 142 Assert.Equal("\"=?utf-8?B?RmlsZcODTmFtZS5iYXQ=?=\"", contentDisposition.Parameters.First().Value); 143 144 contentDisposition.Parameters.Remove(contentDisposition.Parameters.First()); 145 Assert.Null(contentDisposition.FileName); 146 } 147 148 [Fact] FileName_UnknownOrBadEncoding_PropertyFails()149 public void FileName_UnknownOrBadEncoding_PropertyFails() 150 { 151 ContentDispositionHeaderValue contentDisposition = new ContentDispositionHeaderValue("inline"); 152 153 // Note that uppercase letters are used. Comparison should happen case-insensitive. 154 NameValueHeaderValue fileName = new NameValueHeaderValue("FILENAME", "\"=?utf-99?Q?R=mlsZcODTmFtZS5iYXQ=?=\""); 155 contentDisposition.Parameters.Add(fileName); 156 Assert.Equal(1, contentDisposition.Parameters.Count); 157 Assert.Equal("FILENAME", contentDisposition.Parameters.First().Name); 158 Assert.Equal("\"=?utf-99?Q?R=mlsZcODTmFtZS5iYXQ=?=\"", contentDisposition.Parameters.First().Value); 159 Assert.Equal("\"=?utf-99?Q?R=mlsZcODTmFtZS5iYXQ=?=\"", contentDisposition.FileName); 160 161 contentDisposition.FileName = "new_name"; 162 Assert.Equal("new_name", contentDisposition.FileName); 163 Assert.Equal(1, contentDisposition.Parameters.Count); 164 Assert.Equal("FILENAME", contentDisposition.Parameters.First().Name); 165 166 contentDisposition.Parameters.Remove(fileName); 167 Assert.Null(contentDisposition.FileName); 168 } 169 170 [Fact] FileNameStar_AddNameParameterThenUseProperty_ParametersEntryIsOverwritten()171 public void FileNameStar_AddNameParameterThenUseProperty_ParametersEntryIsOverwritten() 172 { 173 ContentDispositionHeaderValue contentDisposition = new ContentDispositionHeaderValue("inline"); 174 175 // Note that uppercase letters are used. Comparison should happen case-insensitive. 176 NameValueHeaderValue fileNameStar = new NameValueHeaderValue("FILENAME*", "old_name"); 177 contentDisposition.Parameters.Add(fileNameStar); 178 Assert.Equal(1, contentDisposition.Parameters.Count); 179 Assert.Equal("FILENAME*", contentDisposition.Parameters.First().Name); 180 Assert.Null(contentDisposition.FileNameStar); // Decode failure 181 182 contentDisposition.FileNameStar = "new_name"; 183 Assert.Equal("new_name", contentDisposition.FileNameStar); 184 Assert.Equal(1, contentDisposition.Parameters.Count); 185 Assert.Equal("FILENAME*", contentDisposition.Parameters.First().Name); 186 Assert.Equal("utf-8\'\'new_name", contentDisposition.Parameters.First().Value); 187 188 contentDisposition.Parameters.Remove(fileNameStar); 189 Assert.Null(contentDisposition.FileNameStar); 190 } 191 192 [Theory] 193 [InlineData("no_quotes")] 194 [InlineData("one'quote")] 195 [InlineData("'triple'quotes'")] FileNameStar_NotTwoQuotes_IsNull(string value)196 public void FileNameStar_NotTwoQuotes_IsNull(string value) 197 { 198 ContentDispositionHeaderValue contentDisposition = new ContentDispositionHeaderValue("inline"); 199 200 // Note that uppercase letters are used. Comparison should happen case-insensitive. 201 NameValueHeaderValue fileNameStar = new NameValueHeaderValue("FILENAME*", value); 202 contentDisposition.Parameters.Add(fileNameStar); 203 Assert.Equal(1, contentDisposition.Parameters.Count); 204 Assert.Same(fileNameStar, contentDisposition.Parameters.First()); 205 Assert.Null(contentDisposition.FileNameStar); // Decode failure 206 } 207 208 [Fact] FileNameStar_NeedsEncoding_EncodedAndDecodedCorrectly()209 public void FileNameStar_NeedsEncoding_EncodedAndDecodedCorrectly() 210 { 211 ContentDispositionHeaderValue contentDisposition = new ContentDispositionHeaderValue("inline"); 212 213 contentDisposition.FileNameStar = "File\u00C3Name.bat"; 214 Assert.Equal("File\u00C3Name.bat", contentDisposition.FileNameStar); 215 Assert.Equal(1, contentDisposition.Parameters.Count); 216 Assert.Equal("filename*", contentDisposition.Parameters.First().Name); 217 Assert.Equal("utf-8\'\'File%C3%83Name.bat", contentDisposition.Parameters.First().Value); 218 219 contentDisposition.Parameters.Remove(contentDisposition.Parameters.First()); 220 Assert.Null(contentDisposition.FileNameStar); 221 } 222 223 [Fact] FileNameStar_UnknownOrBadEncoding_PropertyFails()224 public void FileNameStar_UnknownOrBadEncoding_PropertyFails() 225 { 226 ContentDispositionHeaderValue contentDisposition = new ContentDispositionHeaderValue("inline"); 227 228 // Note that uppercase letters are used. Comparison should happen case-insensitive. 229 NameValueHeaderValue fileNameStar = new NameValueHeaderValue("FILENAME*", "utf-99'lang'File%CZName.bat"); 230 contentDisposition.Parameters.Add(fileNameStar); 231 Assert.Equal(1, contentDisposition.Parameters.Count); 232 Assert.Same(fileNameStar, contentDisposition.Parameters.First()); 233 Assert.Null(contentDisposition.FileNameStar); // Decode failure 234 235 contentDisposition.FileNameStar = "new_name"; 236 Assert.Equal("new_name", contentDisposition.FileNameStar); 237 Assert.Equal(1, contentDisposition.Parameters.Count); 238 Assert.Equal("FILENAME*", contentDisposition.Parameters.First().Name); 239 240 contentDisposition.Parameters.Remove(fileNameStar); 241 Assert.Null(contentDisposition.FileNameStar); 242 } 243 244 [Fact] Dates_AddDateParameterThenUseProperty_ParametersEntryIsOverwritten()245 public void Dates_AddDateParameterThenUseProperty_ParametersEntryIsOverwritten() 246 { 247 string validDateString = "\"Tue, 15 Nov 1994 08:12:31 GMT\""; 248 DateTimeOffset validDate = DateTimeOffset.Parse("Tue, 15 Nov 1994 08:12:31 GMT"); 249 250 ContentDispositionHeaderValue contentDisposition = new ContentDispositionHeaderValue("inline"); 251 252 // Note that uppercase letters are used. Comparison should happen case-insensitive. 253 NameValueHeaderValue dateParameter = new NameValueHeaderValue("Creation-DATE", validDateString); 254 contentDisposition.Parameters.Add(dateParameter); 255 Assert.Equal(1, contentDisposition.Parameters.Count); 256 Assert.Equal("Creation-DATE", contentDisposition.Parameters.First().Name); 257 258 Assert.Equal(validDate, contentDisposition.CreationDate); 259 260 DateTimeOffset newDate = validDate.AddSeconds(1); 261 contentDisposition.CreationDate = newDate; 262 Assert.Equal(newDate, contentDisposition.CreationDate); 263 Assert.Equal(1, contentDisposition.Parameters.Count); 264 Assert.Equal("Creation-DATE", contentDisposition.Parameters.First().Name); 265 Assert.Equal("\"Tue, 15 Nov 1994 08:12:32 GMT\"", contentDisposition.Parameters.First().Value); 266 267 contentDisposition.Parameters.Remove(dateParameter); 268 Assert.Null(contentDisposition.CreationDate); 269 } 270 271 [Fact] Dates_InvalidDates_PropertyFails()272 public void Dates_InvalidDates_PropertyFails() 273 { 274 string invalidDateString = "\"Tue, 15 Nov 94 08:12 GMT\""; 275 276 ContentDispositionHeaderValue contentDisposition = new ContentDispositionHeaderValue("inline"); 277 278 // Note that uppercase letters are used. Comparison should happen case-insensitive. 279 NameValueHeaderValue dateParameter = new NameValueHeaderValue("read-DATE", invalidDateString); 280 contentDisposition.Parameters.Add(dateParameter); 281 Assert.Equal(1, contentDisposition.Parameters.Count); 282 Assert.Equal("read-DATE", contentDisposition.Parameters.First().Name); 283 284 Assert.Null(contentDisposition.ReadDate); 285 286 contentDisposition.ReadDate = null; 287 Assert.Null(contentDisposition.ReadDate); 288 Assert.Equal(0, contentDisposition.Parameters.Count); 289 } 290 291 [Fact] Size_AddSizeParameterThenUseProperty_ParametersEntryIsOverwritten()292 public void Size_AddSizeParameterThenUseProperty_ParametersEntryIsOverwritten() 293 { 294 ContentDispositionHeaderValue contentDisposition = new ContentDispositionHeaderValue("inline"); 295 296 // Note that uppercase letters are used. Comparison should happen case-insensitive. 297 NameValueHeaderValue sizeParameter = new NameValueHeaderValue("SIZE", "279172874239"); 298 contentDisposition.Parameters.Add(sizeParameter); 299 Assert.Equal(1, contentDisposition.Parameters.Count); 300 Assert.Equal("SIZE", contentDisposition.Parameters.First().Name); 301 Assert.Equal(279172874239, contentDisposition.Size); 302 303 contentDisposition.Size = 279172874240; 304 Assert.Equal(279172874240, contentDisposition.Size); 305 Assert.Equal(1, contentDisposition.Parameters.Count); 306 Assert.Equal("SIZE", contentDisposition.Parameters.First().Name); 307 308 contentDisposition.Parameters.Remove(sizeParameter); 309 Assert.Null(contentDisposition.Size); 310 } 311 312 [Fact] Size_InvalidSizes_PropertyFails()313 public void Size_InvalidSizes_PropertyFails() 314 { 315 ContentDispositionHeaderValue contentDisposition = new ContentDispositionHeaderValue("inline"); 316 317 // Note that uppercase letters are used. Comparison should happen case-insensitive. 318 NameValueHeaderValue sizeParameter = new NameValueHeaderValue("SIZE", "-279172874239"); 319 contentDisposition.Parameters.Add(sizeParameter); 320 Assert.Equal(1, contentDisposition.Parameters.Count); 321 Assert.Equal("SIZE", contentDisposition.Parameters.First().Name); 322 Assert.Null(contentDisposition.Size); 323 324 // Negatives not allowed 325 Assert.Throws<ArgumentOutOfRangeException>(() => { contentDisposition.Size = -279172874240; }); 326 327 Assert.Null(contentDisposition.Size); 328 Assert.Equal(1, contentDisposition.Parameters.Count); 329 Assert.Equal("SIZE", contentDisposition.Parameters.First().Name); 330 331 contentDisposition.Parameters.Remove(sizeParameter); 332 Assert.Null(contentDisposition.Size); 333 } 334 335 [Theory] 336 [InlineData(null)] 337 [InlineData(66)] Size_ValueSetGet_RoundtripsSuccessfully(int? value)338 public void Size_ValueSetGet_RoundtripsSuccessfully(int? value) 339 { 340 var contentDisposition = new ContentDispositionHeaderValue("inline") { Size = value }; 341 Assert.Equal(value, contentDisposition.Size); 342 } 343 344 [Fact] ToString_UseDifferentContentDispositions_AllSerializedCorrectly()345 public void ToString_UseDifferentContentDispositions_AllSerializedCorrectly() 346 { 347 ContentDispositionHeaderValue contentDisposition = new ContentDispositionHeaderValue("inline"); 348 Assert.Equal("inline", contentDisposition.ToString()); 349 350 contentDisposition.Name = "myname"; 351 Assert.Equal("inline; name=myname", contentDisposition.ToString()); 352 353 contentDisposition.FileName = "my File Name"; 354 Assert.Equal("inline; name=myname; filename=\"my File Name\"", contentDisposition.ToString()); 355 356 contentDisposition.CreationDate = new DateTimeOffset(new DateTime(2011, 2, 15, 8, 0, 0, DateTimeKind.Utc)); 357 Assert.Equal("inline; name=myname; filename=\"my File Name\"; creation-date=" 358 + "\"Tue, 15 Feb 2011 08:00:00 GMT\"", contentDisposition.ToString()); 359 360 contentDisposition.Parameters.Add(new NameValueHeaderValue("custom", "\"custom value\"")); 361 Assert.Equal("inline; name=myname; filename=\"my File Name\"; creation-date=" 362 + "\"Tue, 15 Feb 2011 08:00:00 GMT\"; custom=\"custom value\"", contentDisposition.ToString()); 363 364 contentDisposition.Name = null; 365 Assert.Equal("inline; filename=\"my File Name\"; creation-date=" 366 + "\"Tue, 15 Feb 2011 08:00:00 GMT\"; custom=\"custom value\"", contentDisposition.ToString()); 367 368 contentDisposition.FileNameStar = "File%Name"; 369 Assert.Equal("inline; filename=\"my File Name\"; creation-date=" 370 + "\"Tue, 15 Feb 2011 08:00:00 GMT\"; custom=\"custom value\"; filename*=utf-8\'\'File%25Name", 371 contentDisposition.ToString()); 372 373 contentDisposition.FileName = null; 374 Assert.Equal("inline; creation-date=\"Tue, 15 Feb 2011 08:00:00 GMT\"; custom=\"custom value\";" 375 + " filename*=utf-8\'\'File%25Name", contentDisposition.ToString()); 376 377 contentDisposition.CreationDate = null; 378 Assert.Equal("inline; custom=\"custom value\"; filename*=utf-8\'\'File%25Name", 379 contentDisposition.ToString()); 380 } 381 382 [Fact] GetHashCode_UseContentDispositionWithAndWithoutParameters_SameOrDifferentHashCodes()383 public void GetHashCode_UseContentDispositionWithAndWithoutParameters_SameOrDifferentHashCodes() 384 { 385 ContentDispositionHeaderValue contentDisposition1 = new ContentDispositionHeaderValue("inline"); 386 ContentDispositionHeaderValue contentDisposition2 = new ContentDispositionHeaderValue("inline"); 387 contentDisposition2.Name = "myname"; 388 ContentDispositionHeaderValue contentDisposition3 = new ContentDispositionHeaderValue("inline"); 389 contentDisposition3.Parameters.Add(new NameValueHeaderValue("name", "value")); 390 ContentDispositionHeaderValue contentDisposition4 = new ContentDispositionHeaderValue("INLINE"); 391 ContentDispositionHeaderValue contentDisposition5 = new ContentDispositionHeaderValue("INLINE"); 392 contentDisposition5.Parameters.Add(new NameValueHeaderValue("NAME", "MYNAME")); 393 394 Assert.NotEqual(contentDisposition1.GetHashCode(), contentDisposition2.GetHashCode()); // "No params vs. name." 395 Assert.NotEqual(contentDisposition1.GetHashCode(), contentDisposition3.GetHashCode()); // "No params vs. custom param." 396 Assert.NotEqual(contentDisposition2.GetHashCode(), contentDisposition3.GetHashCode()); // "name vs. custom param." 397 Assert.Equal(contentDisposition1.GetHashCode(), contentDisposition4.GetHashCode()); // "Different casing." 398 Assert.Equal(contentDisposition2.GetHashCode(), contentDisposition5.GetHashCode()); // "Different casing in name." 399 } 400 401 [Fact] Equals_UseContentDispositionWithAndWithoutParameters_EqualOrNotEqualNoExceptions()402 public void Equals_UseContentDispositionWithAndWithoutParameters_EqualOrNotEqualNoExceptions() 403 { 404 ContentDispositionHeaderValue contentDisposition1 = new ContentDispositionHeaderValue("inline"); 405 ContentDispositionHeaderValue contentDisposition2 = new ContentDispositionHeaderValue("inline"); 406 contentDisposition2.Name = "myName"; 407 ContentDispositionHeaderValue contentDisposition3 = new ContentDispositionHeaderValue("inline"); 408 contentDisposition3.Parameters.Add(new NameValueHeaderValue("name", "value")); 409 ContentDispositionHeaderValue contentDisposition4 = new ContentDispositionHeaderValue("INLINE"); 410 ContentDispositionHeaderValue contentDisposition5 = new ContentDispositionHeaderValue("INLINE"); 411 contentDisposition5.Parameters.Add(new NameValueHeaderValue("NAME", "MYNAME")); 412 ContentDispositionHeaderValue contentDisposition6 = new ContentDispositionHeaderValue("INLINE"); 413 contentDisposition6.Parameters.Add(new NameValueHeaderValue("NAME", "MYNAME")); 414 contentDisposition6.Parameters.Add(new NameValueHeaderValue("custom", "value")); 415 ContentDispositionHeaderValue contentDisposition7 = new ContentDispositionHeaderValue("attachment"); 416 417 Assert.False(contentDisposition1.Equals(contentDisposition2)); // "No params vs. name." 418 Assert.False(contentDisposition2.Equals(contentDisposition1)); // "name vs. no params." 419 Assert.False(contentDisposition1.Equals(null)); // "No params vs. <null>." 420 Assert.False(contentDisposition1.Equals(contentDisposition3)); // "No params vs. custom param." 421 Assert.False(contentDisposition2.Equals(contentDisposition3)); // "name vs. custom param." 422 Assert.True(contentDisposition1.Equals(contentDisposition4)); // "Different casing." 423 Assert.True(contentDisposition2.Equals(contentDisposition5)); // "Different casing in name." 424 Assert.False(contentDisposition5.Equals(contentDisposition6)); // "name vs. custom param." 425 Assert.False(contentDisposition1.Equals(contentDisposition7)); // "inline vs. text/other." 426 } 427 428 [Fact] Clone_Call_CloneFieldsMatchSourceFields()429 public void Clone_Call_CloneFieldsMatchSourceFields() 430 { 431 ContentDispositionHeaderValue source = new ContentDispositionHeaderValue("attachment"); 432 ContentDispositionHeaderValue clone = (ContentDispositionHeaderValue)((ICloneable)source).Clone(); 433 Assert.Equal(source.DispositionType, clone.DispositionType); 434 Assert.Equal(0, clone.Parameters.Count); 435 436 source.Name = "myName"; 437 clone = (ContentDispositionHeaderValue)((ICloneable)source).Clone(); 438 Assert.Equal(source.DispositionType, clone.DispositionType); 439 Assert.Equal("myName", clone.Name); 440 Assert.Equal(1, clone.Parameters.Count); 441 442 source.Parameters.Add(new NameValueHeaderValue("custom", "customValue")); 443 clone = (ContentDispositionHeaderValue)((ICloneable)source).Clone(); 444 Assert.Equal(source.DispositionType, clone.DispositionType); 445 Assert.Equal("myName", clone.Name); 446 Assert.Equal(2, clone.Parameters.Count); 447 Assert.Equal("custom", clone.Parameters.ElementAt(1).Name); 448 Assert.Equal("customValue", clone.Parameters.ElementAt(1).Value); 449 } 450 451 [Fact] GetDispositionTypeLength_DifferentValidScenarios_AllReturnNonZero()452 public void GetDispositionTypeLength_DifferentValidScenarios_AllReturnNonZero() 453 { 454 object result = null; 455 ContentDispositionHeaderValue value = null; 456 457 Assert.Equal(7, ContentDispositionHeaderValue.GetDispositionTypeLength("inline , other/name", 0, 458 out result)); 459 value = (ContentDispositionHeaderValue)result; 460 Assert.Equal("inline", value.DispositionType); 461 Assert.Equal(0, value.Parameters.Count); 462 463 Assert.Equal(6, ContentDispositionHeaderValue.GetDispositionTypeLength("inline", 0, out result)); 464 value = (ContentDispositionHeaderValue)result; 465 Assert.Equal("inline", value.DispositionType); 466 Assert.Equal(0, value.Parameters.Count); 467 468 Assert.Equal(19, ContentDispositionHeaderValue.GetDispositionTypeLength("inline; name=MyName", 0, 469 out result)); 470 value = (ContentDispositionHeaderValue)result; 471 Assert.Equal("inline", value.DispositionType); 472 Assert.Equal("MyName", value.Name); 473 Assert.Equal(1, value.Parameters.Count); 474 475 Assert.Equal(32, ContentDispositionHeaderValue.GetDispositionTypeLength(" inline; custom=value;name=myName", 476 1, out result)); 477 value = (ContentDispositionHeaderValue)result; 478 Assert.Equal("inline", value.DispositionType); 479 Assert.Equal("myName", value.Name); 480 Assert.Equal(2, value.Parameters.Count); 481 482 Assert.Equal(14, ContentDispositionHeaderValue.GetDispositionTypeLength(" inline; custom, next", 483 1, out result)); 484 value = (ContentDispositionHeaderValue)result; 485 Assert.Equal("inline", value.DispositionType); 486 Assert.Null(value.Name); 487 Assert.Equal(1, value.Parameters.Count); 488 Assert.Equal("custom", value.Parameters.ElementAt(0).Name); 489 Assert.Null(value.Parameters.ElementAt(0).Value); 490 491 Assert.Equal(40, ContentDispositionHeaderValue.GetDispositionTypeLength( 492 "inline ; custom =\r\n \"x\" ; name = myName , next", 0, out result)); 493 value = (ContentDispositionHeaderValue)result; 494 Assert.Equal("inline", value.DispositionType); 495 Assert.Equal("myName", value.Name); 496 Assert.Equal(2, value.Parameters.Count); 497 Assert.Equal("custom", value.Parameters.ElementAt(0).Name); 498 Assert.Equal("\"x\"", value.Parameters.ElementAt(0).Value); 499 Assert.Equal("name", value.Parameters.ElementAt(1).Name); 500 Assert.Equal("myName", value.Parameters.ElementAt(1).Value); 501 502 Assert.Equal(29, ContentDispositionHeaderValue.GetDispositionTypeLength( 503 "inline;custom=\"x\";name=myName,next", 0, out result)); 504 value = (ContentDispositionHeaderValue)result; 505 Assert.Equal("inline", value.DispositionType); 506 Assert.Equal("myName", value.Name); 507 Assert.Equal(2, value.Parameters.Count); 508 Assert.Equal("custom", value.Parameters.ElementAt(0).Name); 509 Assert.Equal("\"x\"", value.Parameters.ElementAt(0).Value); 510 Assert.Equal("name", value.Parameters.ElementAt(1).Name); 511 Assert.Equal("myName", value.Parameters.ElementAt(1).Value); 512 } 513 514 [Fact] GetDispositionTypeLength_DifferentInvalidScenarios_AllReturnZero()515 public void GetDispositionTypeLength_DifferentInvalidScenarios_AllReturnZero() 516 { 517 object result = null; 518 519 Assert.Equal(0, ContentDispositionHeaderValue.GetDispositionTypeLength(" inline", 0, out result)); 520 Assert.Null(result); 521 Assert.Equal(0, ContentDispositionHeaderValue.GetDispositionTypeLength("inline;", 0, out result)); 522 Assert.Null(result); 523 Assert.Equal(0, ContentDispositionHeaderValue.GetDispositionTypeLength("inline;name=", 0, out result)); 524 Assert.Null(result); 525 Assert.Equal(0, ContentDispositionHeaderValue.GetDispositionTypeLength("inline;name=value;", 0, out result)); 526 Assert.Null(result); 527 Assert.Equal(0, ContentDispositionHeaderValue.GetDispositionTypeLength("inline;", 0, out result)); 528 Assert.Null(result); 529 Assert.Equal(0, ContentDispositionHeaderValue.GetDispositionTypeLength(null, 0, out result)); 530 Assert.Null(result); 531 Assert.Equal(0, ContentDispositionHeaderValue.GetDispositionTypeLength(string.Empty, 0, out result)); 532 Assert.Null(result); 533 } 534 535 [Fact] Parse_SetOfValidValueStrings_ParsedCorrectly()536 public void Parse_SetOfValidValueStrings_ParsedCorrectly() 537 { 538 ContentDispositionHeaderValue expected = new ContentDispositionHeaderValue("inline"); 539 CheckValidParse("\r\n inline ", expected); 540 CheckValidParse("inline", expected); 541 542 // We don't have to test all possible input strings, since most of the pieces are handled by other parsers. 543 // The purpose of this test is to verify that these other parsers are combined correctly to build a 544 // Content-Disposition parser. 545 expected.Name = "myName"; 546 CheckValidParse("\r\n inline ; name = myName ", expected); 547 CheckValidParse(" inline;name=myName", expected); 548 549 expected.Name = null; 550 expected.DispositionType = "attachment"; 551 expected.FileName = "foo-ae.html"; 552 expected.Parameters.Add(new NameValueHeaderValue("filename*", "UTF-8''foo-%c3%a4.html")); 553 CheckValidParse(@"attachment; filename*=UTF-8''foo-%c3%a4.html; filename=foo-ae.html", expected); 554 } 555 556 [Fact] Parse_SetOfInvalidValueStrings_Throws()557 public void Parse_SetOfInvalidValueStrings_Throws() 558 { 559 CheckInvalidParse(""); 560 CheckInvalidParse(" "); 561 CheckInvalidParse(null); 562 CheckInvalidParse("inline\u4F1A"); 563 CheckInvalidParse("inline ,"); 564 CheckInvalidParse("inline,"); 565 CheckInvalidParse("inline; name=myName ,"); 566 CheckInvalidParse("inline; name=myName,"); 567 CheckInvalidParse("inline; name=my\u4F1AName"); 568 CheckInvalidParse("inline/"); 569 } 570 571 [Fact] TryParse_SetOfValidValueStrings_ParsedCorrectly()572 public void TryParse_SetOfValidValueStrings_ParsedCorrectly() 573 { 574 ContentDispositionHeaderValue expected = new ContentDispositionHeaderValue("inline"); 575 CheckValidTryParse("\r\n inline ", expected); 576 CheckValidTryParse("inline", expected); 577 578 // We don't have to test all possible input strings, since most of the pieces are handled by other parsers. 579 // The purpose of this test is to verify that these other parsers are combined correctly to build a 580 // Content-Disposition parser. 581 expected.Name = "myName"; 582 CheckValidTryParse("\r\n inline ; name = myName ", expected); 583 CheckValidTryParse(" inline;name=myName", expected); 584 } 585 586 [Fact] TryParse_SetOfInvalidValueStrings_ReturnsFalse()587 public void TryParse_SetOfInvalidValueStrings_ReturnsFalse() 588 { 589 CheckInvalidTryParse(""); 590 CheckInvalidTryParse(" "); 591 CheckInvalidTryParse(null); 592 CheckInvalidTryParse("inline\u4F1A"); 593 CheckInvalidTryParse("inline ,"); 594 CheckInvalidTryParse("inline,"); 595 CheckInvalidTryParse("inline; name=myName ,"); 596 CheckInvalidTryParse("inline; name=myName,"); 597 CheckInvalidTryParse("text/"); 598 } 599 600 #region Tests from HenrikN 601 602 603 private static Dictionary<string, ContentDispositionValue> ContentDispositionTestCases = new Dictionary<string, ContentDispositionValue>() 604 { 605 // Valid values 606 { "valid1", new ContentDispositionValue(@"inline", @"This should be equivalent to not including the header at all.", true) }, 607 { "valid2", new ContentDispositionValue(@"inline; filename=""foo.html""", @"'inline', specifying a filename of foo.html", true) }, 608 { "valid3", new ContentDispositionValue(@"inline; filename=""Not an attachment!""", @"'inline', specifying a filename of Not an attachment! - this checks for proper parsing for disposition types.", true) }, 609 { "valid4", new ContentDispositionValue(@"inline; filename=""foo.pdf""", @"'inline', specifying a filename of foo.pdf", true) }, 610 { "valid5", new ContentDispositionValue(@"attachment", @"'attachment' only", true) }, 611 { "valid6", new ContentDispositionValue(@"ATTACHMENT", @"'ATTACHMENT' only", true) }, 612 { "valid7", new ContentDispositionValue(@"attachment; filename=""foo.html""", @"'attachment', specifying a filename of foo.html", true) }, 613 { "valid8", new ContentDispositionValue(@"attachment; filename=""f\oo.html""", @"'attachment', specifying a filename of f\oo.html (the first 'o' being escaped)", true) }, 614 { "valid9", new ContentDispositionValue(@"attachment; filename=""\""quoting\"" tested.html""", @"'attachment', specifying a filename of \""quoting\"" tested.html (using double quotes around ""quoting"" to test... quoting)", true) }, 615 { "valid10", new ContentDispositionValue(@"attachment; filename=""Here's a semicolon;.html""", @"'attachment', specifying a filename of Here's a semicolon;.html - this checks for proper parsing for parameters. ", true) }, 616 { "valid11", new ContentDispositionValue(@"attachment; foo=""bar""; filename=""foo.html""", @"'attachment', specifying a filename of foo.html and an extension parameter ""foo"" which should be ignored (see <a href=""http://greenbytes.de/tech/webdav/rfc2183.html#rfc.section.2.8"">Section 2.8 of RFC 2183</a>.).", true) }, 617 { "valid12", new ContentDispositionValue(@"attachment; foo=""\""\\"";filename=""foo.html""", @"'attachment', specifying a filename of foo.html and an extension parameter ""foo"" which should be ignored (see <a href=""http://greenbytes.de/tech/webdav/rfc2183.html#rfc.section.2.8"">Section 2.8 of RFC 2183</a>.). The extension parameter actually uses backslash-escapes. This tests whether the UA properly skips the parameter.", true) }, 618 { "valid13", new ContentDispositionValue(@"attachment; FILENAME=""foo.html""", @"'attachment', specifying a filename of foo.html", true) }, 619 { "valid14", new ContentDispositionValue(@"attachment; filename=foo.html", @"'attachment', specifying a filename of foo.html using a token instead of a quoted-string.", true) }, 620 { "valid15", new ContentDispositionValue(@"attachment; filename='foo.bar'", @"'attachment', specifying a filename of 'foo.bar' using single quotes. ", true) }, 621 { "valid16", new ContentDispositionValue(@"attachment; filename=""foo-\u00E4.html""", @"'attachment', specifying a filename of foo-\u00E4.html, using plain ISO-8859-1", true) }, 622 { "valid17", new ContentDispositionValue(@"attachment; filename=""foo-ä.html""", @"'attachment', specifying a filename of foo-ä.html, which happens to be foo-\u00E4.html using UTF-8 encoding.", true) }, 623 { "valid18", new ContentDispositionValue(@"attachment; filename=""foo-%41.html""", @"'attachment', specifying a filename of foo-%41.html", true) }, 624 { "valid19", new ContentDispositionValue(@"attachment; filename=""50%.html""", @"'attachment', specifying a filename of 50%.html", true) }, 625 { "valid20", new ContentDispositionValue(@"attachment; filename=""foo-%\41.html""", @"'attachment', specifying a filename of foo-%41.html, using an escape character (this tests whether adding an escape character inside a %xx sequence can be used to disable the non-conformant %xx-unescaping).", true) }, 626 { "valid21", new ContentDispositionValue(@"attachment; name=""foo-%41.html""", @"'attachment', specifying a <i>name</i> parameter of foo-%41.html. (this test was added to observe the behavior of the (unspecified) treatment of ""name"" as synonym for ""filename""; see <a href=""http://www.imc.org/ietf-smtp/mail-archive/msg05023.html"">Ned Freed's summary</a> where this comes from in MIME messages)", true) }, 627 { "valid22", new ContentDispositionValue(@"attachment; filename=""\u00E4-%41.html""", @"'attachment', specifying a filename parameter of \u00E4-%41.html. (this test was added to observe the behavior when non-ASCII characters and percent-hexdig sequences are combined)", true) }, 628 { "valid23", new ContentDispositionValue(@"attachment; filename=""foo-%c3%a4-%e2%82%ac.html""", @"'attachment', specifying a filename of foo-%c3%a4-%e2%82%ac.html, using raw percent encoded UTF-8 to represent foo-\u00E4-€.html", true) }, 629 { "valid24", new ContentDispositionValue(@"attachment; filename =""foo.html""", @"'attachment', specifying a filename of foo.html, with one blank space <em>before</em> the equals character.", true) }, 630 { "valid25", new ContentDispositionValue(@"attachment; xfilename=foo.html", @"'attachment', specifying an ""xfilename"" parameter.", true) }, 631 { "valid26", new ContentDispositionValue(@"attachment; filename=""/foo.html""", @"'attachment', specifying an absolute filename in the filesystem root.", true) }, 632 { "valid27", new ContentDispositionValue(@"attachment; filename=""\\foo.html""", @"'attachment', specifying an absolute filename in the filesystem root.", true) }, 633 { "valid28", new ContentDispositionValue(@"attachment; creation-date=""Wed, 12 Feb 1997 16:29:51 -0500""", @"'attachment', plus creation-date (see <a href=""http://greenbytes.de/tech/webdav/rfc2183.html#rfc.section.2.4"">Section 2.4 of RFC 2183</a>)", true) }, 634 { "valid29", new ContentDispositionValue(@"attachment; modification-date=""Wed, 12 Feb 1997 16:29:51 -0500""", @"'attachment', plus modification-date (see <a href=""http://greenbytes.de/tech/webdav/rfc2183.html#rfc.section.2.5"">Section 2.5 of RFC 2183</a>)", true) }, 635 { "valid30", new ContentDispositionValue(@"foobar", @"This should be equivalent to using ""attachment"".", true) }, 636 { "valid31", new ContentDispositionValue(@"attachment; example=""filename=example.txt""", @"'attachment', with no filename parameter", true) }, 637 { "valid32", new ContentDispositionValue(@"attachment; filename*=iso-8859-1''foo-%E4.html", @"'attachment', specifying a filename of foo-\u00E4.html, using RFC2231 encoded ISO-8859-1", true) }, 638 { "valid33", new ContentDispositionValue(@"attachment; filename*=UTF-8''foo-%c3%a4-%e2%82%ac.html", @"'attachment', specifying a filename of foo-\u00E4-€.html, using RFC2231 encoded UTF-8", true) }, 639 { "valid34", new ContentDispositionValue(@"attachment; filename*=''foo-%c3%a4-%e2%82%ac.html", @"Behavior is undefined in RFC 2231, the charset part is missing, although UTF-8 was used.", true) }, 640 { "valid35", new ContentDispositionValue(@"attachment; filename*=UTF-8''foo-a%cc%88.html", @"'attachment', specifying a filename of foo-\u00E4.html, using RFC2231 encoded UTF-8, but choosing the decomposed form (lowercase a plus COMBINING DIAERESIS) -- on a Windows target system, this should be translated to the preferred Unicode normal form (composed).", true) }, 641 { "valid36", new ContentDispositionValue(@"attachment; filename*= UTF-8''foo-%c3%a4.html", @"'attachment', specifying a filename of foo-\u00E4.html, using RFC2231 encoded UTF-8, with whitespace after ""*=""", true) }, 642 { "valid37", new ContentDispositionValue(@"attachment; filename* =UTF-8''foo-%c3%a4.html", @"'attachment', specifying a filename of foo-\u00E4.html, using RFC2231 encoded UTF-8, with whitespace inside ""* =""", true) }, 643 { "valid38", new ContentDispositionValue(@"attachment; filename*=UTF-8''A-%2541.html", @"'attachment', specifying a filename of A-%41.html, using RFC2231 encoded UTF-8.", true) }, 644 { "valid39", new ContentDispositionValue(@"attachment; filename*=UTF-8''%5cfoo.html", @"'attachment', specifying a filename of /foo.html, using RFC2231 encoded UTF-8.", true) }, 645 { "valid40", new ContentDispositionValue(@"attachment; filename*0=""foo.""; filename*1=""html""", @"'attachment', specifying a filename of foo.html, using RFC2231-style parameter continuations.", true) }, 646 { "valid41", new ContentDispositionValue(@"attachment; filename*0*=UTF-8''foo-%c3%a4; filename*1="".html""", @"'attachment', specifying a filename of foo-\u00E4.html, using both RFC2231-style parameter continuations and UTF-8 encoding.", true) }, 647 { "valid42", new ContentDispositionValue(@"attachment; filename*0=""foo""; filename*01=""bar""", @"'attachment', specifying a filename of foo (the parameter filename*01 should be ignored because of the leading zero)", true) }, 648 { "valid43", new ContentDispositionValue(@"attachment; filename*0=""foo""; filename*2=""bar""", @"'attachment', specifying a filename of foo (the parameter filename*2 should be ignored because there's no filename*1 parameter)", true) }, 649 { "valid44", new ContentDispositionValue(@"attachment; filename*1=""foo.""; filename*2=""html""", @"'attachment' (the filename* parameters should be ignored because filename*0 is missing)", true) }, 650 { "valid45", new ContentDispositionValue(@"attachment; filename*1=""bar""; filename*0=""foo""", "'attachment', specifying a filename of foobar", true) }, 651 { "valid46", new ContentDispositionValue(@"attachment; filename=""foo-ae.html""; filename*=UTF-8''foo-%c3%a4.html", @"'attachment', specifying a filename of foo-ae.html in the traditional format, and foo-\u00E4.html in RFC2231 format.", true) }, 652 { "valid47", new ContentDispositionValue(@"attachment; filename*=UTF-8''foo-%c3%a4.html; filename=""foo-ae.html""", @"'attachment', specifying a filename of foo-ae.html in the traditional format, and foo-\u00E4.html in RFC2231 format.", true) }, 653 { "valid48", new ContentDispositionValue(@"attachment; foobar=x; filename=""foo.html""", @"'attachment', specifying a new parameter ""foobar"", plus a filename of foo.html in the traditional format.", true) }, 654 { "valid49", new ContentDispositionValue(@"attachment; filename=""=?ISO-8859-1?Q?foo-=E4.html?=""", @"attachment; filename=""=?ISO-8859-1?Q?foo-=E4.html?=""", true) }, 655 { "valid50", new ContentDispositionValue(@"attachment; filename=""=?utf-8?B?Zm9vLeQuaHRtbA==?=""", @"attachment; filename=""=?utf-8?B?Zm9vLeQuaHRtbA==?=""", true) }, 656 657 // Invalid values 658 { "invalid1", new ContentDispositionValue(@"""inline""", @"'inline' only, using double quotes", false) }, 659 { "invalid2", new ContentDispositionValue(@"""attachment""", @"'attachment' only, using double quotes", false) }, 660 { "invalid3", new ContentDispositionValue(@"attachment; filename=foo.html ;", @"'attachment', specifying a filename of foo.html using a token instead of a quoted-string, and adding a trailing semicolon.", false) }, 661 { "invalid4", new ContentDispositionValue(@"attachment; filename=foo bar.html", @"'attachment', specifying a filename of foo bar.html without using quoting.", false) }, 662 { "invalid6", new ContentDispositionValue(@"attachment; filename=foo[1](2).html", @"'attachment', specifying a filename of foo[1](2).html, but missing the quotes. Also, ""["", ""]"", ""("" and "")"" are not allowed in the HTTP <a href=""http://greenbytes.de/tech/webdav/draft-ietf-httpbis-p1-messaging-latest.html#rfc.section.1.2.2"">token</a> production.", false) }, 663 { "invalid7", new ContentDispositionValue(@"attachment; filename=foo-\u00E4.html", @"'attachment', specifying a filename of foo-\u00E4.html, but missing the quotes.", false) }, 664 { "invalid9", new ContentDispositionValue(@"filename=foo.html", @"Disposition type missing, filename specified.", false) }, 665 { "invalid10", new ContentDispositionValue(@"x=y; filename=foo.html", @"Disposition type missing, filename specified after extension parameter.", false) }, 666 { "invalid11", new ContentDispositionValue(@"""foo; filename=bar;baz""; filename=qux", @"Disposition type missing, filename ""qux"". Can it be more broken? (Probably)", false) }, 667 { "invalid12", new ContentDispositionValue(@"filename=foo.html, filename=bar.html", @"Disposition type missing, two filenames specified separated by a comma (this is syntactically equivalent to have two instances of the header with one filename parameter each).", false) }, 668 { "invalid13", new ContentDispositionValue(@"; filename=foo.html", @"Disposition type missing (but delimiter present), filename specified.", false) }, 669 { "invalid16", new ContentDispositionValue(@"attachment; filename=""foo.html"".txt", @"'attachment', specifying a filename parameter that is broken (quoted-string followed by more characters). This is invalid syntax. ", false) }, 670 { "invalid17", new ContentDispositionValue(@"attachment; filename=""bar", @"'attachment', specifying a filename parameter that is broken (missing ending double quote). This is invalid syntax.", false) }, 671 { "invalid18", new ContentDispositionValue(@"attachment; filename=foo""bar;baz""qux", @"'attachment', specifying a filename parameter that is broken (disallowed characters in token syntax). This is invalid syntax.", false) }, 672 { "invalid19", new ContentDispositionValue(@"attachment; filename=foo.html, attachment; filename=bar.html", @"'attachment', two comma-separated instances of the header field. As Content-Disposition doesn't use a list-style syntax, this is invalid syntax and, according to <a href=""http://greenbytes.de/tech/webdav/rfc2616.html#rfc.section.4.2.p.5"">RFC 2616, Section 4.2</a>, roughly equivalent to having two separate header field instances.", false) }, 673 { "invalid20", new ContentDispositionValue(@"filename=foo.html; attachment", @"filename parameter and disposition type reversed.", false) }, 674 { "invalid24", new ContentDispositionValue(@"attachment; filename==?ISO-8859-1?Q?foo-=E4.html?=", @"Uses RFC 2047 style encoded word. ""="" is invalid inside the token production, so this is invalid.", false) }, 675 { "invalid25", new ContentDispositionValue(@"attachment; filename==?utf-8?B?Zm9vLeQuaHRtbA==?=", @"Uses RFC 2047 style encoded word. ""="" is invalid inside the token production, so this is invalid.", false) }, 676 }; 677 678 #region Parsing 679 680 [Fact] 681 public void ContentDispositionHeaderValue_Parse_ExpectedResult() 682 { 683 foreach (var cd in ContentDispositionTestCases.Values) 684 { 685 ContentDispositionHeaderValue header = Parse(cd); 686 } 687 } 688 689 [Fact] 690 public void ContentDispositionHeaderValue_TryParse_ExpectedResult() 691 { 692 foreach (var cd in ContentDispositionTestCases.Values) 693 { 694 ContentDispositionHeaderValue header = TryParse(cd); 695 } 696 } 697 698 [Fact] 699 public void ContentDispositionHeader_Valid1_Success() 700 { 701 ContentDispositionValue cd = ContentDispositionTestCases["valid1"]; 702 ContentDispositionHeaderValue header = TryParse(cd); 703 ValidateHeaderValues(header, "inline", null); 704 } 705 706 [Fact] 707 public void ContentDispositionHeader_Valid2_Success() 708 { 709 ContentDispositionValue cd = ContentDispositionTestCases["valid2"]; 710 ContentDispositionHeaderValue header = TryParse(cd); 711 ValidateHeaderValues(header, "inline", @"""foo.html"""); 712 } 713 714 [Fact] 715 public void ContentDispositionHeader_Valid3_Success() 716 { 717 ContentDispositionValue cd = ContentDispositionTestCases["valid3"]; 718 ContentDispositionHeaderValue header = TryParse(cd); 719 ValidateHeaderValues(header, "inline", @"""Not an attachment!"""); 720 } 721 722 [Fact] 723 public void ContentDispositionHeader_Valid4_Success() 724 { 725 ContentDispositionValue cd = ContentDispositionTestCases["valid4"]; 726 ContentDispositionHeaderValue header = TryParse(cd); 727 ValidateHeaderValues(header, "inline", @"""foo.pdf"""); 728 } 729 730 [Fact] 731 public void ContentDispositionHeader_Valid5_Success() 732 { 733 ContentDispositionValue cd = ContentDispositionTestCases["valid5"]; 734 ContentDispositionHeaderValue header = TryParse(cd); 735 ValidateHeaderValues(header, "attachment", null); 736 } 737 738 [Fact] 739 public void ContentDispositionHeader_Valid6_Success() 740 { 741 ContentDispositionValue cd = ContentDispositionTestCases["valid6"]; 742 ContentDispositionHeaderValue header = TryParse(cd); 743 ValidateHeaderValues(header, "ATTACHMENT", null); 744 } 745 746 [Fact] 747 public void ContentDispositionHeader_Valid7_Success() 748 { 749 ContentDispositionValue cd = ContentDispositionTestCases["valid7"]; 750 ContentDispositionHeaderValue header = TryParse(cd); 751 ValidateHeaderValues(header, "attachment", @"""foo.html"""); 752 } 753 754 [Fact] 755 public void ContentDispositionHeader_Valid8_Success() 756 { 757 ContentDispositionValue cd = ContentDispositionTestCases["valid8"]; 758 ContentDispositionHeaderValue header = TryParse(cd); 759 ValidateHeaderValues(header, "attachment", @"""f\oo.html"""); 760 } 761 762 [Fact] 763 public void ContentDispositionHeader_Valid9_Success() 764 { 765 ContentDispositionValue cd = ContentDispositionTestCases["valid9"]; 766 ContentDispositionHeaderValue header = TryParse(cd); 767 ValidateHeaderValues(header, "attachment", @"""\""quoting\"" tested.html"""); 768 } 769 770 [Fact] 771 public void ContentDispositionHeader_Valid10_Success() 772 { 773 ContentDispositionValue cd = ContentDispositionTestCases["valid10"]; 774 ContentDispositionHeaderValue header = TryParse(cd); 775 ValidateHeaderValues(header, "attachment", @"""Here's a semicolon;.html"""); 776 } 777 778 [Fact] 779 public void ContentDispositionHeader_Valid11_Success() 780 { 781 ContentDispositionValue cd = ContentDispositionTestCases["valid11"]; 782 ContentDispositionHeaderValue header = TryParse(cd); 783 ValidateHeaderValues(header, "attachment", @"""foo.html"""); 784 ValidateExtensionParameter(header, "foo", @"""bar"""); 785 } 786 787 [Fact] 788 public void ContentDispositionHeader_Valid12_Success() 789 { 790 ContentDispositionValue cd = ContentDispositionTestCases["valid12"]; 791 ContentDispositionHeaderValue header = TryParse(cd); 792 ValidateHeaderValues(header, "attachment", @"""foo.html"""); 793 ValidateExtensionParameter(header, "foo", @"""\""\\"""); 794 } 795 796 [Fact] 797 public void ContentDispositionHeader_Valid13_Success() 798 { 799 ContentDispositionValue cd = ContentDispositionTestCases["valid13"]; 800 ContentDispositionHeaderValue header = TryParse(cd); 801 ValidateHeaderValues(header, "attachment", @"""foo.html"""); 802 } 803 804 [Fact] 805 public void ContentDispositionHeader_Valid14_Success() 806 { 807 ContentDispositionValue cd = ContentDispositionTestCases["valid14"]; 808 ContentDispositionHeaderValue header = TryParse(cd); 809 ValidateHeaderValues(header, "attachment", @"foo.html"); 810 } 811 812 [Fact] 813 public void ContentDispositionHeader_Valid15_Success() 814 { 815 ContentDispositionValue cd = ContentDispositionTestCases["valid15"]; 816 ContentDispositionHeaderValue header = TryParse(cd); 817 ValidateHeaderValues(header, "attachment", @"'foo.bar'"); 818 } 819 820 [Fact] 821 public void ContentDispositionHeader_Valid16_Success() 822 { 823 ContentDispositionValue cd = ContentDispositionTestCases["valid16"]; 824 ContentDispositionHeaderValue header = TryParse(cd); 825 ValidateHeaderValues(header, "attachment", @"""foo-\u00E4.html"""); 826 } 827 828 [Fact] 829 public void ContentDispositionHeader_Valid17_Success() 830 { 831 ContentDispositionValue cd = ContentDispositionTestCases["valid17"]; 832 ContentDispositionHeaderValue header = TryParse(cd); 833 ValidateHeaderValues(header, "attachment", @"""foo-ä.html"""); 834 } 835 836 [Fact] 837 public void ContentDispositionHeader_Valid18_Success() 838 { 839 ContentDispositionValue cd = ContentDispositionTestCases["valid18"]; 840 ContentDispositionHeaderValue header = TryParse(cd); 841 ValidateHeaderValues(header, "attachment", @"""foo-%41.html"""); 842 } 843 844 [Fact] 845 public void ContentDispositionHeader_Valid19_Success() 846 { 847 ContentDispositionValue cd = ContentDispositionTestCases["valid19"]; 848 ContentDispositionHeaderValue header = TryParse(cd); 849 ValidateHeaderValues(header, "attachment", @"""50%.html"""); 850 } 851 852 [Fact] 853 public void ContentDispositionHeader_Valid20_Success() 854 { 855 ContentDispositionValue cd = ContentDispositionTestCases["valid20"]; 856 ContentDispositionHeaderValue header = TryParse(cd); 857 ValidateHeaderValues(header, "attachment", @"""foo-%\41.html"""); 858 } 859 860 [Fact] 861 public void ContentDispositionHeader_Valid21_Success() 862 { 863 ContentDispositionValue cd = ContentDispositionTestCases["valid21"]; 864 ContentDispositionHeaderValue header = TryParse(cd); 865 ValidateHeaderValues(header, "attachment", null); 866 ValidateExtensionParameter(header, "name", @"""foo-%41.html"""); 867 } 868 869 [Fact] 870 public void ContentDispositionHeader_Valid22_Success() 871 { 872 ContentDispositionValue cd = ContentDispositionTestCases["valid22"]; 873 ContentDispositionHeaderValue header = TryParse(cd); 874 ValidateHeaderValues(header, "attachment", @"""\u00E4-%41.html"""); 875 } 876 877 [Fact] 878 public void ContentDispositionHeader_Valid23_Success() 879 { 880 ContentDispositionValue cd = ContentDispositionTestCases["valid23"]; 881 ContentDispositionHeaderValue header = TryParse(cd); 882 ValidateHeaderValues(header, "attachment", @"""foo-%c3%a4-%e2%82%ac.html"""); 883 } 884 885 [Fact] 886 public void ContentDispositionHeader_Valid24_Success() 887 { 888 ContentDispositionValue cd = ContentDispositionTestCases["valid24"]; 889 ContentDispositionHeaderValue header = TryParse(cd); 890 ValidateHeaderValues(header, "attachment", @"""foo.html"""); 891 } 892 893 [Fact] 894 public void ContentDispositionHeader_Valid25_Success() 895 { 896 ContentDispositionValue cd = ContentDispositionTestCases["valid25"]; 897 ContentDispositionHeaderValue header = TryParse(cd); 898 ValidateHeaderValues(header, "attachment", null); 899 ValidateExtensionParameter(header, "xfilename", @"foo.html"); 900 } 901 902 [Fact] 903 public void ContentDispositionHeader_Valid26_Success() 904 { 905 ContentDispositionValue cd = ContentDispositionTestCases["valid26"]; 906 ContentDispositionHeaderValue header = TryParse(cd); 907 ValidateHeaderValues(header, "attachment", @"""/foo.html"""); 908 } 909 910 [Fact] 911 public void ContentDispositionHeader_Valid27_Success() 912 { 913 ContentDispositionValue cd = ContentDispositionTestCases["valid27"]; 914 ContentDispositionHeaderValue header = TryParse(cd); 915 ValidateHeaderValues(header, "attachment", @"""\\foo.html"""); 916 } 917 918 [Fact] 919 public void ContentDispositionHeader_Valid28_Success() 920 { 921 ContentDispositionValue cd = ContentDispositionTestCases["valid28"]; 922 ContentDispositionHeaderValue header = TryParse(cd); 923 ValidateHeaderValues(header, "attachment", null); 924 ValidateExtensionParameter(header, "creation-date", @"""Wed, 12 Feb 1997 16:29:51 -0500"""); 925 } 926 927 [Fact] 928 public void ContentDispositionHeader_Valid29_Success() 929 { 930 ContentDispositionValue cd = ContentDispositionTestCases["valid29"]; 931 ContentDispositionHeaderValue header = TryParse(cd); 932 ValidateHeaderValues(header, "attachment", null); 933 ValidateExtensionParameter(header, "modification-date", @"""Wed, 12 Feb 1997 16:29:51 -0500"""); 934 } 935 936 [Fact] 937 public void ContentDispositionHeader_Valid30_Success() 938 { 939 ContentDispositionValue cd = ContentDispositionTestCases["valid30"]; 940 ContentDispositionHeaderValue header = TryParse(cd); 941 ValidateHeaderValues(header, "foobar", null); 942 } 943 944 [Fact] 945 public void ContentDispositionHeader_Valid31_Success() 946 { 947 ContentDispositionValue cd = ContentDispositionTestCases["valid31"]; 948 ContentDispositionHeaderValue header = TryParse(cd); 949 ValidateHeaderValues(header, "attachment", null); 950 ValidateExtensionParameter(header, "example", @"""filename=example.txt"""); 951 } 952 953 [Fact] 954 public void ContentDispositionHeader_Valid32_Success() 955 { 956 ContentDispositionValue cd = ContentDispositionTestCases["valid32"]; 957 ContentDispositionHeaderValue header = TryParse(cd); 958 ValidateHeaderValues(header, "attachment", null); 959 ValidateExtensionParameter(header, "filename*", @"iso-8859-1''foo-%E4.html"); 960 } 961 962 [Fact] 963 public void ContentDispositionHeader_Valid33_Success() 964 { 965 ContentDispositionValue cd = ContentDispositionTestCases["valid33"]; 966 ContentDispositionHeaderValue header = TryParse(cd); 967 ValidateHeaderValues(header, "attachment", null); 968 ValidateExtensionParameter(header, "filename*", @"UTF-8''foo-%c3%a4-%e2%82%ac.html"); 969 } 970 971 [Fact] 972 public void ContentDispositionHeader_Valid34_Success() 973 { 974 ContentDispositionValue cd = ContentDispositionTestCases["valid34"]; 975 ContentDispositionHeaderValue header = TryParse(cd); 976 ValidateHeaderValues(header, "attachment", null); 977 ValidateExtensionParameter(header, "filename*", @"''foo-%c3%a4-%e2%82%ac.html"); 978 } 979 980 [Fact] 981 public void ContentDispositionHeader_Valid35_Success() 982 { 983 ContentDispositionValue cd = ContentDispositionTestCases["valid35"]; 984 ContentDispositionHeaderValue header = TryParse(cd); 985 ValidateHeaderValues(header, "attachment", null); 986 ValidateExtensionParameter(header, "filename*", @"UTF-8''foo-a%cc%88.html"); 987 } 988 989 [Fact] 990 public void ContentDispositionHeader_Valid36_Success() 991 { 992 ContentDispositionValue cd = ContentDispositionTestCases["valid36"]; 993 ContentDispositionHeaderValue header = TryParse(cd); 994 ValidateHeaderValues(header, "attachment", null); 995 ValidateExtensionParameter(header, "filename*", @"UTF-8''foo-%c3%a4.html"); 996 } 997 998 [Fact] 999 public void ContentDispositionHeader_Valid37_Success() 1000 { 1001 ContentDispositionValue cd = ContentDispositionTestCases["valid37"]; 1002 ContentDispositionHeaderValue header = TryParse(cd); 1003 ValidateHeaderValues(header, "attachment", null); 1004 ValidateExtensionParameter(header, "filename*", @"UTF-8''foo-%c3%a4.html"); 1005 } 1006 1007 [Fact] 1008 public void ContentDispositionHeader_Valid38_Success() 1009 { 1010 ContentDispositionValue cd = ContentDispositionTestCases["valid38"]; 1011 ContentDispositionHeaderValue header = TryParse(cd); 1012 ValidateHeaderValues(header, "attachment", null); 1013 ValidateExtensionParameter(header, "filename*", @"UTF-8''A-%2541.html"); 1014 } 1015 1016 [Fact] 1017 public void ContentDispositionHeader_Valid39_Success() 1018 { 1019 ContentDispositionValue cd = ContentDispositionTestCases["valid39"]; 1020 ContentDispositionHeaderValue header = TryParse(cd); 1021 ValidateHeaderValues(header, "attachment", null); 1022 ValidateExtensionParameter(header, "filename*", @"UTF-8''%5cfoo.html"); 1023 } 1024 1025 [Fact] 1026 public void ContentDispositionHeader_Valid40_Success() 1027 { 1028 ContentDispositionValue cd = ContentDispositionTestCases["valid40"]; 1029 ContentDispositionHeaderValue header = TryParse(cd); 1030 ValidateHeaderValues(header, "attachment", null); 1031 ValidateExtensionParameter(header, "filename*0", @"""foo."""); 1032 ValidateExtensionParameter(header, "filename*1", @"""html"""); 1033 } 1034 1035 [Fact] 1036 public void ContentDispositionHeader_Valid41_Success() 1037 { 1038 ContentDispositionValue cd = ContentDispositionTestCases["valid41"]; 1039 ContentDispositionHeaderValue header = TryParse(cd); 1040 ValidateHeaderValues(header, "attachment", null); 1041 ValidateExtensionParameter(header, "filename*0*", @"UTF-8''foo-%c3%a4"); 1042 ValidateExtensionParameter(header, "filename*1", @""".html"""); 1043 } 1044 1045 [Fact] 1046 public void ContentDispositionHeader_Valid42_Success() 1047 { 1048 ContentDispositionValue cd = ContentDispositionTestCases["valid42"]; 1049 ContentDispositionHeaderValue header = TryParse(cd); 1050 ValidateHeaderValues(header, "attachment", null); 1051 ValidateExtensionParameter(header, "filename*0", @"""foo"""); 1052 ValidateExtensionParameter(header, "filename*01", @"""bar"""); 1053 } 1054 1055 [Fact] 1056 public void ContentDispositionHeader_Valid43_Success() 1057 { 1058 ContentDispositionValue cd = ContentDispositionTestCases["valid43"]; 1059 ContentDispositionHeaderValue header = TryParse(cd); 1060 ValidateHeaderValues(header, "attachment", null); 1061 ValidateExtensionParameter(header, "filename*0", @"""foo"""); 1062 ValidateExtensionParameter(header, "filename*2", @"""bar"""); 1063 } 1064 1065 [Fact] 1066 public void ContentDispositionHeader_Valid44_Success() 1067 { 1068 ContentDispositionValue cd = ContentDispositionTestCases["valid44"]; 1069 ContentDispositionHeaderValue header = TryParse(cd); 1070 ValidateHeaderValues(header, "attachment", null); 1071 ValidateExtensionParameter(header, "filename*1", @"""foo."""); 1072 ValidateExtensionParameter(header, "filename*2", @"""html"""); 1073 } 1074 1075 [Fact] 1076 public void ContentDispositionHeader_Valid45_Success() 1077 { 1078 ContentDispositionValue cd = ContentDispositionTestCases["valid45"]; 1079 ContentDispositionHeaderValue header = TryParse(cd); 1080 ValidateHeaderValues(header, "attachment", null); 1081 ValidateExtensionParameter(header, "filename*0", @"""foo"""); 1082 ValidateExtensionParameter(header, "filename*1", @"""bar"""); 1083 } 1084 1085 [Fact] 1086 public void ContentDispositionHeader_Valid46_Success() 1087 { 1088 ContentDispositionValue cd = ContentDispositionTestCases["valid46"]; 1089 ContentDispositionHeaderValue header = TryParse(cd); 1090 ValidateHeaderValues(header, "attachment", @"""foo-ae.html"""); 1091 ValidateExtensionParameter(header, "filename*", @"UTF-8''foo-%c3%a4.html"); 1092 } 1093 1094 [Fact] 1095 public void ContentDispositionHeader_Valid47_Success() 1096 { 1097 ContentDispositionValue cd = ContentDispositionTestCases["valid47"]; 1098 ContentDispositionHeaderValue header = TryParse(cd); 1099 ValidateHeaderValues(header, "attachment", @"""foo-ae.html"""); 1100 ValidateExtensionParameter(header, "filename*", @"UTF-8''foo-%c3%a4.html"); 1101 } 1102 1103 [Fact] 1104 public void ContentDispositionHeader_Valid48_Success() 1105 { 1106 ContentDispositionValue cd = ContentDispositionTestCases["valid48"]; 1107 ContentDispositionHeaderValue header = TryParse(cd); 1108 ValidateHeaderValues(header, "attachment", @"""foo.html"""); 1109 ValidateExtensionParameter(header, "foobar", @"x"); 1110 } 1111 1112 [Fact] 1113 public void ContentDispositionHeader_Valid49_Success() 1114 { 1115 ContentDispositionValue cd = ContentDispositionTestCases["valid49"]; 1116 ContentDispositionHeaderValue header = TryParse(cd); 1117 ValidateHeaderValues(header, "attachment", @"""=?ISO-8859-1?Q?foo-=E4.html?="""); 1118 } 1119 1120 #endregion 1121 1122 private static void ValidateHeaderValues(ContentDispositionHeaderValue header, string expectedDispositionType, string expectedFilename) 1123 { 1124 Assert.NotNull(header); 1125 Assert.Equal(expectedDispositionType, header.DispositionType); 1126 Assert.Equal(expectedFilename, header.FileName); 1127 } 1128 1129 private static void ValidateExtensionParameter(ContentDispositionHeaderValue header, string name, string expectedValue) 1130 { 1131 Assert.NotNull(header); 1132 NameValueHeaderValue parameter = FindParameter(header.Parameters, name); 1133 Assert.NotNull(parameter); 1134 Assert.Equal(expectedValue, parameter.Value); 1135 } 1136 1137 private static NameValueHeaderValue FindParameter(ICollection<NameValueHeaderValue> values, string name) 1138 { 1139 if ((values == null) || (values.Count == 0)) 1140 { 1141 return null; 1142 } 1143 1144 foreach (var value in values) 1145 { 1146 if (string.Equals(value.Name, name, StringComparison.OrdinalIgnoreCase)) 1147 { 1148 return value; 1149 } 1150 } 1151 return null; 1152 } 1153 1154 private static ContentDispositionHeaderValue Parse(ContentDispositionValue cd) 1155 { 1156 Assert.NotNull(cd); 1157 ContentDispositionHeaderValue header = null; 1158 if (cd.Valid) 1159 { 1160 header = ContentDispositionHeaderValue.Parse(cd.Value); 1161 Assert.NotNull(header); 1162 } 1163 else 1164 { 1165 Assert.Throws<FormatException>(() => { header = ContentDispositionHeaderValue.Parse(cd.Value); }); 1166 } 1167 1168 return header; 1169 } 1170 1171 private static ContentDispositionHeaderValue TryParse(ContentDispositionValue cd) 1172 { 1173 Assert.NotNull(cd); 1174 ContentDispositionHeaderValue header; 1175 if (cd.Valid) 1176 { 1177 Assert.True(ContentDispositionHeaderValue.TryParse(cd.Value, out header)); 1178 Assert.NotNull(header); 1179 } 1180 else 1181 { 1182 Assert.False(ContentDispositionHeaderValue.TryParse(cd.Value, out header)); 1183 Assert.Null(header); 1184 } 1185 1186 return header; 1187 } 1188 1189 public class ContentDispositionValue 1190 { 1191 public ContentDispositionValue(string value, string description, bool valid) 1192 { 1193 this.Value = value; 1194 this.Description = description; 1195 this.Valid = valid; 1196 } 1197 1198 public string Value { get; private set; } 1199 1200 public string Description { get; private set; } 1201 1202 public bool Valid { get; private set; } 1203 } 1204 1205 #endregion Tests from HenrikN 1206 1207 #region Helper methods 1208 1209 private void CheckValidParse(string input, ContentDispositionHeaderValue expectedResult) 1210 { 1211 ContentDispositionHeaderValue result = ContentDispositionHeaderValue.Parse(input); 1212 Assert.Equal(expectedResult, result); 1213 } 1214 1215 private void CheckInvalidParse(string input) 1216 { 1217 Assert.Throws<FormatException>(() => { ContentDispositionHeaderValue.Parse(input); }); 1218 } 1219 1220 private void CheckValidTryParse(string input, ContentDispositionHeaderValue expectedResult) 1221 { 1222 ContentDispositionHeaderValue result = null; 1223 Assert.True(ContentDispositionHeaderValue.TryParse(input, out result), input); 1224 Assert.Equal(expectedResult, result); 1225 } 1226 1227 private void CheckInvalidTryParse(string input) 1228 { 1229 ContentDispositionHeaderValue result = null; 1230 Assert.False(ContentDispositionHeaderValue.TryParse(input, out result), input); 1231 Assert.Null(result); 1232 } 1233 1234 private static void AssertFormatException(string contentDisposition) 1235 { 1236 Assert.Throws<FormatException>(() => { new ContentDispositionHeaderValue(contentDisposition); }); 1237 } 1238 #endregion 1239 } 1240 } 1241