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
~MockHttpFetcher()34 MockHttpFetcher::~MockHttpFetcher() {
35 CHECK(timeout_id_ == MessageLoop::kTaskIdNull) <<
36 "Call TerminateTransfer() before dtor.";
37 }
38
BeginTransfer(const std::string & url)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.
SendData(bool skip_delivery)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
TimeoutCallback()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.
TerminateTransfer()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
SetHeader(const std::string & header_name,const std::string & header_value)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
GetHeader(const std::string & header_name) const126 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
Pause()133 void MockHttpFetcher::Pause() {
134 CHECK(!paused_);
135 paused_ = true;
136 MessageLoop::current()->CancelTask(timeout_id_);
137 timeout_id_ = MessageLoop::kTaskIdNull;
138 }
139
Unpause()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
FailTransfer(int http_response_code)148 void MockHttpFetcher::FailTransfer(int http_response_code) {
149 fail_transfer_ = true;
150 http_response_code_ = http_response_code;
151 }
152
SignalTransferComplete()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