Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / components / data_reduction_proxy / browser / data_reduction_proxy_tamper_detection_unittest.cc
1 // Copyright 2014 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 "components/data_reduction_proxy/browser/data_reduction_proxy_tamper_detection.h"
6
7 #include <string.h>
8 #include <algorithm>
9 #include <map>
10 #include <vector>
11
12 #include "base/base64.h"
13 #include "base/md5.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_split.h"
17 #include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h"
18 #include "components/data_reduction_proxy/common/data_reduction_proxy_headers_test_utils.h"
19 #include "net/http/http_response_headers.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21
22 #if defined(OS_ANDROID)
23 #include "base/android/jni_android.h"
24 #include "net/android/network_library.h"
25 #endif
26
27 namespace {
28
29 // Calcuates MD5 hash value for a string and then base64 encode it. Testcases
30 // contain expected fingerprint in plain text, which needs to be encoded before
31 // comparison.
32 std::string GetEncoded(const std::string& input) {
33   base::MD5Digest digest;
34   base::MD5Sum(input.c_str(), input.size(), &digest);
35   std::string base64encoded;
36   base::Base64Encode(std::string((char*)digest.a,
37                      ARRAYSIZE_UNSAFE(digest.a)), &base64encoded);
38   return base64encoded;
39 }
40
41 // Replaces all contents within "[]" by corresponding base64 encoded MD5 value.
42 // It can handle nested case like: [[abc]def]. This helper function transforms
43 // fingerprint in plain text to actual encoded fingerprint.
44 void ReplaceWithEncodedString(std::string* input) {
45   size_t start, end, temp;
46   while (true) {
47     start = input->find("[");
48     if (start == std::string::npos) break;
49     while (true) {
50       temp = input->find("[", start + 1);
51       end = input->find("]", start + 1);
52       if (end != std::string::npos && end < temp)
53         break;
54
55       start = temp;
56     }
57     std::string need_to_encode = input->substr(start + 1, end - start - 1);
58     *input = input->substr(0, start) + GetEncoded(need_to_encode) +
59              input->substr(end + 1);
60   }
61 }
62
63 // Returns a vector contains all the values from a comma-separated string.
64 // Some testcases contain string representation of a vector, this helper
65 // function generates a vector from a input string.
66 std::vector<std::string> StringsToVector(const std::string& values) {
67   std::vector<std::string> ret;
68   if (values.empty())
69     return ret;
70   size_t now = 0;
71   size_t next;
72   while ((next = values.find(",", now)) != std::string::npos) {
73     ret.push_back(values.substr(now, next - now));
74     now = next + 1;
75   }
76   return ret;
77 }
78
79 void InitEnv() {
80 #if defined(OS_ANDROID)
81   JNIEnv* env = base::android::AttachCurrentThread();
82   static bool inited = false;
83   if (!inited) {
84     net::android::RegisterNetworkLibrary(env);
85     inited = true;
86   }
87 #endif
88 }
89
90 }  // namespace
91
92 namespace data_reduction_proxy {
93
94 class DataReductionProxyTamperDetectionTest : public testing::Test {
95
96 };
97
98 // Tests function ValidateChromeProxyHeader.
99 TEST_F(DataReductionProxyTamperDetectionTest, ChromeProxy) {
100   // |received_fingerprint| is not the actual fingerprint from data reduction
101   // proxy, instead, the base64 encoded field is in plain text (within "[]")
102   // and needs to be encoded first.
103   struct {
104     std::string label;
105     std::string raw_header;
106     std::string received_fingerprint;
107     bool expected_tampered_with;
108   } test[] = {
109     {
110       "Checks sorting.",
111       "HTTP/1.1 200 OK\n"
112       "Chrome-Proxy: c,b,a,3,2,1,fcp=f\n",
113       "[1,2,3,a,b,c,]",
114       false,
115     },
116     {
117       "Checks Chrome-Proxy's fingerprint removing.",
118       "HTTP/1.1 200 OK\n"
119       "Chrome-Proxy: a,b,c,d,e,3,2,1,fcp=f\n",
120       "[1,2,3,a,b,c,d,e,]",
121       false,
122     },
123     {
124       "Checks no Chrome-Proxy header case (should not happen).",
125       "HTTP/1.1 200 OK\n",
126       "[]",
127       false,
128     },
129     {
130       "Checks empty Chrome-Proxy header case (should not happen).",
131       "HTTP/1.1 200 OK\n"
132       "Chrome-Proxy:    \n",
133       "[,]",
134       false,
135     },
136     {
137       "Checks Chrome-Proxy header with its fingerprint only case.",
138       "HTTP/1.1 200 OK\n"
139       "Chrome-Proxy: fcp=f\n",
140       "[]",
141       false,
142     },
143     {
144       "Checks empty Chrome-Proxy header case, with extra ',' and ' '",
145       "HTTP/1.1 200 OK\n"
146       "Chrome-Proxy: fcp=f  ,  \n",
147       "[]",
148       false,
149     },
150     {
151       "Changed no value to empty value.",
152       "HTTP/1.1 200 OK\n"
153       "Chrome-Proxy: fcp=f\n",
154       "[,]",
155       true,
156     },
157     {
158       "Changed header values.",
159       "HTTP/1.1 200 OK\n"
160       "Chrome-Proxy: a,b=2,c,d=1,fcp=f\n",
161       "[a,b=3,c,d=1,]",
162       true,
163     },
164     {
165       "Changed order of header values.",
166       "HTTP/1.1 200 OK\n"
167       "Chrome-Proxy: c,b,a,fcp=1\n",
168       "[c,b,a,]",
169       true,
170     },
171     {
172       "Checks Chrome-Proxy header with extra ' '.",
173       "HTTP/1.1 200 OK\n"
174       "Chrome-Proxy: a  , b   , c,   d,  fcp=f\n",
175       "[a,b,c,d,]",
176       false
177     },
178     {
179       "Check Chrome-Proxy header with multiple lines and ' '.",
180       "HTTP/1.1 200 OK\n"
181       "Chrome-Proxy:     a    ,   c   , d, fcp=f    \n"
182       "Chrome-Proxy:    b \n",
183       "[a,b,c,d,]",
184       false
185     },
186     {
187       "Checks Chrome-Proxy header with multiple lines, at different positions",
188       "HTTP/1.1 200 OK\n"
189       "Chrome-Proxy:     a   \n"
190       "Chrome-Proxy:    c \n"
191       "Content-Type: 1\n"
192       "Cache-Control: 2\n"
193       "ETag: 3\n"
194       "Chrome-Proxy:    b  \n"
195       "Connection: 4\n"
196       "Expires: 5\n"
197       "Chrome-Proxy:    fcp=f \n"
198       "Via: \n"
199       "Content-Length: 12345\n",
200       "[a,b,c,]",
201       false
202     },
203     {
204       "Checks Chrome-Proxy header with multiple same values.",
205       "HTTP/1.1 200 OK\n"
206       "Chrome-Proxy:     a   \n"
207       "Chrome-Proxy:    b\n"
208       "Chrome-Proxy:    c\n"
209       "Chrome-Proxy:    d,   fcp=f    \n"
210       "Chrome-Proxy:     a   \n",
211       "[a,a,b,c,d,]",
212       false
213     },
214     {
215       "Changed Chrome-Proxy header with multiple lines..",
216       "HTTP/1.1 200 OK\n"
217       "Chrome-Proxy: a\n"
218       "Chrome-Proxy: a\n"
219       "Chrome-Proxy: b\n"
220       "Chrome-Proxy: c,fcp=f\n",
221       "[a,b,c,]",
222       true,
223     },
224     {
225       "Checks case whose received fingerprint is empty.",
226       "HTTP/1.1 200 OK\n"
227       "Chrome-Proxy: a,b,c,fcp=1\n",
228       "[]",
229       true,
230     },
231     {
232       "Checks case whose received fingerprint cannot be base64 decoded.",
233       "HTTP/1.1 200 OK\n"
234       "Chrome-Proxy: a,b,c,fcp=1\n",
235       "not_base64_encoded",
236       true,
237     },
238   };
239
240   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) {
241     ReplaceWithEncodedString(&test[i].received_fingerprint);
242
243     std::string raw_headers(test[i].raw_header);
244     HeadersToRaw(&raw_headers);
245     scoped_refptr<net::HttpResponseHeaders> headers(
246         new net::HttpResponseHeaders(raw_headers));
247
248     DataReductionProxyTamperDetection tamper_detection(headers.get(), true, 0);
249
250     bool tampered = tamper_detection.ValidateChromeProxyHeader(
251         test[i].received_fingerprint);
252
253     EXPECT_EQ(test[i].expected_tampered_with, tampered) << test[i].label;
254   }
255 }
256
257 // Tests function ValidateViaHeader.
258 TEST_F(DataReductionProxyTamperDetectionTest, Via) {
259   struct {
260     std::string label;
261     std::string raw_header;
262     std::string received_fingerprint;
263     bool expected_tampered_with;
264     bool expected_has_chrome_proxy_via_header;
265   } test[] = {
266     {
267       "Checks the case that Chrome-Compression-Proxy occurs at the last.",
268       "HTTP/1.1 200 OK\n"
269       "Via: a, b, c, 1.1 Chrome-Compression-Proxy\n",
270       "",
271       false,
272       true,
273     },
274     {
275       "Checks when there is intermediary.",
276       "HTTP/1.1 200 OK\n"
277       "Via: a, b,  c,   1.1 Chrome-Compression-Proxy,    xyz\n",
278       "",
279       true,
280       true,
281     },
282     {
283       "Checks the case of empty Via header.",
284       "HTTP/1.1 200 OK\n"
285       "Via:  \n",
286       "",
287       true,
288       false,
289     },
290     {
291       "Checks the case that only the data reduction proxy's Via header occurs.",
292       "HTTP/1.1 200 OK\n"
293       "Via:  1.1 Chrome-Compression-Proxy    \n",
294       "",
295       false,
296       true,
297     },
298     {
299       "Checks the case that there are ' ', i.e., empty value after the data"
300       " reduction proxy's Via header.",
301       "HTTP/1.1 200 OK\n"
302       "Via:  1.1 Chrome-Compression-Proxy  ,  , \n",
303       "",
304       false,
305       true,
306     },
307     {
308       "Checks the case when there is no Via header",
309       "HTTP/1.1 200 OK\n",
310       "",
311       true,
312       false,
313     },
314     // Same to above test cases, but with deprecated data reduciton proxy Via
315     // header.
316     {
317       "Checks the case that Chrome Compression Proxy occurs at the last.",
318       "HTTP/1.1 200 OK\n"
319       "Via: a, b, c, 1.1 Chrome Compression Proxy\n",
320       "",
321       false,
322       true,
323     },
324     {
325       "Checks when there is intermediary.",
326       "HTTP/1.1 200 OK\n"
327       "Via: a, b,  c,   1.1 Chrome Compression Proxy,    xyz\n",
328       "",
329       true,
330       true,
331     },
332     {
333       "Checks the case of empty Via header.",
334       "HTTP/1.1 200 OK\n"
335       "Via:  \n",
336       "",
337       true,
338       false,
339     },
340     {
341       "Checks the case that only the data reduction proxy's Via header occurs.",
342       "HTTP/1.1 200 OK\n"
343       "Via:  1.1 Chrome Compression Proxy    \n",
344       "",
345       false,
346       true,
347     },
348     {
349       "Checks the case that there are ' ', i.e., empty value after the data"
350       "reduction proxy's Via header.",
351       "HTTP/1.1 200 OK\n"
352       "Via:  1.1 Chrome Compression Proxy  ,  , \n",
353       "",
354       false,
355       true,
356     },
357     {
358       "Checks the case when there is no Via header",
359       "HTTP/1.1 200 OK\n",
360       "",
361       true,
362       false,
363     },
364   };
365
366   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) {
367     std::string raw_headers(test[i].raw_header);
368     HeadersToRaw(&raw_headers);
369     scoped_refptr<net::HttpResponseHeaders> headers(
370         new net::HttpResponseHeaders(raw_headers));
371
372     DataReductionProxyTamperDetection tamper_detection(headers.get(), true, 0);
373
374     bool has_chrome_proxy_via_header;
375     bool tampered = tamper_detection.ValidateViaHeader(
376         test[i].received_fingerprint, &has_chrome_proxy_via_header);
377
378     EXPECT_EQ(test[i].expected_tampered_with, tampered) << test[i].label;
379     EXPECT_EQ(test[i].expected_has_chrome_proxy_via_header,
380               has_chrome_proxy_via_header) << test[i].label;
381   }
382 }
383
384 // Tests function ValidateOtherHeaders.
385 TEST_F(DataReductionProxyTamperDetectionTest, OtherHeaders) {
386   // For following testcases, |received_fingerprint| is not the actual
387   // fingerprint from data reduction proxy, instead, the base64 encoded field
388   // is in plain text (within "[]") and needs to be encoded first. For example,
389   // "[12345;]|content-length" needs to be encoded to
390   // "Base64Encoded(MD5(12345;))|content-length" before calling the checking
391   // function.
392   struct {
393     std::string label;
394     std::string raw_header;
395     std::string received_fingerprint;
396     bool expected_tampered_with;
397   } test[] = {
398     {
399       "Checks the case that only one header is requested.",
400       "HTTP/1.1 200 OK\n"
401       "Content-Length: 12345\n",
402       "[12345,;]|content-length",
403       false
404     },
405     {
406       "Checks the case that there is only one requested header and it does not"
407       "exist.",
408       "HTTP/1.1 200 OK\n",
409       "[;]|non_exist_header",
410       false
411     },
412     {
413       "Checks the case of multiple headers are requested.",
414       "HTTP/1.1 200 OK\n"
415       "Content-Type: 1\n"
416       "Cache-Control: 2\n"
417       "ETag: 3\n"
418       "Connection: 4\n"
419       "Expires: 5\n",
420       "[1,;2,;3,;4,;5,;]|content-type|cache-control|etag|connection|expires",
421       false
422     },
423     {
424       "Checks the case that one header has multiple values.",
425       "HTTP/1.1 200 OK\n"
426       "Content-Type: aaa1,    bbb1, ccc1\n"
427       "Cache-Control: aaa2\n",
428       "[aaa1,bbb1,ccc1,;aaa2,;]|content-type|cache-control",
429       false
430     },
431     {
432       "Checks the case that one header has multiple lines.",
433       "HTTP/1.1 200 OK\n"
434       "Content-Type: aaa1,   ccc1\n"
435       "Content-Type: xxx1,    bbb1, ccc1\n"
436       "Cache-Control: aaa2\n",
437       "[aaa1,bbb1,ccc1,ccc1,xxx1,;aaa2,;]|content-type|cache-control",
438       false
439     },
440     {
441       "Checks the case that more than one headers have multiple values.",
442       "HTTP/1.1 200 OK\n"
443       "Content-Type: aaa1,   ccc1\n"
444       "Cache-Control: ccc2    , bbb2\n"
445       "Content-Type:  bbb1, ccc1\n"
446       "Cache-Control: aaa2   \n",
447       "[aaa1,bbb1,ccc1,ccc1,;aaa2,bbb2,ccc2,;]|content-type|cache-control",
448       false
449     },
450     {
451       "Checks the case that one of the requested headers is missing (Expires).",
452       "HTTP/1.1 200 OK\n"
453       "Content-Type: aaa1,   ccc1\n",
454       "[aaa1,ccc1,;;]|content-type|expires",
455       false
456     },
457     {
458       "Checks the case that some of the requested headers have empty value.",
459       "HTTP/1.1 200 OK\n"
460       "Content-Type:   \n"
461       "Cache-Control: \n",
462       "[,;,;]|content-type|cache-control",
463       false
464     },
465     {
466       "Checks the case that all the requested headers are missing.",
467       "HTTP/1.1 200 OK\n",
468       "[;;]|content-type|expires",
469       false
470     },
471     {
472       "Checks the case that some headers are missing, some of them are empty.",
473       "HTTP/1.1 200 OK\n"
474       "Cache-Control: \n",
475       "[;,;]|content-type|cache-control",
476       false
477     },
478     {
479       "Checks the case there is no requested header (header list is empty).",
480       "HTTP/1.1 200 OK\n"
481       "Chrome-Proxy: aut=aauutthh,bbbypas=0,aaxxx=xxx,bbbloc=1\n"
482       "Content-Type: 1\n"
483       "Cache-Control: 2\n",
484       "[]",
485       false
486     },
487     {
488       "Checks tampered requested header values.",
489       "HTTP/1.1 200 OK\n"
490       "Content-Type: aaa1,   ccc1\n"
491       "Cache-Control: ccc2    , bbb2\n",
492       "[aaa1,bbb1,;bbb2,ccc2,;]|content-type|cache-control",
493       true
494     },
495   };
496
497   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) {
498     ReplaceWithEncodedString(&test[i].received_fingerprint);
499
500     std::string raw_headers(test[i].raw_header);
501     HeadersToRaw(&raw_headers);
502     scoped_refptr<net::HttpResponseHeaders> headers(
503         new net::HttpResponseHeaders(raw_headers));
504
505     DataReductionProxyTamperDetection tamper_detection(headers.get(), true, 0);
506
507     bool tampered = tamper_detection.ValidateOtherHeaders(
508         test[i].received_fingerprint);
509
510     EXPECT_EQ(test[i].expected_tampered_with, tampered) << test[i].label;
511   }
512 }
513
514 // Tests function ValidateContentLengthHeader.
515 TEST_F(DataReductionProxyTamperDetectionTest, ContentLength) {
516   struct {
517     std::string label;
518     std::string raw_header;
519     std::string received_fingerprint;
520     bool expected_tampered_with;
521   } test[] = {
522     {
523       "Checks the case fingerprint matches received response.",
524       "HTTP/1.1 200 OK\n"
525       "Content-Length: 12345\n",
526       "12345",
527       false,
528     },
529     {
530       "Checks case that response got modified.",
531       "HTTP/1.1 200 OK\n"
532       "Content-Length: 12345\n",
533       "125",
534       true,
535     },
536     {
537       "Checks the case that the data reduction proxy has not sent"
538       "Content-Length header.",
539       "HTTP/1.1 200 OK\n"
540       "Content-Length: 12345\n",
541       "",
542       false,
543     },
544     {
545       "Checks the case that the data reduction proxy sends invalid"
546       "Content-Length header.",
547       "HTTP/1.1 200 OK\n"
548       "Content-Length: 12345\n",
549       "aaa",
550       false,
551     },
552     {
553       "Checks the case that the data reduction proxy sends invalid"
554       "Content-Length header.",
555       "HTTP/1.1 200 OK\n"
556       "Content-Length: aaa\n",
557       "aaa",
558       false,
559     },
560     {
561       "Checks the case that Content-Length header is missing at the Chromium"
562       "client side.",
563       "HTTP/1.1 200 OK\n",
564       "123",
565       false,
566     },
567     {
568       "Checks the case that Content-Length header are missing at both end.",
569       "HTTP/1.1 200 OK\n",
570       "",
571       false,
572     },
573   };
574
575   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) {
576     std::string raw_headers(test[i].raw_header);
577     HeadersToRaw(&raw_headers);
578     scoped_refptr<net::HttpResponseHeaders> headers(
579         new net::HttpResponseHeaders(raw_headers));
580
581     DataReductionProxyTamperDetection tamper_detection(headers.get(), true, 0);
582
583     bool tampered = tamper_detection.ValidateContentLengthHeader(
584         test[i].received_fingerprint);
585
586     EXPECT_EQ(test[i].expected_tampered_with, tampered) << test[i].label;
587   }
588 }
589
590 // Tests ValuesToSortedString function.
591 TEST_F(DataReductionProxyTamperDetectionTest, ValuesToSortedString) {
592   struct {
593     std::string label;
594     std::string input_values;
595     std::string expected_output_string;
596   } test[] = {
597     {
598       "Checks the correctness of sorting.",
599       "3,2,1,",
600       "1,2,3,",
601     },
602     {
603       "Checks the case that there is an empty input vector.",
604       "",
605       "",
606     },
607     {
608       "Checks the case that there is an empty string in the input vector.",
609       ",",
610       ",",
611     },
612   };
613
614   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) {
615     std::vector<std::string> input_values =
616         StringsToVector(test[i].input_values);
617     std::string output_string =
618         DataReductionProxyTamperDetection::ValuesToSortedString(&input_values);
619     EXPECT_EQ(output_string, test[i].expected_output_string) << test[i].label;
620   }
621 }
622
623 // Tests GetHeaderValues function.
624 TEST_F(DataReductionProxyTamperDetectionTest, GetHeaderValues) {
625   struct {
626     std::string label;
627     std::string raw_header;
628     std::string header_name;
629     std::string expected_output_values;
630   } test[] = {
631     {
632       "Checks the correctness of getting single line header.",
633       "HTTP/1.1 200 OK\n"
634       "test: 1, 2, 3\n",
635       "test",
636       "1,2,3,",
637     },
638     {
639       "Checks the correctness of getting multiple lines header.",
640       "HTTP/1.1 200 OK\n"
641       "test: 1, 2, 3\n"
642       "test: 4, 5, 6\n"
643       "test: 7, 8, 9\n",
644       "test",
645       "1,2,3,4,5,6,7,8,9,",
646     },
647     {
648       "Checks the correctness of getting missing header.",
649       "HTTP/1.1 200 OK\n",
650       "test",
651       "",
652     },
653     {
654       "Checks the correctness of getting empty header.",
655       "HTTP/1.1 200 OK\n"
656       "test:   \n",
657       "test",
658       ",",
659     },
660   };
661
662   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) {
663     std::string raw_headers(test[i].raw_header);
664     HeadersToRaw(&raw_headers);
665     scoped_refptr<net::HttpResponseHeaders> headers(
666         new net::HttpResponseHeaders(raw_headers));
667
668     std::vector<std::string> expected_output_values =
669         StringsToVector(test[i].expected_output_values);
670
671     std::vector<std::string> output_values =
672         DataReductionProxyTamperDetection::GetHeaderValues(headers.get(),
673                                                            test[i].header_name);
674     EXPECT_EQ(expected_output_values, output_values) << test[i].label;
675   }
676 }
677
678 // Tests main function DetectAndReport.
679 TEST_F(DataReductionProxyTamperDetectionTest, DetectAndReport) {
680   struct {
681     std::string label;
682     std::string raw_header;
683     bool expected_tampered_with;
684   } test[] = {
685     {
686       "Check no fingerprint added case.",
687       "HTTP/1.1 200 OK\n"
688       "Via: a1, b2, 1.1 Chrome-Compression-Proxy\n"
689       "Content-Length: 12345\n"
690       "Chrome-Proxy: bypass=0\n",
691       false,
692     },
693     {
694       "Check the case Chrome-Proxy fingerprint doesn't match.",
695       "HTTP/1.1 200 OK\n"
696       "Via: a1, b2, 1.1 Chrome-Compression-Proxy\n"
697       "Content-Length: 12345\n"
698       "header1: header_1\n"
699       "header2: header_2\n"
700       "header3: header_3\n"
701       "Chrome-Proxy: fcl=12345, "
702       "foh=[header_1,;header_2,;header_3,;]|header1|header2|header3,fvia=0,"
703       "fcp=abc\n",
704       true,
705     },
706     {
707       "Check the case response matches the fingerprint completely.",
708       "HTTP/1.1 200 OK\n"
709       "Via: a1, b2, 1.1 Chrome-Compression-Proxy\n"
710       "Content-Length: 12345\n"
711       "header1: header_1\n"
712       "header2: header_2\n"
713       "header3: header_3\n"
714       "Chrome-Proxy: fcl=12345, "
715       "foh=[header_1,;header_2,;header_3,;]|header1|header2|header3,"
716       "fvia=0, fcp=[fcl=12345,foh=[header_1,;header_2,;header_3,;]"
717       "|header1|header2|header3,fvia=0,]\n",
718       false,
719     },
720     {
721       "Check the case that Content-Length doesn't match.",
722       "HTTP/1.1 200 OK\n"
723       "Via: a1, b2, 1.1 Chrome-Compression-Proxy\n"
724       "Content-Length: 0\n"
725       "header1: header_1\n"
726       "header2: header_2\n"
727       "header3: header_3\n"
728       "Chrome-Proxy: fcl=12345, "
729       "foh=[header_1,;header_2,;header_3,;]|header1|header2|header3, fvia=0, "
730       "fcp=[fcl=12345,foh=[header_1,;header_2,;header_3,;]|"
731       "header1|header2|header3,fvia=0,]\n",
732       true,
733     },
734   };
735
736   InitEnv();
737
738   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) {
739     std::string raw_headers(test[i].raw_header);
740     ReplaceWithEncodedString(&raw_headers);
741     HeadersToRaw(&raw_headers);
742     scoped_refptr<net::HttpResponseHeaders> headers(
743         new net::HttpResponseHeaders(raw_headers));
744
745     EXPECT_EQ(
746         test[i].expected_tampered_with,
747         DataReductionProxyTamperDetection::DetectAndReport(headers.get(), true))
748         << test[i].label;
749   }
750 }
751
752 } // namespace