• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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