1 // Copyright 2015 The Chromium Authors
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/metrics/net/net_metrics_log_uploader.h"
6
7 #include <memory>
8
9 #include "base/base64.h"
10 #include "base/functional/bind.h"
11 #include "base/run_loop.h"
12 #include "base/test/bind.h"
13 #include "base/test/task_environment.h"
14 #include "components/encrypted_messages/encrypted_message.pb.h"
15 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
16 #include "services/network/test/test_url_loader_factory.h"
17 #include "services/network/test/test_utils.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "third_party/metrics_proto/reporting_info.pb.h"
20 #include "third_party/zlib/google/compression_utils.h"
21 #include "url/gurl.h"
22
23 namespace metrics {
24
25 class NetMetricsLogUploaderTest : public testing::Test {
26 public:
NetMetricsLogUploaderTest()27 NetMetricsLogUploaderTest()
28 : on_upload_complete_count_(0),
29 test_shared_url_loader_factory_(
30 base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
31 &test_url_loader_factory_)) {
32 test_url_loader_factory_.SetInterceptor(base::BindLambdaForTesting(
33 [&](const network::ResourceRequest& request) {
34 upload_data_ = network::GetUploadData(request);
35 headers_ = request.headers;
36 loop_.Quit();
37 }));
38 }
39
40 NetMetricsLogUploaderTest(const NetMetricsLogUploaderTest&) = delete;
41 NetMetricsLogUploaderTest& operator=(const NetMetricsLogUploaderTest&) =
42 delete;
43
CreateAndOnUploadCompleteReuseUploader()44 void CreateAndOnUploadCompleteReuseUploader() {
45 ReportingInfo reporting_info;
46 reporting_info.set_attempt_count(10);
47 uploader_ = std::make_unique<NetMetricsLogUploader>(
48 test_shared_url_loader_factory_, GURL("https://dummy_server"),
49 "dummy_mime", MetricsLogUploader::UMA,
50 base::BindRepeating(
51 &NetMetricsLogUploaderTest::OnUploadCompleteReuseUploader,
52 base::Unretained(this)));
53 uploader_->UploadLog("initial_dummy_data", LogMetadata(),
54 "initial_dummy_hash", "initial_dummy_signature",
55 reporting_info);
56 }
57
CreateUploaderAndUploadToSecureURL(const std::string & url)58 void CreateUploaderAndUploadToSecureURL(const std::string& url) {
59 ReportingInfo dummy_reporting_info;
60 uploader_ = std::make_unique<NetMetricsLogUploader>(
61 test_shared_url_loader_factory_, GURL(url), "dummy_mime",
62 MetricsLogUploader::UMA,
63 base::BindRepeating(&NetMetricsLogUploaderTest::DummyOnUploadComplete,
64 base::Unretained(this)));
65 uploader_->UploadLog("dummy_data", LogMetadata(), "dummy_hash",
66 "dummy_signature", dummy_reporting_info);
67 }
68
CreateUploaderAndUploadToInsecureURL()69 void CreateUploaderAndUploadToInsecureURL() {
70 ReportingInfo dummy_reporting_info;
71 uploader_ = std::make_unique<NetMetricsLogUploader>(
72 test_shared_url_loader_factory_, GURL("http://dummy_insecure_server"),
73 "dummy_mime", MetricsLogUploader::UMA,
74 base::BindRepeating(&NetMetricsLogUploaderTest::DummyOnUploadComplete,
75 base::Unretained(this)));
76 std::string compressed_message;
77 // Compress the data since the encryption code expects a compressed log,
78 // and tries to decompress it before encrypting it.
79 compression::GzipCompress("dummy_data", &compressed_message);
80 uploader_->UploadLog(compressed_message, LogMetadata(), "dummy_hash",
81 "dummy_signature", dummy_reporting_info);
82 }
83
DummyOnUploadComplete(int response_code,int error_code,bool was_https,bool force_discard,base::StringPiece force_discard_reason)84 void DummyOnUploadComplete(int response_code,
85 int error_code,
86 bool was_https,
87 bool force_discard,
88 base::StringPiece force_discard_reason) {
89 log_was_force_discarded_ = force_discard;
90 }
91
OnUploadCompleteReuseUploader(int response_code,int error_code,bool was_https,bool force_discard,base::StringPiece force_discard_reason)92 void OnUploadCompleteReuseUploader(int response_code,
93 int error_code,
94 bool was_https,
95 bool force_discard,
96 base::StringPiece force_discard_reason) {
97 ++on_upload_complete_count_;
98 if (on_upload_complete_count_ == 1) {
99 ReportingInfo reporting_info;
100 reporting_info.set_attempt_count(20);
101 uploader_->UploadLog("dummy_data", LogMetadata(), "dummy_hash",
102 "dummy_signature", reporting_info);
103 }
104 log_was_force_discarded_ = force_discard;
105 }
106
on_upload_complete_count() const107 int on_upload_complete_count() const {
108 return on_upload_complete_count_;
109 }
110
test_url_loader_factory()111 network::TestURLLoaderFactory* test_url_loader_factory() {
112 return &test_url_loader_factory_;
113 }
114
last_request_headers()115 const net::HttpRequestHeaders& last_request_headers() { return headers_; }
116
last_upload_data()117 const std::string& last_upload_data() { return upload_data_; }
118
WaitForRequest()119 void WaitForRequest() { loop_.Run(); }
120
log_was_force_discarded()121 bool log_was_force_discarded() { return log_was_force_discarded_; }
122
123 private:
124 std::unique_ptr<NetMetricsLogUploader> uploader_;
125 int on_upload_complete_count_;
126
127 network::TestURLLoaderFactory test_url_loader_factory_;
128 scoped_refptr<network::SharedURLLoaderFactory>
129 test_shared_url_loader_factory_;
130
131 base::test::TaskEnvironment task_environment_;
132
133 base::RunLoop loop_;
134 std::string upload_data_;
135 net::HttpRequestHeaders headers_;
136 bool log_was_force_discarded_ = false;
137 };
138
CheckReportingInfoHeader(net::HttpRequestHeaders headers,int expected_attempt_count)139 void CheckReportingInfoHeader(net::HttpRequestHeaders headers,
140 int expected_attempt_count) {
141 std::string reporting_info_base64;
142 EXPECT_TRUE(
143 headers.GetHeader("X-Chrome-UMA-ReportingInfo", &reporting_info_base64));
144 std::string reporting_info_string;
145 EXPECT_TRUE(
146 base::Base64Decode(reporting_info_base64, &reporting_info_string));
147 ReportingInfo reporting_info;
148 EXPECT_TRUE(reporting_info.ParseFromString(reporting_info_string));
149 EXPECT_EQ(reporting_info.attempt_count(), expected_attempt_count);
150 }
151
TEST_F(NetMetricsLogUploaderTest,OnUploadCompleteReuseUploader)152 TEST_F(NetMetricsLogUploaderTest, OnUploadCompleteReuseUploader) {
153 CreateAndOnUploadCompleteReuseUploader();
154 WaitForRequest();
155
156 // Mimic the initial fetcher callback.
157 CheckReportingInfoHeader(last_request_headers(), 10);
158 auto* pending_request_0 = test_url_loader_factory()->GetPendingRequest(0);
159 test_url_loader_factory()->SimulateResponseWithoutRemovingFromPendingList(
160 pending_request_0, "");
161
162 // Mimic the second fetcher callback.
163 CheckReportingInfoHeader(last_request_headers(), 20);
164 auto* pending_request_1 = test_url_loader_factory()->GetPendingRequest(1);
165 test_url_loader_factory()->SimulateResponseWithoutRemovingFromPendingList(
166 pending_request_1, "");
167
168 EXPECT_EQ(on_upload_complete_count(), 2);
169 EXPECT_FALSE(log_was_force_discarded());
170 }
171
172 // Verifies that when no server URLs are specified, the logs are forcibly
173 // discarded.
TEST_F(NetMetricsLogUploaderTest,ForceDiscard)174 TEST_F(NetMetricsLogUploaderTest, ForceDiscard) {
175 CreateUploaderAndUploadToSecureURL(/*url=*/"");
176 WaitForRequest();
177
178 // Mimic the initial fetcher callback.
179 auto* pending_request_0 = test_url_loader_factory()->GetPendingRequest(0);
180 test_url_loader_factory()->SimulateResponseWithoutRemovingFromPendingList(
181 pending_request_0, "");
182
183 EXPECT_TRUE(log_was_force_discarded());
184 }
185
186 // Test that attempting to upload to an HTTP URL results in an encrypted
187 // message.
TEST_F(NetMetricsLogUploaderTest,MessageOverHTTPIsEncrypted)188 TEST_F(NetMetricsLogUploaderTest, MessageOverHTTPIsEncrypted) {
189 CreateUploaderAndUploadToInsecureURL();
190 WaitForRequest();
191 encrypted_messages::EncryptedMessage message;
192 EXPECT_TRUE(message.ParseFromString(last_upload_data()));
193 }
194
195 // Test that attempting to upload to an HTTPS URL results in an unencrypted
196 // message.
TEST_F(NetMetricsLogUploaderTest,MessageOverHTTPSIsNotEncrypted)197 TEST_F(NetMetricsLogUploaderTest, MessageOverHTTPSIsNotEncrypted) {
198 CreateUploaderAndUploadToSecureURL("https://dummy_secure_server");
199 WaitForRequest();
200 EXPECT_EQ(last_upload_data(), "dummy_data");
201 }
202
203 // Test that attempting to upload to localhost over http results in an
204 // unencrypted message.
TEST_F(NetMetricsLogUploaderTest,MessageOverHTTPLocalhostIsNotEncrypted)205 TEST_F(NetMetricsLogUploaderTest, MessageOverHTTPLocalhostIsNotEncrypted) {
206 CreateUploaderAndUploadToSecureURL("http://localhost");
207 WaitForRequest();
208 EXPECT_EQ(last_upload_data(), "dummy_data");
209 }
210
211 } // namespace metrics
212