1 // 2 // Copyright (C) 2009 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/common/mock_http_fetcher.h" 18 19 #include <algorithm> 20 21 #include <base/bind.h> 22 #include <base/logging.h> 23 #include <base/strings/string_util.h> 24 #include <base/time/time.h> 25 #include <gtest/gtest.h> 26 27 // This is a mock implementation of HttpFetcher which is useful for testing. 28 29 using brillo::MessageLoop; 30 using std::min; 31 32 namespace chromeos_update_engine { 33 34 MockHttpFetcher::~MockHttpFetcher() { 35 CHECK(timeout_id_ == MessageLoop::kTaskIdNull) << 36 "Call TerminateTransfer() before dtor."; 37 } 38 39 void MockHttpFetcher::BeginTransfer(const std::string& url) { 40 EXPECT_FALSE(never_use_); 41 if (fail_transfer_ || data_.empty()) { 42 // No data to send, just notify of completion.. 43 SignalTransferComplete(); 44 return; 45 } 46 if (sent_size_ < data_.size()) 47 SendData(true); 48 } 49 50 // Returns false on one condition: If timeout_id_ was already set 51 // and it needs to be deleted by the caller. If timeout_id_ is null 52 // when this function is called, this function will always return true. 53 bool MockHttpFetcher::SendData(bool skip_delivery) { 54 if (fail_transfer_) { 55 SignalTransferComplete(); 56 return timeout_id_ != MessageLoop::kTaskIdNull; 57 } 58 59 CHECK_LT(sent_size_, data_.size()); 60 if (!skip_delivery) { 61 const size_t chunk_size = min(kMockHttpFetcherChunkSize, 62 data_.size() - sent_size_); 63 CHECK(delegate_); 64 delegate_->ReceivedBytes(this, &data_[sent_size_], chunk_size); 65 // We may get terminated in the callback. 66 if (sent_size_ == data_.size()) { 67 LOG(INFO) << "Terminated in the ReceivedBytes callback."; 68 return timeout_id_ != MessageLoop::kTaskIdNull; 69 } 70 sent_size_ += chunk_size; 71 CHECK_LE(sent_size_, data_.size()); 72 if (sent_size_ == data_.size()) { 73 // We've sent all the data. Notify of success. 74 SignalTransferComplete(); 75 } 76 } 77 78 if (paused_) { 79 // If we're paused, we should return true if timeout_id_ is set, 80 // since we need the caller to delete it. 81 return timeout_id_ != MessageLoop::kTaskIdNull; 82 } 83 84 if (timeout_id_ != MessageLoop::kTaskIdNull) { 85 // we still need a timeout if there's more data to send 86 return sent_size_ < data_.size(); 87 } else if (sent_size_ < data_.size()) { 88 // we don't have a timeout source and we need one 89 timeout_id_ = MessageLoop::current()->PostDelayedTask( 90 FROM_HERE, 91 base::Bind(&MockHttpFetcher::TimeoutCallback, base::Unretained(this)), 92 base::TimeDelta::FromMilliseconds(10)); 93 } 94 return true; 95 } 96 97 void MockHttpFetcher::TimeoutCallback() { 98 CHECK(!paused_); 99 if (SendData(false)) { 100 // We need to re-schedule the timeout. 101 timeout_id_ = MessageLoop::current()->PostDelayedTask( 102 FROM_HERE, 103 base::Bind(&MockHttpFetcher::TimeoutCallback, base::Unretained(this)), 104 base::TimeDelta::FromMilliseconds(10)); 105 } else { 106 timeout_id_ = MessageLoop::kTaskIdNull; 107 } 108 } 109 110 // If the transfer is in progress, aborts the transfer early. 111 // The transfer cannot be resumed. 112 void MockHttpFetcher::TerminateTransfer() { 113 LOG(INFO) << "Terminating transfer."; 114 sent_size_ = data_.size(); 115 // Kill any timeout, it is ok to call with kTaskIdNull. 116 MessageLoop::current()->CancelTask(timeout_id_); 117 timeout_id_ = MessageLoop::kTaskIdNull; 118 delegate_->TransferTerminated(this); 119 } 120 121 void MockHttpFetcher::SetHeader(const std::string& header_name, 122 const std::string& header_value) { 123 extra_headers_[base::ToLowerASCII(header_name)] = header_value; 124 } 125 126 std::string MockHttpFetcher::GetHeader(const std::string& header_name) const { 127 const auto it = extra_headers_.find(base::ToLowerASCII(header_name)); 128 if (it == extra_headers_.end()) 129 return ""; 130 return it->second; 131 } 132 133 void MockHttpFetcher::Pause() { 134 CHECK(!paused_); 135 paused_ = true; 136 MessageLoop::current()->CancelTask(timeout_id_); 137 timeout_id_ = MessageLoop::kTaskIdNull; 138 } 139 140 void MockHttpFetcher::Unpause() { 141 CHECK(paused_) << "You must pause before unpause."; 142 paused_ = false; 143 if (sent_size_ < data_.size()) { 144 SendData(false); 145 } 146 } 147 148 void MockHttpFetcher::FailTransfer(int http_response_code) { 149 fail_transfer_ = true; 150 http_response_code_ = http_response_code; 151 } 152 153 void MockHttpFetcher::SignalTransferComplete() { 154 // If the transfer has been failed, the HTTP response code should be set 155 // already. 156 if (!fail_transfer_) { 157 http_response_code_ = 200; 158 } 159 delegate_->TransferComplete(this, !fail_transfer_); 160 } 161 162 } // namespace chromeos_update_engine 163