1 // 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> 22 #include <gmock/gmock.h> 23 #include <gtest/gtest.h> 24 25 #include "update_engine/common/fake_hardware.h" 26 #include "update_engine/common/mock_proxy_resolver.h" 27 #include "update_engine/mock_libcurl_http_fetcher.h" 28 29 using std::string; 30 31 namespace chromeos_update_engine { 32 33 namespace { 34 constexpr char kHeaderName[] = "X-Goog-Test-Header"; 35 } 36 37 class 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_; 47 MockLibcurlHttpFetcher libcurl_fetcher_{nullptr, &fake_hardware_}; 48 UnresolvedHostStateMachine state_machine_; 49 }; 50 51 TEST_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 59 TEST_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 67 TEST_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 76 TEST_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 84 TEST_F(LibcurlHttpFetcherTest, InvalidURLTest) { 85 int no_network_max_retries = 1; 86 libcurl_fetcher_.set_no_network_max_retries(no_network_max_retries); 87 88 libcurl_fetcher_.BeginTransfer("not-a-URL"); 89 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 97 TEST_F(LibcurlHttpFetcherTest, CouldNotResolveHostTest) { 98 int no_network_max_retries = 1; 99 libcurl_fetcher_.set_no_network_max_retries(no_network_max_retries); 100 101 libcurl_fetcher_.BeginTransfer("https://An-uNres0lvable-uRl.invalid"); 102 103 // It's slower on Android that libcurl handle may not finish within 1 cycle. 104 // Will need to wait for more cycles until it finishes. Original test didn't 105 // correctly handle when we need to re-watch libcurl fds. 106 while (loop_.PendingTasks() && 107 libcurl_fetcher_.GetAuxiliaryErrorCode() == ErrorCode::kSuccess) { 108 loop_.RunOnce(true); 109 } 110 111 EXPECT_EQ(libcurl_fetcher_.GetAuxiliaryErrorCode(), 112 ErrorCode::kUnresolvedHostError); 113 114 while (loop_.PendingTasks()) { 115 loop_.RunOnce(true); 116 } 117 // The auxilary error code should've have been changed. 118 EXPECT_EQ(libcurl_fetcher_.GetAuxiliaryErrorCode(), 119 ErrorCode::kUnresolvedHostError); 120 121 // If libcurl fails to resolve the name, we call res_init() to reload 122 // resolv.conf and retry exactly once more. See crbug.com/982813 for details. 123 EXPECT_EQ(libcurl_fetcher_.get_no_network_max_retries(), 124 no_network_max_retries + 1); 125 } 126 127 TEST_F(LibcurlHttpFetcherTest, HostResolvedTest) { 128 int no_network_max_retries = 2; 129 libcurl_fetcher_.set_no_network_max_retries(no_network_max_retries); 130 131 // This test actually sends request to internet but according to 132 // https://tools.ietf.org/html/rfc2606#section-2, .invalid domain names are 133 // reserved and sure to be invalid. Ideally we should mock libcurl or 134 // reorganize LibcurlHttpFetcher so the part that sends request can be mocked 135 // easily. 136 // TODO(xiaochu) Refactor LibcurlHttpFetcher (and its relates) so it's 137 // easier to mock the part that depends on internet connectivity. 138 libcurl_fetcher_.BeginTransfer("https://An-uNres0lvable-uRl.invalid"); 139 140 // It's slower on Android that libcurl handle may not finish within 1 cycle. 141 // Will need to wait for more cycles until it finishes. Original test didn't 142 // correctly handle when we need to re-watch libcurl fds. 143 while (loop_.PendingTasks() && 144 libcurl_fetcher_.GetAuxiliaryErrorCode() == ErrorCode::kSuccess) { 145 loop_.RunOnce(true); 146 } 147 148 EXPECT_EQ(libcurl_fetcher_.GetAuxiliaryErrorCode(), 149 ErrorCode::kUnresolvedHostError); 150 151 // The second time, it will resolve, with error code 200 but we set the 152 // download size be smaller than the transfer size so it will retry again. 153 EXPECT_CALL(libcurl_fetcher_, GetHttpResponseCode()) 154 .WillOnce(testing::Invoke( 155 [this]() { libcurl_fetcher_.http_response_code_ = 200; })) 156 .WillRepeatedly(testing::Invoke( 157 [this]() { libcurl_fetcher_.http_response_code_ = 0; })); 158 libcurl_fetcher_.transfer_size_ = 10; 159 160 // It's slower on Android that libcurl handle may not finish within 1 cycle. 161 // Will need to wait for more cycles until it finishes. Original test didn't 162 // correctly handle when we need to re-watch libcurl fds. 163 while (loop_.PendingTasks() && libcurl_fetcher_.GetAuxiliaryErrorCode() == 164 ErrorCode::kUnresolvedHostError) { 165 loop_.RunOnce(true); 166 } 167 168 EXPECT_EQ(libcurl_fetcher_.GetAuxiliaryErrorCode(), 169 ErrorCode::kUnresolvedHostRecovered); 170 171 while (loop_.PendingTasks()) { 172 loop_.RunOnce(true); 173 } 174 // The auxilary error code should not have been changed. 175 EXPECT_EQ(libcurl_fetcher_.GetAuxiliaryErrorCode(), 176 ErrorCode::kUnresolvedHostRecovered); 177 178 // If libcurl fails to resolve the name, we call res_init() to reload 179 // resolv.conf and retry exactly once more. See crbug.com/982813 for details. 180 EXPECT_EQ(libcurl_fetcher_.get_no_network_max_retries(), 181 no_network_max_retries + 1); 182 } 183 184 TEST_F(LibcurlHttpFetcherTest, HttpFetcherStateMachineRetryFailedTest) { 185 state_machine_.UpdateState(true); 186 state_machine_.UpdateState(true); 187 EXPECT_EQ(state_machine_.GetState(), 188 UnresolvedHostStateMachine::State::kNotRetry); 189 } 190 191 TEST_F(LibcurlHttpFetcherTest, HttpFetcherStateMachineRetrySucceedTest) { 192 state_machine_.UpdateState(true); 193 state_machine_.UpdateState(false); 194 EXPECT_EQ(state_machine_.GetState(), 195 UnresolvedHostStateMachine::State::kRetriedSuccess); 196 } 197 198 TEST_F(LibcurlHttpFetcherTest, HttpFetcherStateMachineNoRetryTest) { 199 state_machine_.UpdateState(false); 200 state_machine_.UpdateState(false); 201 EXPECT_EQ(state_machine_.GetState(), 202 UnresolvedHostStateMachine::State::kInit); 203 } 204 205 } // namespace chromeos_update_engine 206