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#ifndef UPDATE_ENGINE_COMMON_HTTP_FETCHER_H_
18#define UPDATE_ENGINE_COMMON_HTTP_FETCHER_H_
19
20#include <deque>
21#include <string>
22#include <vector>
23
24#include <base/callback.h>
25#include <base/logging.h>
26#include <base/macros.h>
27#include <brillo/message_loops/message_loop.h>
28
29#include "update_engine/common/http_common.h"
30#include "update_engine/proxy_resolver.h"
31
32// This class is a simple wrapper around an HTTP library (libcurl). We can
33// easily mock out this interface for testing.
34
35// Implementations of this class should use asynchronous i/o. They can access
36// the MessageLoop to request callbacks when timers or file descriptors change.
37
38namespace chromeos_update_engine {
39
40class HttpFetcherDelegate;
41
42class HttpFetcher {
43 public:
44  // |proxy_resolver| is the resolver that will be consulted for proxy
45  // settings. It may be null, in which case direct connections will
46  // be used. Does not take ownership of the resolver.
47  explicit HttpFetcher(ProxyResolver* proxy_resolver)
48      : post_data_set_(false),
49        http_response_code_(0),
50        delegate_(nullptr),
51        proxies_(1, kNoProxy),
52        proxy_resolver_(proxy_resolver),
53        callback_(nullptr) {}
54  virtual ~HttpFetcher();
55
56  void set_delegate(HttpFetcherDelegate* delegate) { delegate_ = delegate; }
57  HttpFetcherDelegate* delegate() const { return delegate_; }
58  int http_response_code() const { return http_response_code_; }
59
60  // Optional: Post data to the server. The HttpFetcher should make a copy
61  // of this data and upload it via HTTP POST during the transfer. The type of
62  // the data is necessary for properly setting the Content-Type HTTP header.
63  void SetPostData(const void* data, size_t size, HttpContentType type);
64
65  // Same without a specified Content-Type.
66  void SetPostData(const void* data, size_t size);
67
68  // Proxy methods to set the proxies, then to pop them off.
69  // Returns true on success.
70  bool ResolveProxiesForUrl(const std::string& url,
71                            const base::Closure& callback);
72
73  void SetProxies(const std::deque<std::string>& proxies) {
74    proxies_ = proxies;
75  }
76  const std::string& GetCurrentProxy() const {
77    return proxies_.front();
78  }
79  bool HasProxy() const { return !proxies_.empty(); }
80  void PopProxy() { proxies_.pop_front(); }
81
82  // Downloading should resume from this offset
83  virtual void SetOffset(off_t offset) = 0;
84
85  // Set/unset the length of the range to be downloaded.
86  virtual void SetLength(size_t length) = 0;
87  virtual void UnsetLength() = 0;
88
89  // Begins the transfer to the specified URL. This fetcher instance should not
90  // be destroyed until either TransferComplete, or TransferTerminated is
91  // called.
92  virtual void BeginTransfer(const std::string& url) = 0;
93
94  // Aborts the transfer. The transfer may not abort right away -- delegate's
95  // TransferTerminated() will be called when the transfer is actually done.
96  virtual void TerminateTransfer() = 0;
97
98  // Add or update a custom header to be sent with every request. If the same
99  // |header_name| is passed twice, the second |header_value| would override the
100  // previous value.
101  virtual void SetHeader(const std::string& header_name,
102                         const std::string& header_value) = 0;
103
104  // If data is coming in too quickly, you can call Pause() to pause the
105  // transfer. The delegate will not have ReceivedBytes() called while
106  // an HttpFetcher is paused.
107  virtual void Pause() = 0;
108
109  // Used to unpause an HttpFetcher and let the bytes stream in again.
110  // If a delegate is set, ReceivedBytes() may be called on it before
111  // Unpause() returns
112  virtual void Unpause() = 0;
113
114  // These two function are overloaded in LibcurlHttp fetcher to speed
115  // testing.
116  virtual void set_idle_seconds(int seconds) {}
117  virtual void set_retry_seconds(int seconds) {}
118
119  // Sets the values used to time out the connection if the transfer
120  // rate is less than |low_speed_bps| bytes/sec for more than
121  // |low_speed_sec| seconds.
122  virtual void set_low_speed_limit(int low_speed_bps, int low_speed_sec) = 0;
123
124  // Sets the connect timeout, e.g. the maximum amount of time willing
125  // to wait for establishing a connection to the server.
126  virtual void set_connect_timeout(int connect_timeout_seconds) = 0;
127
128  // Sets the number of allowed retries.
129  virtual void set_max_retry_count(int max_retry_count) = 0;
130
131  // Get the total number of bytes downloaded by fetcher.
132  virtual size_t GetBytesDownloaded() = 0;
133
134  ProxyResolver* proxy_resolver() const { return proxy_resolver_; }
135
136 protected:
137  // The URL we're actively fetching from
138  std::string url_;
139
140  // POST data for the transfer, and whether or not it was ever set
141  bool post_data_set_;
142  brillo::Blob post_data_;
143  HttpContentType post_content_type_;
144
145  // The server's HTTP response code from the last transfer. This
146  // field should be set to 0 when a new transfer is initiated, and
147  // set to the response code when the transfer is complete.
148  int http_response_code_;
149
150  // The delegate; may be null.
151  HttpFetcherDelegate* delegate_;
152
153  // Proxy servers
154  std::deque<std::string> proxies_;
155
156  ProxyResolver* const proxy_resolver_;
157
158  // The ID of the idle callback, used when we have no proxy resolver.
159  brillo::MessageLoop::TaskId no_resolver_idle_id_{
160      brillo::MessageLoop::kTaskIdNull};
161
162  // Callback for when we are resolving proxies
163  std::unique_ptr<base::Closure> callback_;
164
165 private:
166  // Callback from the proxy resolver
167  void ProxiesResolved(const std::deque<std::string>& proxies);
168  static void StaticProxiesResolved(const std::deque<std::string>& proxies,
169                                    void* data) {
170    reinterpret_cast<HttpFetcher*>(data)->ProxiesResolved(proxies);
171  }
172
173  // Callback used to run the proxy resolver callback when there is no
174  // |proxy_resolver_|.
175  void NoProxyResolverCallback();
176
177  DISALLOW_COPY_AND_ASSIGN(HttpFetcher);
178};
179
180// Interface for delegates
181class HttpFetcherDelegate {
182 public:
183  virtual ~HttpFetcherDelegate() = default;
184
185  // Called every time bytes are received.
186  virtual void ReceivedBytes(HttpFetcher* fetcher,
187                             const void* bytes,
188                             size_t length) = 0;
189
190  // Called if the fetcher seeks to a particular offset.
191  virtual void SeekToOffset(off_t offset) {}
192
193  // When a transfer has completed, exactly one of these two methods will be
194  // called. TransferTerminated is called when the transfer has been aborted
195  // through TerminateTransfer. TransferComplete is called in all other
196  // situations. It's OK to destroy the |fetcher| object in this callback.
197  virtual void TransferComplete(HttpFetcher* fetcher, bool successful) = 0;
198  virtual void TransferTerminated(HttpFetcher* fetcher) {}
199};
200
201}  // namespace chromeos_update_engine
202
203#endif  // UPDATE_ENGINE_COMMON_HTTP_FETCHER_H_
204