// // Copyright (C) 2009 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "update_engine/common/mock_http_fetcher.h" #include <algorithm> #include <base/bind.h> #include <base/logging.h> #include <base/strings/string_util.h> #include <base/time/time.h> #include <gtest/gtest.h> // This is a mock implementation of HttpFetcher which is useful for testing. using brillo::MessageLoop; using std::min; namespace chromeos_update_engine { MockHttpFetcher::~MockHttpFetcher() { CHECK(timeout_id_ == MessageLoop::kTaskIdNull) << "Call TerminateTransfer() before dtor."; } void MockHttpFetcher::BeginTransfer(const std::string& url) { EXPECT_FALSE(never_use_); if (fail_transfer_ || data_.empty()) { // No data to send, just notify of completion.. SignalTransferComplete(); return; } if (sent_size_ < data_.size()) SendData(true); } // Returns false on one condition: If timeout_id_ was already set // and it needs to be deleted by the caller. If timeout_id_ is null // when this function is called, this function will always return true. bool MockHttpFetcher::SendData(bool skip_delivery) { if (fail_transfer_) { SignalTransferComplete(); return timeout_id_ != MessageLoop::kTaskIdNull; } CHECK_LT(sent_size_, data_.size()); if (!skip_delivery) { const size_t chunk_size = min(kMockHttpFetcherChunkSize, data_.size() - sent_size_); CHECK(delegate_); delegate_->ReceivedBytes(this, &data_[sent_size_], chunk_size); // We may get terminated in the callback. if (sent_size_ == data_.size()) { LOG(INFO) << "Terminated in the ReceivedBytes callback."; return timeout_id_ != MessageLoop::kTaskIdNull; } sent_size_ += chunk_size; CHECK_LE(sent_size_, data_.size()); if (sent_size_ == data_.size()) { // We've sent all the data. Notify of success. SignalTransferComplete(); } } if (paused_) { // If we're paused, we should return true if timeout_id_ is set, // since we need the caller to delete it. return timeout_id_ != MessageLoop::kTaskIdNull; } if (timeout_id_ != MessageLoop::kTaskIdNull) { // we still need a timeout if there's more data to send return sent_size_ < data_.size(); } else if (sent_size_ < data_.size()) { // we don't have a timeout source and we need one timeout_id_ = MessageLoop::current()->PostDelayedTask( FROM_HERE, base::Bind(&MockHttpFetcher::TimeoutCallback, base::Unretained(this)), base::TimeDelta::FromMilliseconds(10)); } return true; } void MockHttpFetcher::TimeoutCallback() { CHECK(!paused_); if (SendData(false)) { // We need to re-schedule the timeout. timeout_id_ = MessageLoop::current()->PostDelayedTask( FROM_HERE, base::Bind(&MockHttpFetcher::TimeoutCallback, base::Unretained(this)), base::TimeDelta::FromMilliseconds(10)); } else { timeout_id_ = MessageLoop::kTaskIdNull; } } // If the transfer is in progress, aborts the transfer early. // The transfer cannot be resumed. void MockHttpFetcher::TerminateTransfer() { LOG(INFO) << "Terminating transfer."; sent_size_ = data_.size(); // Kill any timeout, it is ok to call with kTaskIdNull. MessageLoop::current()->CancelTask(timeout_id_); timeout_id_ = MessageLoop::kTaskIdNull; delegate_->TransferTerminated(this); } void MockHttpFetcher::SetHeader(const std::string& header_name, const std::string& header_value) { extra_headers_[base::ToLowerASCII(header_name)] = header_value; } std::string MockHttpFetcher::GetHeader(const std::string& header_name) const { const auto it = extra_headers_.find(base::ToLowerASCII(header_name)); if (it == extra_headers_.end()) return ""; return it->second; } void MockHttpFetcher::Pause() { CHECK(!paused_); paused_ = true; MessageLoop::current()->CancelTask(timeout_id_); timeout_id_ = MessageLoop::kTaskIdNull; } void MockHttpFetcher::Unpause() { CHECK(paused_) << "You must pause before unpause."; paused_ = false; if (sent_size_ < data_.size()) { SendData(false); } } void MockHttpFetcher::FailTransfer(int http_response_code) { fail_transfer_ = true; http_response_code_ = http_response_code; } void MockHttpFetcher::SignalTransferComplete() { // If the transfer has been failed, the HTTP response code should be set // already. if (!fail_transfer_) { http_response_code_ = 200; } delegate_->TransferComplete(this, !fail_transfer_); } } // namespace chromeos_update_engine