blob: 2bf10f525071122b7b4d09b582b78a5b0ab94448 [file] [log] [blame]
Vitaly Buka4615e0d2015-10-14 15:35:12 -07001// Copyright 2015 The Weave Authors. All rights reserved.
Vitaly Buka17b0a8a2015-08-31 19:12:35 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Johan Euphrosine3523fdd2015-10-14 20:46:05 -07005#include "examples/provider/curl_http_client.h"
Vitaly Buka17b0a8a2015-08-31 19:12:35 -07006
Vitaly Bukaa627e122015-11-06 14:55:53 -08007#include <future>
8#include <thread>
9
Vitaly Buka17b0a8a2015-08-31 19:12:35 -070010#include <base/bind.h>
Vitaly Buka82f215e2015-11-09 17:36:16 -080011#include <base/logging.h>
Vitaly Buka17b0a8a2015-08-31 19:12:35 -070012#include <curl/curl.h>
Vitaly Buka1a42e142015-10-10 18:15:15 -070013#include <weave/enum_to_string.h>
Vitaly Buka82f215e2015-11-09 17:36:16 -080014#include <weave/provider/task_runner.h>
Vitaly Buka17b0a8a2015-08-31 19:12:35 -070015
16namespace weave {
17namespace examples {
18
19namespace {
20
Vitaly Buka1e363672015-09-25 14:01:16 -070021struct ResponseImpl : public provider::HttpClient::Response {
Vitaly Buka866b60a2015-10-09 14:24:55 -070022 int GetStatusCode() const override { return status; }
23 std::string GetContentType() const override { return content_type; }
Vitaly Buka4774df22015-10-09 12:36:22 -070024 std::string GetData() const override { return data; }
Vitaly Buka17b0a8a2015-08-31 19:12:35 -070025
Vitaly Buka4774df22015-10-09 12:36:22 -070026 long status{0};
Vitaly Buka17b0a8a2015-08-31 19:12:35 -070027 std::string content_type;
28 std::string data;
29};
30
31size_t WriteFunction(void* contents, size_t size, size_t nmemb, void* userp) {
Vitaly Buka34668e72015-12-15 14:46:47 -080032 static_cast<std::string*>(userp)->append(static_cast<const char*>(contents),
33 size * nmemb);
Vitaly Buka17b0a8a2015-08-31 19:12:35 -070034 return size * nmemb;
35}
36
Vitaly Bukac27390d2015-11-19 14:42:35 -080037size_t HeaderFunction(void* contents, size_t size, size_t nmemb, void* userp) {
38 std::string header(static_cast<const char*>(contents), size * nmemb);
39 auto pos = header.find(':');
40 if (pos != std::string::npos) {
41 std::pair<std::string, std::string> header_pair;
42
43 static const char kSpaces[] = " \t\r\n";
44 header_pair.first = header.substr(0, pos);
45 pos = header.find_first_not_of(kSpaces, pos + 1);
46 if (pos != std::string::npos) {
47 auto last_non_space = header.find_last_not_of(kSpaces);
48 if (last_non_space >= pos)
49 header_pair.second = header.substr(pos, last_non_space - pos + 1);
50 }
51
Vitaly Buka34668e72015-12-15 14:46:47 -080052 static_cast<provider::HttpClient::Headers*>(userp)->emplace_back(
53 std::move(header_pair));
Vitaly Bukac27390d2015-11-19 14:42:35 -080054 }
55 return size * nmemb;
56}
57
Vitaly Bukaa627e122015-11-06 14:55:53 -080058std::pair<std::unique_ptr<CurlHttpClient::Response>, ErrorPtr>
59SendRequestBlocking(CurlHttpClient::Method method,
60 const std::string& url,
61 const CurlHttpClient::Headers& headers,
62 const std::string& data) {
Vitaly Buka17b0a8a2015-08-31 19:12:35 -070063 std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> curl{curl_easy_init(),
64 &curl_easy_cleanup};
65 CHECK(curl);
66
Vitaly Buka1a42e142015-10-10 18:15:15 -070067 switch (method) {
Vitaly Bukaa627e122015-11-06 14:55:53 -080068 case CurlHttpClient::Method::kGet:
Johan Euphrosine1ca3c222015-10-15 20:43:42 -070069 CHECK_EQ(CURLE_OK, curl_easy_setopt(curl.get(), CURLOPT_HTTPGET, 1L));
70 break;
Vitaly Bukaa627e122015-11-06 14:55:53 -080071 case CurlHttpClient::Method::kPost:
Johan Euphrosine1ca3c222015-10-15 20:43:42 -070072 CHECK_EQ(CURLE_OK, curl_easy_setopt(curl.get(), CURLOPT_HTTPPOST, 1L));
73 break;
Vitaly Bukaa627e122015-11-06 14:55:53 -080074 case CurlHttpClient::Method::kPatch:
75 case CurlHttpClient::Method::kPut:
Vitaly Buka1a42e142015-10-10 18:15:15 -070076 CHECK_EQ(CURLE_OK, curl_easy_setopt(curl.get(), CURLOPT_CUSTOMREQUEST,
77 weave::EnumToString(method).c_str()));
78 break;
Vitaly Buka17b0a8a2015-08-31 19:12:35 -070079 }
80
81 CHECK_EQ(CURLE_OK, curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str()));
82
83 curl_slist* chunk = nullptr;
84 for (const auto& h : headers)
85 chunk = curl_slist_append(chunk, (h.first + ": " + h.second).c_str());
86
87 CHECK_EQ(CURLE_OK, curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, chunk));
88
Vitaly Bukaa627e122015-11-06 14:55:53 -080089 if (!data.empty() || method == CurlHttpClient::Method::kPost) {
Vitaly Buka17b0a8a2015-08-31 19:12:35 -070090 CHECK_EQ(CURLE_OK,
91 curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, data.c_str()));
92 }
93
94 std::unique_ptr<ResponseImpl> response{new ResponseImpl};
95 CHECK_EQ(CURLE_OK,
96 curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, &WriteFunction));
97 CHECK_EQ(CURLE_OK,
98 curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &response->data));
99 CHECK_EQ(CURLE_OK, curl_easy_setopt(curl.get(), CURLOPT_HEADERFUNCTION,
Vitaly Bukac27390d2015-11-19 14:42:35 -0800100 &HeaderFunction));
101 provider::HttpClient::Headers response_headers;
102 CHECK_EQ(CURLE_OK,
103 curl_easy_setopt(curl.get(), CURLOPT_HEADERDATA, &response_headers));
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700104
105 CURLcode res = curl_easy_perform(curl.get());
106 if (chunk)
107 curl_slist_free_all(chunk);
108
Vitaly Buka17280372015-10-10 17:11:47 -0700109 ErrorPtr error;
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700110 if (res != CURLE_OK) {
Vitaly Buka17280372015-10-10 17:11:47 -0700111 Error::AddTo(&error, FROM_HERE, "curl", "curl_easy_perform_error",
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700112 curl_easy_strerror(res));
Vitaly Bukaa627e122015-11-06 14:55:53 -0800113 return {nullptr, std::move(error)};
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700114 }
115
Vitaly Bukac27390d2015-11-19 14:42:35 -0800116 for (const auto& header : response_headers) {
117 if (header.first == "Content-Type")
118 response->content_type = header.second;
119 }
120
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700121 CHECK_EQ(CURLE_OK, curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE,
122 &response->status));
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700123
Vitaly Bukaa627e122015-11-06 14:55:53 -0800124 return {std::move(response), nullptr};
125}
126
127} // namespace
128
129CurlHttpClient::CurlHttpClient(provider::TaskRunner* task_runner)
130 : task_runner_{task_runner} {}
131
132void CurlHttpClient::SendRequest(Method method,
133 const std::string& url,
134 const Headers& headers,
135 const std::string& data,
136 const SendRequestCallback& callback) {
137 pending_tasks_.emplace_back(
138 std::async(std::launch::async, SendRequestBlocking, method, url, headers,
139 data),
140 callback);
141 if (pending_tasks_.size() == 1) // More means check is scheduled.
142 CheckTasks();
143}
144
145void CurlHttpClient::CheckTasks() {
Vitaly Buka82f215e2015-11-09 17:36:16 -0800146 VLOG(4) << "CurlHttpClient::CheckTasks, size=" << pending_tasks_.size();
Vitaly Bukaa627e122015-11-06 14:55:53 -0800147 auto ready_begin =
Vitaly Buka82f215e2015-11-09 17:36:16 -0800148 std::partition(pending_tasks_.begin(), pending_tasks_.end(),
Vitaly Bukaa627e122015-11-06 14:55:53 -0800149 [](const decltype(pending_tasks_)::value_type& value) {
150 return value.first.wait_for(std::chrono::seconds(0)) !=
151 std::future_status::ready;
152 });
153
154 for (auto it = ready_begin; it != pending_tasks_.end(); ++it) {
Vitaly Buka82f215e2015-11-09 17:36:16 -0800155 CHECK(it->first.valid());
Vitaly Bukaa627e122015-11-06 14:55:53 -0800156 auto result = it->first.get();
157 VLOG(2) << "CurlHttpClient::CheckTasks done";
158 task_runner_->PostDelayedTask(
159 FROM_HERE, base::Bind(it->second, base::Passed(&result.first),
160 base::Passed(&result.second)),
161 {});
162 }
163
164 pending_tasks_.erase(ready_begin, pending_tasks_.end());
165
166 if (pending_tasks_.empty()) {
167 VLOG(2) << "No more CurlHttpClient tasks";
168 return;
169 }
170
Vitaly Buka17280372015-10-10 17:11:47 -0700171 task_runner_->PostDelayedTask(
Vitaly Bukaa627e122015-11-06 14:55:53 -0800172 FROM_HERE,
173 base::Bind(&CurlHttpClient::CheckTasks, weak_ptr_factory_.GetWeakPtr()),
174 base::TimeDelta::FromMilliseconds(100));
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700175}
176
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700177} // namespace examples
178} // namespace weave