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