libcurl_http_fetcher.h revision 9bbd18757660a09fb8831147b17916df8a3212e5
149fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com// Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
249fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com// Use of this source code is governed by a BSD-style license that can be
349fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com// found in the LICENSE file.
449fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com
5c98a7edf648aad88b3f66df3b5a7d43d6a6d7fa9adlr@google.com#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_LIBCURL_HTTP_FETCHER_H__
6c98a7edf648aad88b3f66df3b5a7d43d6a6d7fa9adlr@google.com#define CHROMEOS_PLATFORM_UPDATE_ENGINE_LIBCURL_HTTP_FETCHER_H__
749fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com
849fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com#include <map>
949fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com#include <string>
1049fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com#include <curl/curl.h>
1149fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com#include <glib.h>
1249fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com#include "base/basictypes.h"
13c98a7edf648aad88b3f66df3b5a7d43d6a6d7fa9adlr@google.com#include "chromeos/obsolete_logging.h"
1449fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com#include "update_engine/http_fetcher.h"
1549fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com
1649fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com// This is a concrete implementation of HttpFetcher that uses libcurl to do the
1749fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com// http work.
1849fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com
1949fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.comnamespace chromeos_update_engine {
2049fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com
2149fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.comclass LibcurlHttpFetcher : public HttpFetcher {
2249fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com public:
2349fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  LibcurlHttpFetcher()
2449fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com      : curl_multi_handle_(NULL), curl_handle_(NULL),
2549fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com        timeout_source_(NULL), transfer_in_progress_(false),
269bbd18757660a09fb8831147b17916df8a3212e5Andrew de los Reyes        retry_count_(0), idle_ms_(1000) {}
2749fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com
2849fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // Cleans up all internal state. Does not notify delegate
2949fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  ~LibcurlHttpFetcher();
3049fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com
3149fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // Begins the transfer if it hasn't already begun.
3249fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  virtual void BeginTransfer(const std::string& url);
3349fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com
3449fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // If the transfer is in progress, aborts the transfer early.
3549fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // The transfer cannot be resumed.
3649fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  virtual void TerminateTransfer();
3749fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com
3849fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // Suspend the transfer by calling curl_easy_pause(CURLPAUSE_ALL).
3949fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  virtual void Pause();
4049fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com
4149fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // Resume the transfer by calling curl_easy_pause(CURLPAUSE_CONT).
4249fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  virtual void Unpause();
4349fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com
4449fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // Libcurl sometimes asks to be called back after some time while
4549fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // leaving that time unspecified. In that case, we pick a reasonable
4649fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // default of one second, but it can be overridden here. This is
4749fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // primarily useful for testing.
4849fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // From http://curl.haxx.se/libcurl/c/curl_multi_timeout.html:
4949fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  //     if libcurl returns a -1 timeout here, it just means that libcurl
5049fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  //     currently has no stored timeout value. You must not wait too long
5149fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  //     (more than a few seconds perhaps) before you call
5249fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  //     curl_multi_perform() again.
5349fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  void set_idle_ms(long ms) {
5449fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com    idle_ms_ = ms;
5549fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  }
5649fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com private:
57c98a7edf648aad88b3f66df3b5a7d43d6a6d7fa9adlr@google.com  // Resumes a transfer where it left off. This will use the
58c98a7edf648aad88b3f66df3b5a7d43d6a6d7fa9adlr@google.com  // HTTP Range: header to make a new connection from where the last
59c98a7edf648aad88b3f66df3b5a7d43d6a6d7fa9adlr@google.com  // left off.
60c98a7edf648aad88b3f66df3b5a7d43d6a6d7fa9adlr@google.com  virtual void ResumeTransfer(const std::string& url);
6149fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com
6249fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // These two methods are for glib main loop callbacks. They are called
6349fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // when either a file descriptor is ready for work or when a timer
6449fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // has fired. The static versions are shims for libcurl which has a C API.
6549fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  bool FDCallback(GIOChannel *source, GIOCondition condition);
6649fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  static gboolean StaticFDCallback(GIOChannel *source,
6749fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com                                   GIOCondition condition,
6849fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com                                   gpointer data) {
6949fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com    return reinterpret_cast<LibcurlHttpFetcher*>(data)->FDCallback(source,
7049fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com                                                                   condition);
7149fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  }
723270f7411f55b872db385d0edffdfed18a684121Andrew de los Reyes  gboolean TimeoutCallback();
7349fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  static gboolean StaticTimeoutCallback(gpointer data) {
7449fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com    return reinterpret_cast<LibcurlHttpFetcher*>(data)->TimeoutCallback();
7549fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  }
769bbd18757660a09fb8831147b17916df8a3212e5Andrew de los Reyes
779bbd18757660a09fb8831147b17916df8a3212e5Andrew de los Reyes  gboolean RetryTimeoutCallback();
789bbd18757660a09fb8831147b17916df8a3212e5Andrew de los Reyes  static gboolean StaticRetryTimeoutCallback(void* arg) {
799bbd18757660a09fb8831147b17916df8a3212e5Andrew de los Reyes    return static_cast<LibcurlHttpFetcher*>(arg)->RetryTimeoutCallback();
809bbd18757660a09fb8831147b17916df8a3212e5Andrew de los Reyes  }
8149fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com
8249fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // Calls into curl_multi_perform to let libcurl do its work. Returns after
8349fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // curl_multi_perform is finished, which may actually be after more than
8449fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // one call to curl_multi_perform. This method will set up the glib run
8549fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // loop with sources for future work that libcurl will do.
8649fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // This method will not block.
873270f7411f55b872db385d0edffdfed18a684121Andrew de los Reyes  // Returns true if we should resume immediately after this call.
883270f7411f55b872db385d0edffdfed18a684121Andrew de los Reyes  bool CurlPerformOnce();
8949fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com
9049fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // Sets up glib main loop sources as needed by libcurl. This is generally
9149fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // the file descriptor of the socket and a timer in case nothing happens
9249fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // on the fds.
9349fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  void SetupMainloopSources();
9449fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com
9549fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // Callback called by libcurl when new data has arrived on the transfer
9649fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  size_t LibcurlWrite(void *ptr, size_t size, size_t nmemb);
9749fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  static size_t StaticLibcurlWrite(void *ptr, size_t size,
9849fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com                                   size_t nmemb, void *stream) {
9949fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com    return reinterpret_cast<LibcurlHttpFetcher*>(stream)->
10049fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com        LibcurlWrite(ptr, size, nmemb);
10149fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  }
10249fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com
10349fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // Cleans up the following if they are non-null:
10449fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // curl(m) handles, io_channels_, timeout_source_.
10549fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  void CleanUp();
10649fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com
10749fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // Handles for the libcurl library
10849fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  CURLM *curl_multi_handle_;
10949fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  CURL *curl_handle_;
11049fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com
11149fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // a list of all file descriptors that we're waiting on from the
11249fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // glib main loop
11349fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  typedef std::map<int, std::pair<GIOChannel*, guint> > IOChannels;
11449fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  IOChannels io_channels_;
11549fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com
11649fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // if non-NULL, a timer we're waiting on. glib main loop will call us back
11749fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  // when it fires.
11849fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  GSource* timeout_source_;
11949fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com
12049fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  bool transfer_in_progress_;
12149fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com
122c98a7edf648aad88b3f66df3b5a7d43d6a6d7fa9adlr@google.com  // The transfer size. -1 if not known.
123c98a7edf648aad88b3f66df3b5a7d43d6a6d7fa9adlr@google.com  off_t transfer_size_;
124c98a7edf648aad88b3f66df3b5a7d43d6a6d7fa9adlr@google.com
125c98a7edf648aad88b3f66df3b5a7d43d6a6d7fa9adlr@google.com  // How many bytes have been downloaded and sent to the delegate.
126c98a7edf648aad88b3f66df3b5a7d43d6a6d7fa9adlr@google.com  off_t bytes_downloaded_;
127c98a7edf648aad88b3f66df3b5a7d43d6a6d7fa9adlr@google.com
128c98a7edf648aad88b3f66df3b5a7d43d6a6d7fa9adlr@google.com  // If we resumed an earlier transfer, data offset that we used for the
129c98a7edf648aad88b3f66df3b5a7d43d6a6d7fa9adlr@google.com  // new connection.  0 otherwise.
130c98a7edf648aad88b3f66df3b5a7d43d6a6d7fa9adlr@google.com  off_t resume_offset_;
1319bbd18757660a09fb8831147b17916df8a3212e5Andrew de los Reyes
1329bbd18757660a09fb8831147b17916df8a3212e5Andrew de los Reyes  // Number of resumes performed.
1339bbd18757660a09fb8831147b17916df8a3212e5Andrew de los Reyes  int retry_count_;
134c98a7edf648aad88b3f66df3b5a7d43d6a6d7fa9adlr@google.com
13549fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  long idle_ms_;
13649fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com  DISALLOW_COPY_AND_ASSIGN(LibcurlHttpFetcher);
13749fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com};
13849fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com
13949fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com}  // namespace chromeos_update_engine
14049fdf1889b965be25f929eeebc5b60cd40b9043rspangler@google.com
141c98a7edf648aad88b3f66df3b5a7d43d6a6d7fa9adlr@google.com#endif  // CHROMEOS_PLATFORM_UPDATE_ENGINE_LIBCURL_HTTP_FETCHER_H__
142