xref: /aosp_15_r20/system/update_engine/libcurl_http_fetcher_unittest.cc (revision 5a9231315b4521097b8dc3750bc806fcafe0c72f)
1*5a923131SAndroid Build Coastguard Worker //
2*5a923131SAndroid Build Coastguard Worker // Copyright (C) 2019 The Android Open Source Project
3*5a923131SAndroid Build Coastguard Worker //
4*5a923131SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
5*5a923131SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
6*5a923131SAndroid Build Coastguard Worker // You may obtain a copy of the License at
7*5a923131SAndroid Build Coastguard Worker //
8*5a923131SAndroid Build Coastguard Worker //      http://www.apache.org/licenses/LICENSE-2.0
9*5a923131SAndroid Build Coastguard Worker //
10*5a923131SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
11*5a923131SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
12*5a923131SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*5a923131SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
14*5a923131SAndroid Build Coastguard Worker // limitations under the License.
15*5a923131SAndroid Build Coastguard Worker //
16*5a923131SAndroid Build Coastguard Worker 
17*5a923131SAndroid Build Coastguard Worker #include "update_engine/libcurl_http_fetcher.h"
18*5a923131SAndroid Build Coastguard Worker 
19*5a923131SAndroid Build Coastguard Worker #include <string>
20*5a923131SAndroid Build Coastguard Worker 
21*5a923131SAndroid Build Coastguard Worker #include <brillo/message_loops/fake_message_loop.h>
22*5a923131SAndroid Build Coastguard Worker #include <gmock/gmock.h>
23*5a923131SAndroid Build Coastguard Worker #include <gtest/gtest.h>
24*5a923131SAndroid Build Coastguard Worker 
25*5a923131SAndroid Build Coastguard Worker #include "update_engine/common/fake_hardware.h"
26*5a923131SAndroid Build Coastguard Worker #include "update_engine/mock_libcurl_http_fetcher.h"
27*5a923131SAndroid Build Coastguard Worker 
28*5a923131SAndroid Build Coastguard Worker using std::string;
29*5a923131SAndroid Build Coastguard Worker 
30*5a923131SAndroid Build Coastguard Worker namespace chromeos_update_engine {
31*5a923131SAndroid Build Coastguard Worker 
32*5a923131SAndroid Build Coastguard Worker namespace {
33*5a923131SAndroid Build Coastguard Worker 
34*5a923131SAndroid Build Coastguard Worker constexpr char kHeaderName[] = "X-Goog-Test-Header";
35*5a923131SAndroid Build Coastguard Worker 
36*5a923131SAndroid Build Coastguard Worker class LibcurlHttpFetcherTest : public ::testing::Test {
37*5a923131SAndroid Build Coastguard Worker  protected:
SetUp()38*5a923131SAndroid Build Coastguard Worker   void SetUp() override {
39*5a923131SAndroid Build Coastguard Worker     loop_.SetAsCurrent();
40*5a923131SAndroid Build Coastguard Worker     fake_hardware_.SetIsOfficialBuild(true);
41*5a923131SAndroid Build Coastguard Worker     fake_hardware_.SetIsOOBEEnabled(false);
42*5a923131SAndroid Build Coastguard Worker   }
43*5a923131SAndroid Build Coastguard Worker 
44*5a923131SAndroid Build Coastguard Worker   brillo::FakeMessageLoop loop_{nullptr};
45*5a923131SAndroid Build Coastguard Worker   FakeHardware fake_hardware_;
46*5a923131SAndroid Build Coastguard Worker   MockLibcurlHttpFetcher libcurl_fetcher_{&fake_hardware_};
47*5a923131SAndroid Build Coastguard Worker   UnresolvedHostStateMachine state_machine_;
48*5a923131SAndroid Build Coastguard Worker };
49*5a923131SAndroid Build Coastguard Worker 
50*5a923131SAndroid Build Coastguard Worker }  // namespace
51*5a923131SAndroid Build Coastguard Worker 
TEST_F(LibcurlHttpFetcherTest,GetEmptyHeaderValueTest)52*5a923131SAndroid Build Coastguard Worker TEST_F(LibcurlHttpFetcherTest, GetEmptyHeaderValueTest) {
53*5a923131SAndroid Build Coastguard Worker   const string header_value = "";
54*5a923131SAndroid Build Coastguard Worker   string actual_header_value;
55*5a923131SAndroid Build Coastguard Worker   libcurl_fetcher_.SetHeader(kHeaderName, header_value);
56*5a923131SAndroid Build Coastguard Worker   EXPECT_TRUE(libcurl_fetcher_.GetHeader(kHeaderName, &actual_header_value));
57*5a923131SAndroid Build Coastguard Worker   EXPECT_EQ("", actual_header_value);
58*5a923131SAndroid Build Coastguard Worker }
59*5a923131SAndroid Build Coastguard Worker 
TEST_F(LibcurlHttpFetcherTest,GetHeaderTest)60*5a923131SAndroid Build Coastguard Worker TEST_F(LibcurlHttpFetcherTest, GetHeaderTest) {
61*5a923131SAndroid Build Coastguard Worker   const string header_value = "This-is-value 123";
62*5a923131SAndroid Build Coastguard Worker   string actual_header_value;
63*5a923131SAndroid Build Coastguard Worker   libcurl_fetcher_.SetHeader(kHeaderName, header_value);
64*5a923131SAndroid Build Coastguard Worker   EXPECT_TRUE(libcurl_fetcher_.GetHeader(kHeaderName, &actual_header_value));
65*5a923131SAndroid Build Coastguard Worker   EXPECT_EQ(header_value, actual_header_value);
66*5a923131SAndroid Build Coastguard Worker }
67*5a923131SAndroid Build Coastguard Worker 
TEST_F(LibcurlHttpFetcherTest,GetNonExistentHeaderValueTest)68*5a923131SAndroid Build Coastguard Worker TEST_F(LibcurlHttpFetcherTest, GetNonExistentHeaderValueTest) {
69*5a923131SAndroid Build Coastguard Worker   string actual_header_value;
70*5a923131SAndroid Build Coastguard Worker   // Skip |SetHeaader()| call.
71*5a923131SAndroid Build Coastguard Worker   EXPECT_FALSE(libcurl_fetcher_.GetHeader(kHeaderName, &actual_header_value));
72*5a923131SAndroid Build Coastguard Worker   // Even after a failed |GetHeaderValue()|, enforce that the passed pointer to
73*5a923131SAndroid Build Coastguard Worker   // modifiable string was cleared to be empty.
74*5a923131SAndroid Build Coastguard Worker   EXPECT_EQ("", actual_header_value);
75*5a923131SAndroid Build Coastguard Worker }
76*5a923131SAndroid Build Coastguard Worker 
TEST_F(LibcurlHttpFetcherTest,GetHeaderEdgeCaseTest)77*5a923131SAndroid Build Coastguard Worker TEST_F(LibcurlHttpFetcherTest, GetHeaderEdgeCaseTest) {
78*5a923131SAndroid Build Coastguard Worker   const string header_value = "\a\b\t\v\f\r\\ edge:-case: \a\b\t\v\f\r\\";
79*5a923131SAndroid Build Coastguard Worker   string actual_header_value;
80*5a923131SAndroid Build Coastguard Worker   libcurl_fetcher_.SetHeader(kHeaderName, header_value);
81*5a923131SAndroid Build Coastguard Worker   EXPECT_TRUE(libcurl_fetcher_.GetHeader(kHeaderName, &actual_header_value));
82*5a923131SAndroid Build Coastguard Worker   EXPECT_EQ(header_value, actual_header_value);
83*5a923131SAndroid Build Coastguard Worker }
84*5a923131SAndroid Build Coastguard Worker 
TEST_F(LibcurlHttpFetcherTest,InvalidURLTest)85*5a923131SAndroid Build Coastguard Worker TEST_F(LibcurlHttpFetcherTest, InvalidURLTest) {
86*5a923131SAndroid Build Coastguard Worker   int no_network_max_retries = 1;
87*5a923131SAndroid Build Coastguard Worker   libcurl_fetcher_.set_no_network_max_retries(no_network_max_retries);
88*5a923131SAndroid Build Coastguard Worker 
89*5a923131SAndroid Build Coastguard Worker   libcurl_fetcher_.BeginTransfer("not-a-URL");
90*5a923131SAndroid Build Coastguard Worker   while (loop_.PendingTasks()) {
91*5a923131SAndroid Build Coastguard Worker     loop_.RunOnce(true);
92*5a923131SAndroid Build Coastguard Worker   }
93*5a923131SAndroid Build Coastguard Worker 
94*5a923131SAndroid Build Coastguard Worker   EXPECT_EQ(libcurl_fetcher_.get_no_network_max_retries(),
95*5a923131SAndroid Build Coastguard Worker             no_network_max_retries);
96*5a923131SAndroid Build Coastguard Worker }
97*5a923131SAndroid Build Coastguard Worker 
TEST_F(LibcurlHttpFetcherTest,CouldNotResolveHostTest)98*5a923131SAndroid Build Coastguard Worker TEST_F(LibcurlHttpFetcherTest, CouldNotResolveHostTest) {
99*5a923131SAndroid Build Coastguard Worker   int no_network_max_retries = 1;
100*5a923131SAndroid Build Coastguard Worker   libcurl_fetcher_.set_no_network_max_retries(no_network_max_retries);
101*5a923131SAndroid Build Coastguard Worker 
102*5a923131SAndroid Build Coastguard Worker   libcurl_fetcher_.BeginTransfer("https://An-uNres0lvable-uRl.invalid");
103*5a923131SAndroid Build Coastguard Worker 
104*5a923131SAndroid Build Coastguard Worker   // It's slower on Android that libcurl handle may not finish within 1 cycle.
105*5a923131SAndroid Build Coastguard Worker   // Will need to wait for more cycles until it finishes. Original test didn't
106*5a923131SAndroid Build Coastguard Worker   // correctly handle when we need to re-watch libcurl fds.
107*5a923131SAndroid Build Coastguard Worker   while (loop_.PendingTasks() &&
108*5a923131SAndroid Build Coastguard Worker          libcurl_fetcher_.GetAuxiliaryErrorCode() == ErrorCode::kSuccess) {
109*5a923131SAndroid Build Coastguard Worker     loop_.RunOnce(true);
110*5a923131SAndroid Build Coastguard Worker   }
111*5a923131SAndroid Build Coastguard Worker 
112*5a923131SAndroid Build Coastguard Worker   EXPECT_EQ(libcurl_fetcher_.GetAuxiliaryErrorCode(),
113*5a923131SAndroid Build Coastguard Worker             ErrorCode::kUnresolvedHostError);
114*5a923131SAndroid Build Coastguard Worker 
115*5a923131SAndroid Build Coastguard Worker   while (loop_.PendingTasks()) {
116*5a923131SAndroid Build Coastguard Worker     loop_.RunOnce(true);
117*5a923131SAndroid Build Coastguard Worker   }
118*5a923131SAndroid Build Coastguard Worker   // The auxilary error code should've have been changed.
119*5a923131SAndroid Build Coastguard Worker   EXPECT_EQ(libcurl_fetcher_.GetAuxiliaryErrorCode(),
120*5a923131SAndroid Build Coastguard Worker             ErrorCode::kUnresolvedHostError);
121*5a923131SAndroid Build Coastguard Worker 
122*5a923131SAndroid Build Coastguard Worker   // If libcurl fails to resolve the name, we call res_init() to reload
123*5a923131SAndroid Build Coastguard Worker   // resolv.conf and retry exactly once more. See crbug.com/982813 for details.
124*5a923131SAndroid Build Coastguard Worker   EXPECT_EQ(libcurl_fetcher_.get_no_network_max_retries(),
125*5a923131SAndroid Build Coastguard Worker             no_network_max_retries + 1);
126*5a923131SAndroid Build Coastguard Worker }
127*5a923131SAndroid Build Coastguard Worker 
TEST_F(LibcurlHttpFetcherTest,HostResolvedTest)128*5a923131SAndroid Build Coastguard Worker TEST_F(LibcurlHttpFetcherTest, HostResolvedTest) {
129*5a923131SAndroid Build Coastguard Worker   int no_network_max_retries = 2;
130*5a923131SAndroid Build Coastguard Worker   libcurl_fetcher_.set_no_network_max_retries(no_network_max_retries);
131*5a923131SAndroid Build Coastguard Worker 
132*5a923131SAndroid Build Coastguard Worker   // This test actually sends request to internet but according to
133*5a923131SAndroid Build Coastguard Worker   // https://tools.ietf.org/html/rfc2606#section-2, .invalid domain names are
134*5a923131SAndroid Build Coastguard Worker   // reserved and sure to be invalid. Ideally we should mock libcurl or
135*5a923131SAndroid Build Coastguard Worker   // reorganize LibcurlHttpFetcher so the part that sends request can be mocked
136*5a923131SAndroid Build Coastguard Worker   // easily.
137*5a923131SAndroid Build Coastguard Worker   // TODO(xiaochu) Refactor LibcurlHttpFetcher (and its relates) so it's
138*5a923131SAndroid Build Coastguard Worker   // easier to mock the part that depends on internet connectivity.
139*5a923131SAndroid Build Coastguard Worker   libcurl_fetcher_.BeginTransfer("https://An-uNres0lvable-uRl.invalid");
140*5a923131SAndroid Build Coastguard Worker 
141*5a923131SAndroid Build Coastguard Worker   // It's slower on Android that libcurl handle may not finish within 1 cycle.
142*5a923131SAndroid Build Coastguard Worker   // Will need to wait for more cycles until it finishes. Original test didn't
143*5a923131SAndroid Build Coastguard Worker   // correctly handle when we need to re-watch libcurl fds.
144*5a923131SAndroid Build Coastguard Worker   while (loop_.PendingTasks() &&
145*5a923131SAndroid Build Coastguard Worker          libcurl_fetcher_.GetAuxiliaryErrorCode() == ErrorCode::kSuccess) {
146*5a923131SAndroid Build Coastguard Worker     loop_.RunOnce(true);
147*5a923131SAndroid Build Coastguard Worker   }
148*5a923131SAndroid Build Coastguard Worker 
149*5a923131SAndroid Build Coastguard Worker   EXPECT_EQ(libcurl_fetcher_.GetAuxiliaryErrorCode(),
150*5a923131SAndroid Build Coastguard Worker             ErrorCode::kUnresolvedHostError);
151*5a923131SAndroid Build Coastguard Worker 
152*5a923131SAndroid Build Coastguard Worker   // The second time, it will resolve, with error code 200 but we set the
153*5a923131SAndroid Build Coastguard Worker   // download size be smaller than the transfer size so it will retry again.
154*5a923131SAndroid Build Coastguard Worker   EXPECT_CALL(libcurl_fetcher_, GetHttpResponseCode())
155*5a923131SAndroid Build Coastguard Worker       .WillOnce(testing::Invoke(
156*5a923131SAndroid Build Coastguard Worker           [this]() { libcurl_fetcher_.http_response_code_ = 200; }))
157*5a923131SAndroid Build Coastguard Worker       .WillRepeatedly(testing::Invoke(
158*5a923131SAndroid Build Coastguard Worker           [this]() { libcurl_fetcher_.http_response_code_ = 0; }));
159*5a923131SAndroid Build Coastguard Worker   libcurl_fetcher_.transfer_size_ = 10;
160*5a923131SAndroid Build Coastguard Worker 
161*5a923131SAndroid Build Coastguard Worker   // It's slower on Android that libcurl handle may not finish within 1 cycle.
162*5a923131SAndroid Build Coastguard Worker   // Will need to wait for more cycles until it finishes. Original test didn't
163*5a923131SAndroid Build Coastguard Worker   // correctly handle when we need to re-watch libcurl fds.
164*5a923131SAndroid Build Coastguard Worker   while (loop_.PendingTasks() && libcurl_fetcher_.GetAuxiliaryErrorCode() ==
165*5a923131SAndroid Build Coastguard Worker                                      ErrorCode::kUnresolvedHostError) {
166*5a923131SAndroid Build Coastguard Worker     loop_.RunOnce(true);
167*5a923131SAndroid Build Coastguard Worker   }
168*5a923131SAndroid Build Coastguard Worker 
169*5a923131SAndroid Build Coastguard Worker   EXPECT_EQ(libcurl_fetcher_.GetAuxiliaryErrorCode(),
170*5a923131SAndroid Build Coastguard Worker             ErrorCode::kUnresolvedHostRecovered);
171*5a923131SAndroid Build Coastguard Worker 
172*5a923131SAndroid Build Coastguard Worker   while (loop_.PendingTasks()) {
173*5a923131SAndroid Build Coastguard Worker     loop_.RunOnce(true);
174*5a923131SAndroid Build Coastguard Worker   }
175*5a923131SAndroid Build Coastguard Worker   // The auxilary error code should not have been changed.
176*5a923131SAndroid Build Coastguard Worker   EXPECT_EQ(libcurl_fetcher_.GetAuxiliaryErrorCode(),
177*5a923131SAndroid Build Coastguard Worker             ErrorCode::kUnresolvedHostRecovered);
178*5a923131SAndroid Build Coastguard Worker 
179*5a923131SAndroid Build Coastguard Worker   // If libcurl fails to resolve the name, we call res_init() to reload
180*5a923131SAndroid Build Coastguard Worker   // resolv.conf and retry exactly once more. See crbug.com/982813 for details.
181*5a923131SAndroid Build Coastguard Worker   EXPECT_EQ(libcurl_fetcher_.get_no_network_max_retries(),
182*5a923131SAndroid Build Coastguard Worker             no_network_max_retries + 1);
183*5a923131SAndroid Build Coastguard Worker }
184*5a923131SAndroid Build Coastguard Worker 
TEST_F(LibcurlHttpFetcherTest,HttpFetcherStateMachineRetryFailedTest)185*5a923131SAndroid Build Coastguard Worker TEST_F(LibcurlHttpFetcherTest, HttpFetcherStateMachineRetryFailedTest) {
186*5a923131SAndroid Build Coastguard Worker   state_machine_.UpdateState(true);
187*5a923131SAndroid Build Coastguard Worker   state_machine_.UpdateState(true);
188*5a923131SAndroid Build Coastguard Worker   EXPECT_EQ(state_machine_.GetState(),
189*5a923131SAndroid Build Coastguard Worker             UnresolvedHostStateMachine::State::kNotRetry);
190*5a923131SAndroid Build Coastguard Worker }
191*5a923131SAndroid Build Coastguard Worker 
TEST_F(LibcurlHttpFetcherTest,HttpFetcherStateMachineRetrySucceedTest)192*5a923131SAndroid Build Coastguard Worker TEST_F(LibcurlHttpFetcherTest, HttpFetcherStateMachineRetrySucceedTest) {
193*5a923131SAndroid Build Coastguard Worker   state_machine_.UpdateState(true);
194*5a923131SAndroid Build Coastguard Worker   state_machine_.UpdateState(false);
195*5a923131SAndroid Build Coastguard Worker   EXPECT_EQ(state_machine_.GetState(),
196*5a923131SAndroid Build Coastguard Worker             UnresolvedHostStateMachine::State::kRetriedSuccess);
197*5a923131SAndroid Build Coastguard Worker }
198*5a923131SAndroid Build Coastguard Worker 
TEST_F(LibcurlHttpFetcherTest,HttpFetcherStateMachineNoRetryTest)199*5a923131SAndroid Build Coastguard Worker TEST_F(LibcurlHttpFetcherTest, HttpFetcherStateMachineNoRetryTest) {
200*5a923131SAndroid Build Coastguard Worker   state_machine_.UpdateState(false);
201*5a923131SAndroid Build Coastguard Worker   state_machine_.UpdateState(false);
202*5a923131SAndroid Build Coastguard Worker   EXPECT_EQ(state_machine_.GetState(),
203*5a923131SAndroid Build Coastguard Worker             UnresolvedHostStateMachine::State::kInit);
204*5a923131SAndroid Build Coastguard Worker }
205*5a923131SAndroid Build Coastguard Worker 
206*5a923131SAndroid Build Coastguard Worker }  // namespace chromeos_update_engine
207