Shaka Packager SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends
http_key_fetcher.cc
1 // Copyright 2014 Google Inc. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file or at
5 // https://developers.google.com/open-source/licenses/bsd
6 
7 #include "packager/media/base/http_key_fetcher.h"
8 
9 #include <curl/curl.h>
10 
11 #include "packager/base/logging.h"
12 #include "packager/base/strings/stringprintf.h"
13 #include "packager/base/synchronization/lock.h"
14 
15 namespace shaka {
16 
17 namespace {
18 const char kUserAgentString[] = "shaka-packager-http_fetcher/1.0";
19 const char kSoapActionHeader[] =
20  "SOAPAction: \"http://schemas.microsoft.com/DRM/2007/03/protocols/"
21  "AcquirePackagingData\"";
22 const char kXmlContentTypeHeader[] = "Content-Type: text/xml; charset=UTF-8";
23 
24 // Scoped CURL implementation which cleans up itself when goes out of scope.
25 class ScopedCurl {
26  public:
27  ScopedCurl() { ptr_ = curl_easy_init(); }
28  ~ScopedCurl() {
29  if (ptr_)
30  curl_easy_cleanup(ptr_);
31  }
32 
33  CURL* get() { return ptr_; }
34 
35  private:
36  CURL* ptr_;
37  DISALLOW_COPY_AND_ASSIGN(ScopedCurl);
38 };
39 
40 size_t AppendToString(char* ptr, size_t size, size_t nmemb, std::string* response) {
41  DCHECK(ptr);
42  DCHECK(response);
43  const size_t total_size = size * nmemb;
44  response->append(ptr, total_size);
45  return total_size;
46 }
47 
48 class LibCurlInitializer {
49  public:
50  LibCurlInitializer() : initialized_(false) {
51  base::AutoLock lock(lock_);
52  if (!initialized_) {
53  curl_global_init(CURL_GLOBAL_DEFAULT);
54  initialized_ = true;
55  }
56  }
57 
58  ~LibCurlInitializer() {
59  base::AutoLock lock(lock_);
60  if (initialized_) {
61  curl_global_cleanup();
62  initialized_ = false;
63  }
64  }
65 
66  private:
67  base::Lock lock_;
68  bool initialized_;
69 
70  DISALLOW_COPY_AND_ASSIGN(LibCurlInitializer);
71 };
72 
73 } // namespace
74 
75 namespace media {
76 
77 HttpKeyFetcher::HttpKeyFetcher() : timeout_in_seconds_(0) {}
78 
79 HttpKeyFetcher::HttpKeyFetcher(uint32_t timeout_in_seconds)
80  : timeout_in_seconds_(timeout_in_seconds) {}
81 
82 HttpKeyFetcher::~HttpKeyFetcher() {}
83 
84 Status HttpKeyFetcher::FetchKeys(const std::string& url,
85  const std::string& request,
86  std::string* response) {
87  return Post(url, request, response);
88 }
89 
90 Status HttpKeyFetcher::Get(const std::string& path, std::string* response) {
91  return FetchInternal(GET, path, "", response);
92 }
93 
94 Status HttpKeyFetcher::Post(const std::string& path,
95  const std::string& data,
96  std::string* response) {
97  return FetchInternal(POST, path, data, response);
98 }
99 
100 Status HttpKeyFetcher::FetchInternal(HttpMethod method,
101  const std::string& path,
102  const std::string& data,
103  std::string* response) {
104  DCHECK(method == GET || method == POST);
105  static LibCurlInitializer lib_curl_initializer;
106 
107  ScopedCurl scoped_curl;
108  CURL* curl = scoped_curl.get();
109  if (!curl) {
110  LOG(ERROR) << "curl_easy_init() failed.";
111  return Status(error::HTTP_FAILURE, "curl_easy_init() failed.");
112  }
113  response->clear();
114 
115  curl_easy_setopt(curl, CURLOPT_URL, path.c_str());
116  curl_easy_setopt(curl, CURLOPT_USERAGENT, kUserAgentString);
117  curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout_in_seconds_);
118  curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
119  curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
120  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, AppendToString);
121  curl_easy_setopt(curl, CURLOPT_WRITEDATA, response);
122 
123  if (!client_cert_private_key_file_.empty() && !client_cert_file_.empty()) {
124  // Some PlayReady packaging servers only allow connects via HTTPS with
125  // client certificates.
126  curl_easy_setopt(curl, CURLOPT_SSLKEY,
127  client_cert_private_key_file_.data());
128  if (!client_cert_private_key_password_.empty()) {
129  curl_easy_setopt(curl, CURLOPT_KEYPASSWD,
130  client_cert_private_key_password_.data());
131  }
132  curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, "PEM");
133  curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM");
134  curl_easy_setopt(curl, CURLOPT_SSLCERT, client_cert_file_.data());
135  }
136  if (!ca_file_.empty()) {
137  // Host validation needs to be off when using self-signed certificates.
138  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
139  curl_easy_setopt(curl, CURLOPT_CAINFO, ca_file_.data());
140  }
141  if (method == POST) {
142  curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
143  curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, data.size());
144  if (data.find("soap:Envelope") > 0) {
145  // Adds Http headers for SOAP requests.
146  struct curl_slist *chunk = NULL;
147  chunk = curl_slist_append(chunk, kXmlContentTypeHeader);
148  chunk = curl_slist_append(chunk, kSoapActionHeader);
149  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
150  }
151  }
152  CURLcode res = curl_easy_perform(curl);
153  if (res != CURLE_OK) {
154  std::string error_message = base::StringPrintf(
155  "curl_easy_perform() failed: %s.", curl_easy_strerror(res));
156  if (res == CURLE_HTTP_RETURNED_ERROR) {
157  long response_code = 0;
158  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
159  error_message += base::StringPrintf(" Response code: %ld.", response_code);
160  }
161 
162  LOG(ERROR) << error_message;
163  return Status(
164  res == CURLE_OPERATION_TIMEDOUT ? error::TIME_OUT : error::HTTP_FAILURE,
165  error_message);
166  }
167  return Status::OK;
168 }
169 
170 } // namespace media
171 } // namespace shaka
virtual Status Get(const std::string &url, std::string *response)
Status FetchKeys(const std::string &url, const std::string &request, std::string *response) override
HttpKeyFetcher()
Creates a fetcher with no timeout.
virtual Status Post(const std::string &url, const std::string &data, std::string *response)