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