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