1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.h" 6 7 #include "base/basictypes.h" 8 #include "base/files/file_path.h" 9 #include "base/message_loop/message_loop.h" 10 #include "base/values.h" 11 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.h" 12 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.h" 13 #include "content/public/browser/resource_request_info.h" 14 #include "net/base/request_priority.h" 15 #include "net/test/embedded_test_server/embedded_test_server.h" 16 #include "net/url_request/url_request_test_util.h" 17 #include "testing/gtest/include/gtest/gtest.h" 18 19 using base::DictionaryValue; 20 using base::FundamentalValue; 21 using base::ListValue; 22 using base::StringValue; 23 using base::Value; 24 25 namespace extensions { 26 27 namespace keys = declarative_webrequest_constants; 28 29 namespace { 30 const char kUnknownConditionName[] = "unknownType"; 31 32 base::FilePath TestDataPath(base::StringPiece relative_to_src) { 33 base::FilePath src_dir; 34 CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &src_dir)); 35 return src_dir.AppendASCII(relative_to_src); 36 } 37 38 TEST(WebRequestConditionAttributeTest, CreateConditionAttribute) { 39 // Necessary for TestURLRequest. 40 base::MessageLoopForIO message_loop; 41 42 std::string error; 43 scoped_refptr<const WebRequestConditionAttribute> result; 44 base::StringValue string_value("main_frame"); 45 base::ListValue resource_types; 46 resource_types.Append(new base::StringValue("main_frame")); 47 48 // Test wrong condition name passed. 49 error.clear(); 50 result = WebRequestConditionAttribute::Create( 51 kUnknownConditionName, &resource_types, &error); 52 EXPECT_FALSE(error.empty()); 53 EXPECT_FALSE(result.get()); 54 55 // Test wrong data type passed 56 error.clear(); 57 result = WebRequestConditionAttribute::Create( 58 keys::kResourceTypeKey, &string_value, &error); 59 EXPECT_FALSE(error.empty()); 60 EXPECT_FALSE(result.get()); 61 62 error.clear(); 63 result = WebRequestConditionAttribute::Create( 64 keys::kContentTypeKey, &string_value, &error); 65 EXPECT_FALSE(error.empty()); 66 EXPECT_FALSE(result.get()); 67 68 // Test success 69 error.clear(); 70 result = WebRequestConditionAttribute::Create( 71 keys::kResourceTypeKey, &resource_types, &error); 72 EXPECT_EQ("", error); 73 ASSERT_TRUE(result.get()); 74 EXPECT_EQ(WebRequestConditionAttribute::CONDITION_RESOURCE_TYPE, 75 result->GetType()); 76 EXPECT_EQ(std::string(keys::kResourceTypeKey), result->GetName()); 77 } 78 79 TEST(WebRequestConditionAttributeTest, ResourceType) { 80 // Necessary for TestURLRequest. 81 base::MessageLoopForIO message_loop; 82 83 std::string error; 84 base::ListValue resource_types; 85 // The 'sub_frame' value is chosen arbitrarily, so as the corresponding 86 // ResourceType::Type is not 0, the default value. 87 resource_types.Append(new base::StringValue("sub_frame")); 88 89 scoped_refptr<const WebRequestConditionAttribute> attribute = 90 WebRequestConditionAttribute::Create( 91 keys::kResourceTypeKey, &resource_types, &error); 92 EXPECT_EQ("", error); 93 ASSERT_TRUE(attribute.get()); 94 EXPECT_EQ(std::string(keys::kResourceTypeKey), attribute->GetName()); 95 96 net::TestURLRequestContext context; 97 net::TestURLRequest url_request_ok( 98 GURL("http://www.example.com"), net::DEFAULT_PRIORITY, NULL, &context); 99 content::ResourceRequestInfo::AllocateForTesting( 100 &url_request_ok, ResourceType::SUB_FRAME, NULL, -1, -1, -1, false); 101 EXPECT_TRUE(attribute->IsFulfilled(WebRequestData(&url_request_ok, 102 ON_BEFORE_REQUEST))); 103 104 net::TestURLRequest url_request_fail( 105 GURL("http://www.example.com"), net::DEFAULT_PRIORITY, NULL, &context); 106 content::ResourceRequestInfo::AllocateForTesting( 107 &url_request_fail, ResourceType::MAIN_FRAME, NULL, -1, -1, -1, false); 108 EXPECT_FALSE(attribute->IsFulfilled(WebRequestData(&url_request_fail, 109 ON_BEFORE_REQUEST))); 110 } 111 112 TEST(WebRequestConditionAttributeTest, ContentType) { 113 // Necessary for TestURLRequest. 114 base::MessageLoopForIO message_loop; 115 116 std::string error; 117 scoped_refptr<const WebRequestConditionAttribute> result; 118 119 net::test_server::EmbeddedTestServer test_server; 120 test_server.ServeFilesFromDirectory(TestDataPath( 121 "chrome/test/data/extensions/api_test/webrequest/declarative")); 122 ASSERT_TRUE(test_server.InitializeAndWaitUntilReady()); 123 124 net::TestURLRequestContext context; 125 net::TestDelegate delegate; 126 net::TestURLRequest url_request(test_server.GetURL("/headers.html"), 127 net::DEFAULT_PRIORITY, 128 &delegate, 129 &context); 130 url_request.Start(); 131 base::MessageLoop::current()->Run(); 132 133 base::ListValue content_types; 134 content_types.Append(new base::StringValue("text/plain")); 135 scoped_refptr<const WebRequestConditionAttribute> attribute_include = 136 WebRequestConditionAttribute::Create( 137 keys::kContentTypeKey, &content_types, &error); 138 EXPECT_EQ("", error); 139 ASSERT_TRUE(attribute_include.get()); 140 EXPECT_FALSE(attribute_include->IsFulfilled( 141 WebRequestData(&url_request, ON_BEFORE_REQUEST, 142 url_request.response_headers()))); 143 EXPECT_TRUE(attribute_include->IsFulfilled( 144 WebRequestData(&url_request, ON_HEADERS_RECEIVED, 145 url_request.response_headers()))); 146 EXPECT_EQ(std::string(keys::kContentTypeKey), attribute_include->GetName()); 147 148 scoped_refptr<const WebRequestConditionAttribute> attribute_exclude = 149 WebRequestConditionAttribute::Create( 150 keys::kExcludeContentTypeKey, &content_types, &error); 151 EXPECT_EQ("", error); 152 ASSERT_TRUE(attribute_exclude.get()); 153 EXPECT_FALSE(attribute_exclude->IsFulfilled( 154 WebRequestData(&url_request, ON_HEADERS_RECEIVED, 155 url_request.response_headers()))); 156 157 content_types.Clear(); 158 content_types.Append(new base::StringValue("something/invalid")); 159 scoped_refptr<const WebRequestConditionAttribute> attribute_unincluded = 160 WebRequestConditionAttribute::Create( 161 keys::kContentTypeKey, &content_types, &error); 162 EXPECT_EQ("", error); 163 ASSERT_TRUE(attribute_unincluded.get()); 164 EXPECT_FALSE(attribute_unincluded->IsFulfilled( 165 WebRequestData(&url_request, ON_HEADERS_RECEIVED, 166 url_request.response_headers()))); 167 168 scoped_refptr<const WebRequestConditionAttribute> attribute_unexcluded = 169 WebRequestConditionAttribute::Create( 170 keys::kExcludeContentTypeKey, &content_types, &error); 171 EXPECT_EQ("", error); 172 ASSERT_TRUE(attribute_unexcluded.get()); 173 EXPECT_TRUE(attribute_unexcluded->IsFulfilled( 174 WebRequestData(&url_request, ON_HEADERS_RECEIVED, 175 url_request.response_headers()))); 176 EXPECT_EQ(std::string(keys::kExcludeContentTypeKey), 177 attribute_unexcluded->GetName()); 178 } 179 180 // Testing WebRequestConditionAttributeThirdParty. 181 TEST(WebRequestConditionAttributeTest, ThirdParty) { 182 // Necessary for TestURLRequest. 183 base::MessageLoopForIO message_loop; 184 185 std::string error; 186 const FundamentalValue value_true(true); 187 // This attribute matches only third party requests. 188 scoped_refptr<const WebRequestConditionAttribute> third_party_attribute = 189 WebRequestConditionAttribute::Create(keys::kThirdPartyKey, 190 &value_true, 191 &error); 192 ASSERT_EQ("", error); 193 ASSERT_TRUE(third_party_attribute.get()); 194 EXPECT_EQ(std::string(keys::kThirdPartyKey), 195 third_party_attribute->GetName()); 196 const FundamentalValue value_false(false); 197 // This attribute matches only first party requests. 198 scoped_refptr<const WebRequestConditionAttribute> first_party_attribute = 199 WebRequestConditionAttribute::Create(keys::kThirdPartyKey, 200 &value_false, 201 &error); 202 ASSERT_EQ("", error); 203 ASSERT_TRUE(first_party_attribute.get()); 204 EXPECT_EQ(std::string(keys::kThirdPartyKey), 205 first_party_attribute->GetName()); 206 207 const GURL url_empty; 208 const GURL url_a("http://a.com"); 209 const GURL url_b("http://b.com"); 210 net::TestURLRequestContext context; 211 net::TestDelegate delegate; 212 net::TestURLRequest url_request( 213 url_a, net::DEFAULT_PRIORITY, &delegate, &context); 214 215 for (unsigned int i = 1; i <= kLastActiveStage; i <<= 1) { 216 if (!(kActiveStages & i)) 217 continue; 218 const RequestStage stage = static_cast<RequestStage>(i); 219 url_request.set_first_party_for_cookies(url_empty); 220 EXPECT_FALSE(third_party_attribute->IsFulfilled(WebRequestData(&url_request, 221 stage))); 222 EXPECT_TRUE(first_party_attribute->IsFulfilled(WebRequestData(&url_request, 223 stage))); 224 225 url_request.set_first_party_for_cookies(url_b); 226 EXPECT_TRUE(third_party_attribute->IsFulfilled(WebRequestData(&url_request, 227 stage))); 228 EXPECT_FALSE(first_party_attribute->IsFulfilled(WebRequestData(&url_request, 229 stage))); 230 231 url_request.set_first_party_for_cookies(url_a); 232 EXPECT_FALSE(third_party_attribute->IsFulfilled(WebRequestData(&url_request, 233 stage))); 234 EXPECT_TRUE(first_party_attribute->IsFulfilled(WebRequestData(&url_request, 235 stage))); 236 } 237 } 238 239 // Testing WebRequestConditionAttributeStages. This iterates over all stages, 240 // and tests a couple of "stage" attributes -- one created with an empty set of 241 // applicable stages, one for each stage applicable for that stage, and one 242 // applicable in all stages. 243 TEST(WebRequestConditionAttributeTest, Stages) { 244 // Necessary for TestURLRequest. 245 base::MessageLoopForIO message_loop; 246 247 typedef std::pair<RequestStage, const char*> StageNamePair; 248 static const StageNamePair active_stages[] = { 249 StageNamePair(ON_BEFORE_REQUEST, keys::kOnBeforeRequestEnum), 250 StageNamePair(ON_BEFORE_SEND_HEADERS, keys::kOnBeforeSendHeadersEnum), 251 StageNamePair(ON_HEADERS_RECEIVED, keys::kOnHeadersReceivedEnum), 252 StageNamePair(ON_AUTH_REQUIRED, keys::kOnAuthRequiredEnum) 253 }; 254 255 // Check that exactly all active stages are considered in this test. 256 unsigned int covered_stages = 0; 257 for (size_t i = 0; i < arraysize(active_stages); ++i) 258 covered_stages |= active_stages[i].first; 259 EXPECT_EQ(kActiveStages, covered_stages); 260 261 std::string error; 262 263 // Create an attribute with an empty set of applicable stages. 264 base::ListValue empty_list; 265 scoped_refptr<const WebRequestConditionAttribute> empty_attribute = 266 WebRequestConditionAttribute::Create(keys::kStagesKey, 267 &empty_list, 268 &error); 269 EXPECT_EQ("", error); 270 ASSERT_TRUE(empty_attribute.get()); 271 EXPECT_EQ(std::string(keys::kStagesKey), empty_attribute->GetName()); 272 273 // Create an attribute with all possible applicable stages. 274 base::ListValue all_stages; 275 for (size_t i = 0; i < arraysize(active_stages); ++i) 276 all_stages.AppendString(active_stages[i].second); 277 scoped_refptr<const WebRequestConditionAttribute> attribute_with_all = 278 WebRequestConditionAttribute::Create(keys::kStagesKey, 279 &all_stages, 280 &error); 281 EXPECT_EQ("", error); 282 ASSERT_TRUE(attribute_with_all.get()); 283 EXPECT_EQ(std::string(keys::kStagesKey), attribute_with_all->GetName()); 284 285 // Create one attribute for each single stage, to be applicable in that stage. 286 std::vector<scoped_refptr<const WebRequestConditionAttribute> > 287 one_stage_attributes; 288 289 for (size_t i = 0; i < arraysize(active_stages); ++i) { 290 base::ListValue single_stage_list; 291 single_stage_list.AppendString(active_stages[i].second); 292 one_stage_attributes.push_back( 293 WebRequestConditionAttribute::Create(keys::kStagesKey, 294 &single_stage_list, 295 &error)); 296 EXPECT_EQ("", error); 297 ASSERT_TRUE(one_stage_attributes.back().get() != NULL); 298 } 299 300 const GURL url_empty; 301 net::TestURLRequestContext context; 302 net::TestDelegate delegate; 303 net::TestURLRequest url_request( 304 url_empty, net::DEFAULT_PRIORITY, &delegate, &context); 305 306 for (size_t i = 0; i < arraysize(active_stages); ++i) { 307 EXPECT_FALSE(empty_attribute->IsFulfilled( 308 WebRequestData(&url_request, active_stages[i].first))); 309 310 for (size_t j = 0; j < one_stage_attributes.size(); ++j) { 311 EXPECT_EQ(i == j, 312 one_stage_attributes[j]->IsFulfilled( 313 WebRequestData(&url_request, active_stages[i].first))); 314 } 315 316 EXPECT_TRUE(attribute_with_all->IsFulfilled( 317 WebRequestData(&url_request, active_stages[i].first))); 318 } 319 } 320 321 namespace { 322 323 // Builds a vector of vectors of string pointers from an array of strings. 324 // |array| is in fact a sequence of arrays. The array |sizes| captures the sizes 325 // of all parts of |array|, and |size| is the length of |sizes| itself. 326 // Example (this is pseudo-code, not C++): 327 // array = { "a", "b", "c", "d", "e", "f" } 328 // sizes = { 2, 0, 4 } 329 // size = 3 330 // results in out == { {&"a", &"b"}, {}, {&"c", &"d", &"e", &"f"} } 331 void GetArrayAsVector(const std::string array[], 332 const size_t sizes[], 333 const size_t size, 334 std::vector< std::vector<const std::string*> >* out) { 335 out->clear(); 336 size_t next = 0; 337 for (size_t i = 0; i < size; ++i) { 338 out->push_back(std::vector<const std::string*>()); 339 for (size_t j = next; j < next + sizes[i]; ++j) { 340 out->back().push_back(&(array[j])); 341 } 342 next += sizes[i]; 343 } 344 } 345 346 // Builds a DictionaryValue from an array of the form {name1, value1, name2, 347 // value2, ...}. Values for the same key are grouped in a ListValue. 348 scoped_ptr<base::DictionaryValue> GetDictionaryFromArray( 349 const std::vector<const std::string*>& array) { 350 const size_t length = array.size(); 351 CHECK(length % 2 == 0); 352 353 scoped_ptr<base::DictionaryValue> dictionary(new base::DictionaryValue); 354 for (size_t i = 0; i < length; i += 2) { 355 const std::string* name = array[i]; 356 const std::string* value = array[i+1]; 357 if (dictionary->HasKey(*name)) { 358 base::Value* entry = NULL; 359 scoped_ptr<base::Value> entry_owned; 360 base::ListValue* list = NULL; 361 if (!dictionary->GetWithoutPathExpansion(*name, &entry)) 362 return scoped_ptr<base::DictionaryValue>(); 363 switch (entry->GetType()) { 364 case base::Value::TYPE_STRING: 365 // Replace the present string with a list. 366 list = new base::ListValue; 367 // Ignoring return value, we already verified the entry is there. 368 dictionary->RemoveWithoutPathExpansion(*name, &entry_owned); 369 list->Append(entry_owned.release()); 370 list->Append(new base::StringValue(*value)); 371 dictionary->SetWithoutPathExpansion(*name, list); 372 break; 373 case base::Value::TYPE_LIST: // Just append to the list. 374 CHECK(entry->GetAsList(&list)); 375 list->Append(new base::StringValue(*value)); 376 break; 377 default: 378 NOTREACHED(); // We never put other Values here. 379 return scoped_ptr<base::DictionaryValue>(); 380 } 381 } else { 382 dictionary->SetString(*name, *value); 383 } 384 } 385 return dictionary.Pass(); 386 } 387 388 // Returns whether the response headers from |url_request| satisfy the match 389 // criteria given in |tests|. For at least one |i| all tests from |tests[i]| 390 // must pass. If |positive_test| is true, the dictionary is interpreted as the 391 // containsHeaders property of a RequestMatcher, otherwise as 392 // doesNotContainHeaders. 393 void MatchAndCheck(const std::vector< std::vector<const std::string*> >& tests, 394 const std::string& key, 395 RequestStage stage, 396 net::URLRequest* url_request, 397 bool* result) { 398 base::ListValue contains_headers; 399 for (size_t i = 0; i < tests.size(); ++i) { 400 scoped_ptr<base::DictionaryValue> temp(GetDictionaryFromArray(tests[i])); 401 ASSERT_TRUE(temp.get()); 402 contains_headers.Append(temp.release()); 403 } 404 405 std::string error; 406 scoped_refptr<const WebRequestConditionAttribute> attribute = 407 WebRequestConditionAttribute::Create(key, &contains_headers, &error); 408 ASSERT_EQ("", error); 409 ASSERT_TRUE(attribute.get()); 410 EXPECT_EQ(key, attribute->GetName()); 411 412 *result = attribute->IsFulfilled(WebRequestData( 413 url_request, stage, url_request->response_headers())); 414 } 415 416 } // namespace 417 418 // Here we test WebRequestConditionAttributeRequestHeaders for matching 419 // correctly against request headers. This test is not as extensive as 420 // "ResponseHeaders" (below), because the header-matching code is shared 421 // by both types of condition attributes, so it is enough to test it once. 422 TEST(WebRequestConditionAttributeTest, RequestHeaders) { 423 // Necessary for TestURLRequest. 424 base::MessageLoopForIO message_loop; 425 426 net::TestURLRequestContext context; 427 net::TestDelegate delegate; 428 net::TestURLRequest url_request(GURL("http://example.com"), // Dummy URL. 429 net::DEFAULT_PRIORITY, 430 &delegate, 431 &context); 432 url_request.SetExtraRequestHeaderByName( 433 "Custom-header", "custom/value", true /* overwrite */); 434 url_request.Start(); 435 base::MessageLoop::current()->Run(); 436 437 std::vector<std::vector<const std::string*> > tests; 438 bool result = false; 439 440 const RequestStage stage = ON_BEFORE_SEND_HEADERS; 441 442 // First set of test data -- passing conjunction. 443 const std::string kPassingCondition[] = { 444 keys::kNameContainsKey, "CuStOm", // Header names are case insensitive. 445 keys::kNameEqualsKey, "custom-header", 446 keys::kValueSuffixKey, "alue", 447 keys::kValuePrefixKey, "custom/value" 448 }; 449 const size_t kPassingConditionSizes[] = { arraysize(kPassingCondition) }; 450 GetArrayAsVector(kPassingCondition, kPassingConditionSizes, 1u, &tests); 451 // Positive filter, passing (conjunction of tests). 452 MatchAndCheck(tests, keys::kRequestHeadersKey, stage, &url_request, &result); 453 EXPECT_TRUE(result); 454 // Negative filter, failing (conjunction of tests). 455 MatchAndCheck( 456 tests, keys::kExcludeRequestHeadersKey, stage, &url_request, &result); 457 EXPECT_FALSE(result); 458 459 // Second set of test data -- failing disjunction. 460 const std::string kFailCondition[] = { 461 keys::kNameSuffixKey, "Custom", // Test 1. 462 keys::kNameEqualsKey, "ustom-valu", // Test 2. 463 keys::kValuePrefixKey, "custom ", // Test 3. 464 keys::kValueContainsKey, " value" // Test 4. 465 }; 466 const size_t kFailConditionSizes[] = { 2u, 2u, 2u, 2u }; 467 GetArrayAsVector(kFailCondition, kFailConditionSizes, 4u, &tests); 468 // Positive filter, failing (disjunction of tests). 469 MatchAndCheck(tests, keys::kRequestHeadersKey, stage, &url_request, &result); 470 EXPECT_FALSE(result); 471 // Negative filter, passing (disjunction of tests). 472 MatchAndCheck( 473 tests, keys::kExcludeRequestHeadersKey, stage, &url_request, &result); 474 EXPECT_TRUE(result); 475 476 // Third set of test data, corner case -- empty disjunction. 477 GetArrayAsVector(NULL, NULL, 0u, &tests); 478 // Positive filter, failing (no test to pass). 479 MatchAndCheck(tests, keys::kRequestHeadersKey, stage, &url_request, &result); 480 EXPECT_FALSE(result); 481 // Negative filter, passing (no test to fail). 482 MatchAndCheck( 483 tests, keys::kExcludeRequestHeadersKey, stage, &url_request, &result); 484 EXPECT_TRUE(result); 485 486 // Fourth set of test data, corner case -- empty conjunction. 487 const size_t kEmptyConjunctionSizes[] = { 0u }; 488 GetArrayAsVector(NULL, kEmptyConjunctionSizes, 1u, &tests); 489 // Positive filter, passing (trivial test). 490 MatchAndCheck(tests, keys::kRequestHeadersKey, stage, &url_request, &result); 491 EXPECT_TRUE(result); 492 // Negative filter, failing. 493 MatchAndCheck( 494 tests, keys::kExcludeRequestHeadersKey, stage, &url_request, &result); 495 EXPECT_FALSE(result); 496 } 497 498 // Here we test WebRequestConditionAttributeResponseHeaders for: 499 // 1. Correct implementation of prefix/suffix/contains/equals matching. 500 // 2. Performing logical disjunction (||) between multiple specifications. 501 // 3. Negating the match in case of 'doesNotContainHeaders'. 502 TEST(WebRequestConditionAttributeTest, ResponseHeaders) { 503 // Necessary for TestURLRequest. 504 base::MessageLoopForIO message_loop; 505 506 net::test_server::EmbeddedTestServer test_server; 507 test_server.ServeFilesFromDirectory(TestDataPath( 508 "chrome/test/data/extensions/api_test/webrequest/declarative")); 509 ASSERT_TRUE(test_server.InitializeAndWaitUntilReady()); 510 511 net::TestURLRequestContext context; 512 net::TestDelegate delegate; 513 net::TestURLRequest url_request(test_server.GetURL("/headers.html"), 514 net::DEFAULT_PRIORITY, 515 &delegate, 516 &context); 517 url_request.Start(); 518 base::MessageLoop::current()->Run(); 519 520 // In all the tests below we assume that the server includes the headers 521 // Custom-Header: custom/value 522 // Custom-Header-B: valueA 523 // Custom-Header-B: valueB 524 // Custom-Header-C: valueC, valueD 525 // Custom-Header-D: 526 // in the response, but does not include "Non-existing: void". 527 528 std::vector< std::vector<const std::string*> > tests; 529 bool result; 530 531 const RequestStage stage = ON_HEADERS_RECEIVED; 532 533 // 1.a. -- All these tests should pass. 534 const std::string kPassingCondition[] = { 535 keys::kNamePrefixKey, "Custom", 536 keys::kNameSuffixKey, "m-header", // Header names are case insensitive. 537 keys::kValueContainsKey, "alu", 538 keys::kValueEqualsKey, "custom/value" 539 }; 540 const size_t kPassingConditionSizes[] = { arraysize(kPassingCondition) }; 541 GetArrayAsVector(kPassingCondition, kPassingConditionSizes, 1u, &tests); 542 MatchAndCheck(tests, keys::kResponseHeadersKey, stage, &url_request, &result); 543 EXPECT_TRUE(result); 544 545 // 1.b. -- None of the following tests in the discjunction should pass. 546 const std::string kFailCondition[] = { 547 keys::kNamePrefixKey, " Custom", // Test 1. 548 keys::kNameContainsKey, " -", // Test 2. 549 keys::kValueSuffixKey, "alu", // Test 3. 550 keys::kValueEqualsKey, "custom" // Test 4. 551 }; 552 const size_t kFailConditionSizes[] = { 2u, 2u, 2u, 2u }; 553 GetArrayAsVector(kFailCondition, kFailConditionSizes, 4u, &tests); 554 MatchAndCheck(tests, keys::kResponseHeadersKey, stage, &url_request, &result); 555 EXPECT_FALSE(result); 556 557 // 1.c. -- This should fail (mixing name and value from different headers) 558 const std::string kMixingCondition[] = { 559 keys::kNameSuffixKey, "Header-B", 560 keys::kValueEqualsKey, "custom/value" 561 }; 562 const size_t kMixingConditionSizes[] = { arraysize(kMixingCondition) }; 563 GetArrayAsVector(kMixingCondition, kMixingConditionSizes, 1u, &tests); 564 MatchAndCheck(tests, keys::kResponseHeadersKey, stage, &url_request, &result); 565 EXPECT_FALSE(result); 566 567 // 1.d. -- Test handling multiple values for one header (both should pass). 568 const std::string kMoreValues1[] = { 569 keys::kNameEqualsKey, "Custom-header-b", 570 keys::kValueEqualsKey, "valueA" 571 }; 572 const size_t kMoreValues1Sizes[] = { arraysize(kMoreValues1) }; 573 GetArrayAsVector(kMoreValues1, kMoreValues1Sizes, 1u, &tests); 574 MatchAndCheck(tests, keys::kResponseHeadersKey, stage, &url_request, &result); 575 EXPECT_TRUE(result); 576 const std::string kMoreValues2[] = { 577 keys::kNameEqualsKey, "Custom-header-b", 578 keys::kValueEqualsKey, "valueB" 579 }; 580 const size_t kMoreValues2Sizes[] = { arraysize(kMoreValues2) }; 581 GetArrayAsVector(kMoreValues2, kMoreValues2Sizes, 1u, &tests); 582 MatchAndCheck(tests, keys::kResponseHeadersKey, stage, &url_request, &result); 583 EXPECT_TRUE(result); 584 585 // 1.e. -- This should fail as conjunction but pass as disjunction. 586 const std::string kConflict[] = { 587 keys::kNameSuffixKey, "Header", // True for some header. 588 keys::kNameContainsKey, "Header-B" // True for a different header. 589 }; 590 // First disjunction, no conflict. 591 const size_t kNoConflictSizes[] = { 2u, 2u }; 592 GetArrayAsVector(kConflict, kNoConflictSizes, 2u, &tests); 593 MatchAndCheck(tests, keys::kResponseHeadersKey, stage, &url_request, &result); 594 EXPECT_TRUE(result); 595 // Then conjunction, conflict. 596 const size_t kConflictSizes[] = { arraysize(kConflict) }; 597 GetArrayAsVector(kConflict, kConflictSizes, 1u, &tests); 598 MatchAndCheck(tests, keys::kResponseHeadersKey, stage, &url_request, &result); 599 EXPECT_FALSE(result); 600 601 // 1.f. -- This should pass, checking for correct treatment of ',' in values. 602 const std::string kComma[] = { 603 keys::kNameSuffixKey, "Header-C", 604 keys::kValueEqualsKey, "valueC, valueD" 605 }; 606 const size_t kCommaSizes[] = { arraysize(kComma) }; 607 GetArrayAsVector(kComma, kCommaSizes, 1u, &tests); 608 MatchAndCheck(tests, keys::kResponseHeadersKey, stage, &url_request, &result); 609 EXPECT_TRUE(result); 610 611 // 1.g. -- This should pass, empty values are values as well. 612 const std::string kEmpty[] = { 613 keys::kNameEqualsKey, "custom-header-d", 614 keys::kValueEqualsKey, "" 615 }; 616 const size_t kEmptySizes[] = { arraysize(kEmpty) }; 617 GetArrayAsVector(kEmpty, kEmptySizes, 1u, &tests); 618 MatchAndCheck(tests, keys::kResponseHeadersKey, stage, &url_request, &result); 619 EXPECT_TRUE(result); 620 621 // 1.h. -- Values are case-sensitive, this should fail. 622 const std::string kLowercase[] = { 623 keys::kNameEqualsKey, "Custom-header-b", 624 keys::kValuePrefixKey, "valueb", // valueb != valueB 625 keys::kNameEqualsKey, "Custom-header-b", 626 keys::kValueSuffixKey, "valueb", 627 keys::kNameEqualsKey, "Custom-header-b", 628 keys::kValueContainsKey, "valueb", 629 keys::kNameEqualsKey, "Custom-header-b", 630 keys::kValueEqualsKey, "valueb" 631 }; 632 const size_t kLowercaseSizes[] = { 4u, 4u, 4u, 4u }; // As disjunction. 633 GetArrayAsVector(kLowercase, kLowercaseSizes, 4u, &tests); 634 MatchAndCheck(tests, keys::kResponseHeadersKey, stage, &url_request, &result); 635 EXPECT_FALSE(result); 636 637 // 1.i. -- Names are case-insensitive, this should pass. 638 const std::string kUppercase[] = { 639 keys::kNamePrefixKey, "CUSTOM-HEADER-B", 640 keys::kNameSuffixKey, "CUSTOM-HEADER-B", 641 keys::kNameEqualsKey, "CUSTOM-HEADER-B", 642 keys::kNameContainsKey, "CUSTOM-HEADER-B" 643 }; 644 const size_t kUppercaseSizes[] = { arraysize(kUppercase) }; // Conjunction. 645 GetArrayAsVector(kUppercase, kUppercaseSizes, 1u, &tests); 646 MatchAndCheck(tests, keys::kResponseHeadersKey, stage, &url_request, &result); 647 EXPECT_TRUE(result); 648 649 // 2.a. -- This should pass as disjunction, because one of the tests passes. 650 const std::string kDisjunction[] = { 651 keys::kNamePrefixKey, "Non-existing", // This one fails. 652 keys::kNameSuffixKey, "Non-existing", // This one fails. 653 keys::kValueEqualsKey, "void", // This one fails. 654 keys::kValueContainsKey, "alu" // This passes. 655 }; 656 const size_t kDisjunctionSizes[] = { 2u, 2u, 2u, 2u }; 657 GetArrayAsVector(kDisjunction, kDisjunctionSizes, 4u, &tests); 658 MatchAndCheck(tests, keys::kResponseHeadersKey, stage, &url_request, &result); 659 EXPECT_TRUE(result); 660 661 // 3.a. -- This should pass. 662 const std::string kNonExistent[] = { 663 keys::kNameEqualsKey, "Non-existing", 664 keys::kValueEqualsKey, "void" 665 }; 666 const size_t kNonExistentSizes[] = { arraysize(kNonExistent) }; 667 GetArrayAsVector(kNonExistent, kNonExistentSizes, 1u, &tests); 668 MatchAndCheck( 669 tests, keys::kExcludeResponseHeadersKey, stage, &url_request, &result); 670 EXPECT_TRUE(result); 671 672 // 3.b. -- This should fail. 673 const std::string kExisting[] = { 674 keys::kNameEqualsKey, "custom-header-b", 675 keys::kValueEqualsKey, "valueB" 676 }; 677 const size_t kExistingSize[] = { arraysize(kExisting) }; 678 GetArrayAsVector(kExisting, kExistingSize, 1u, &tests); 679 MatchAndCheck( 680 tests, keys::kExcludeResponseHeadersKey, stage, &url_request, &result); 681 EXPECT_FALSE(result); 682 } 683 684 } // namespace 685 } // namespace extensions 686