blob: 2628eccc23669d6d39989d8f47f2bb385b9eb714 [file] [log] [blame]
rspangler@google.com49fdf182009-10-10 00:57:34 +00001// Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
adlr@google.comc98a7ed2009-12-04 18:54:03 +00005#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_LIBCURL_HTTP_FETCHER_H__
6#define CHROMEOS_PLATFORM_UPDATE_ENGINE_LIBCURL_HTTP_FETCHER_H__
rspangler@google.com49fdf182009-10-10 00:57:34 +00007
8#include <map>
9#include <string>
10#include <curl/curl.h>
11#include <glib.h>
12#include "base/basictypes.h"
Chris Masone790e62e2010-08-12 10:41:18 -070013#include "base/logging.h"
rspangler@google.com49fdf182009-10-10 00:57:34 +000014#include "update_engine/http_fetcher.h"
15
16// This is a concrete implementation of HttpFetcher that uses libcurl to do the
17// http work.
18
19namespace chromeos_update_engine {
20
21class LibcurlHttpFetcher : public HttpFetcher {
22 public:
Darin Petkov41c2fcf2010-08-25 13:14:48 -070023 static const int kMaxRedirects = 10;
24
rspangler@google.com49fdf182009-10-10 00:57:34 +000025 LibcurlHttpFetcher()
Darin Petkovb83371f2010-08-17 09:34:49 -070026 : curl_multi_handle_(NULL),
27 curl_handle_(NULL),
28 timeout_source_(NULL),
29 transfer_in_progress_(false),
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -070030 transfer_size_(0),
31 bytes_downloaded_(0),
32 resume_offset_(0),
Darin Petkovb83371f2010-08-17 09:34:49 -070033 retry_count_(0),
34 retry_seconds_(60),
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -070035 idle_seconds_(1),
Andrew de los Reyesd57d1472010-10-21 13:34:08 -070036 force_connection_type_(false),
37 forced_expensive_connection_(false),
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -070038 in_write_callback_(false),
39 terminate_requested_(false) {}
rspangler@google.com49fdf182009-10-10 00:57:34 +000040
41 // Cleans up all internal state. Does not notify delegate
42 ~LibcurlHttpFetcher();
43
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -070044 void SetOffset(off_t offset) { bytes_downloaded_ = offset; }
45
rspangler@google.com49fdf182009-10-10 00:57:34 +000046 // Begins the transfer if it hasn't already begun.
47 virtual void BeginTransfer(const std::string& url);
48
49 // If the transfer is in progress, aborts the transfer early.
50 // The transfer cannot be resumed.
51 virtual void TerminateTransfer();
52
53 // Suspend the transfer by calling curl_easy_pause(CURLPAUSE_ALL).
54 virtual void Pause();
55
56 // Resume the transfer by calling curl_easy_pause(CURLPAUSE_CONT).
57 virtual void Unpause();
58
59 // Libcurl sometimes asks to be called back after some time while
60 // leaving that time unspecified. In that case, we pick a reasonable
61 // default of one second, but it can be overridden here. This is
62 // primarily useful for testing.
63 // From http://curl.haxx.se/libcurl/c/curl_multi_timeout.html:
64 // if libcurl returns a -1 timeout here, it just means that libcurl
65 // currently has no stored timeout value. You must not wait too long
66 // (more than a few seconds perhaps) before you call
67 // curl_multi_perform() again.
Darin Petkovb83371f2010-08-17 09:34:49 -070068 void set_idle_seconds(int seconds) { idle_seconds_ = seconds; }
69
70 // Sets the retry timeout. Useful for testing.
71 void set_retry_seconds(int seconds) { retry_seconds_ = seconds; }
Andrew de los Reyesd57d1472010-10-21 13:34:08 -070072
73 void SetConnectionAsExpensive(bool is_expensive) {
74 force_connection_type_ = true;
75 forced_expensive_connection_ = is_expensive;
76 }
Darin Petkovb83371f2010-08-17 09:34:49 -070077
rspangler@google.com49fdf182009-10-10 00:57:34 +000078 private:
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -070079 // Asks libcurl for the http response code and stores it in the object.
80 void GetHttpResponseCode();
81
adlr@google.comc98a7ed2009-12-04 18:54:03 +000082 // Resumes a transfer where it left off. This will use the
83 // HTTP Range: header to make a new connection from where the last
84 // left off.
85 virtual void ResumeTransfer(const std::string& url);
rspangler@google.com49fdf182009-10-10 00:57:34 +000086
87 // These two methods are for glib main loop callbacks. They are called
88 // when either a file descriptor is ready for work or when a timer
89 // has fired. The static versions are shims for libcurl which has a C API.
90 bool FDCallback(GIOChannel *source, GIOCondition condition);
91 static gboolean StaticFDCallback(GIOChannel *source,
92 GIOCondition condition,
93 gpointer data) {
94 return reinterpret_cast<LibcurlHttpFetcher*>(data)->FDCallback(source,
95 condition);
96 }
Andrew de los Reyes3270f742010-07-15 22:28:14 -070097 gboolean TimeoutCallback();
rspangler@google.com49fdf182009-10-10 00:57:34 +000098 static gboolean StaticTimeoutCallback(gpointer data) {
99 return reinterpret_cast<LibcurlHttpFetcher*>(data)->TimeoutCallback();
100 }
Darin Petkovb83371f2010-08-17 09:34:49 -0700101
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700102 gboolean RetryTimeoutCallback();
103 static gboolean StaticRetryTimeoutCallback(void* arg) {
104 return static_cast<LibcurlHttpFetcher*>(arg)->RetryTimeoutCallback();
105 }
rspangler@google.com49fdf182009-10-10 00:57:34 +0000106
107 // Calls into curl_multi_perform to let libcurl do its work. Returns after
108 // curl_multi_perform is finished, which may actually be after more than
109 // one call to curl_multi_perform. This method will set up the glib run
110 // loop with sources for future work that libcurl will do.
111 // This method will not block.
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700112 // Returns true if we should resume immediately after this call.
Andrew de los Reyescb319332010-07-19 10:55:01 -0700113 void CurlPerformOnce();
rspangler@google.com49fdf182009-10-10 00:57:34 +0000114
115 // Sets up glib main loop sources as needed by libcurl. This is generally
116 // the file descriptor of the socket and a timer in case nothing happens
117 // on the fds.
118 void SetupMainloopSources();
119
120 // Callback called by libcurl when new data has arrived on the transfer
121 size_t LibcurlWrite(void *ptr, size_t size, size_t nmemb);
122 static size_t StaticLibcurlWrite(void *ptr, size_t size,
123 size_t nmemb, void *stream) {
124 return reinterpret_cast<LibcurlHttpFetcher*>(stream)->
125 LibcurlWrite(ptr, size, nmemb);
126 }
127
128 // Cleans up the following if they are non-null:
129 // curl(m) handles, io_channels_, timeout_source_.
130 void CleanUp();
131
Andrew de los Reyesd57d1472010-10-21 13:34:08 -0700132 // Returns whether or not the current network connection is considered
133 // expensive.
134 bool ConnectionIsExpensive() const;
135
rspangler@google.com49fdf182009-10-10 00:57:34 +0000136 // Handles for the libcurl library
137 CURLM *curl_multi_handle_;
138 CURL *curl_handle_;
139
140 // a list of all file descriptors that we're waiting on from the
141 // glib main loop
142 typedef std::map<int, std::pair<GIOChannel*, guint> > IOChannels;
143 IOChannels io_channels_;
144
145 // if non-NULL, a timer we're waiting on. glib main loop will call us back
146 // when it fires.
147 GSource* timeout_source_;
148
149 bool transfer_in_progress_;
150
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000151 // The transfer size. -1 if not known.
152 off_t transfer_size_;
153
154 // How many bytes have been downloaded and sent to the delegate.
155 off_t bytes_downloaded_;
156
157 // If we resumed an earlier transfer, data offset that we used for the
158 // new connection. 0 otherwise.
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -0700159 // In this class, resume refers to resuming a dropped HTTP connection,
160 // not to resuming an interrupted download.
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000161 off_t resume_offset_;
Darin Petkovb83371f2010-08-17 09:34:49 -0700162
Andrew de los Reyes9bbd1872010-07-16 14:52:29 -0700163 // Number of resumes performed.
164 int retry_count_;
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000165
Darin Petkovb83371f2010-08-17 09:34:49 -0700166 // Seconds to wait before retrying a resume.
167 int retry_seconds_;
168
169 // Seconds to wait before asking libcurl to "perform".
170 int idle_seconds_;
Andrew de los Reyesd57d1472010-10-21 13:34:08 -0700171
172 // If true, assume the network is expensive or not, according to
173 // forced_expensive_connection_. (Useful for testing).
174 bool force_connection_type_;
175 bool forced_expensive_connection_;
Darin Petkovb83371f2010-08-17 09:34:49 -0700176
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -0700177 // If true, we are currently performing a write callback on the delegate.
178 bool in_write_callback_;
179
180 // We can't clean everything up while we're in a write callback, so
181 // if we get a terminate request, queue it until we can handle it.
182 bool terminate_requested_;
183
rspangler@google.com49fdf182009-10-10 00:57:34 +0000184 DISALLOW_COPY_AND_ASSIGN(LibcurlHttpFetcher);
185};
186
187} // namespace chromeos_update_engine
188
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000189#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_LIBCURL_HTTP_FETCHER_H__