Use folly::dynamic::object and folly::dynamic::string exclusivly
[hiphop-php.git] / hphp / runtime / base / http-client.cpp
blobca34fabf44616d7238cd2617fbe6649b630b508a
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/base/http-client.h"
18 #include "hphp/runtime/base/runtime-option.h"
19 #include "hphp/runtime/server/server-stats.h"
20 #include "hphp/runtime/base/curl-tls-workarounds.h"
21 #include "hphp/runtime/base/execution-context.h"
22 #include "hphp/util/timer.h"
23 #include <curl/curl.h>
24 #include <curl/easy.h>
25 #include "hphp/util/logger.h"
26 #include "hphp/util/ssl-init.h"
28 namespace HPHP {
29 ///////////////////////////////////////////////////////////////////////////////
31 //so that curl_global_init() is called ahead of time, avoiding crash
32 class StaticInitializer {
33 public:
34 StaticInitializer() {
35 curl_global_init(CURL_GLOBAL_ALL);
36 SSLInit::Init();
39 static StaticInitializer s_initCurl;
41 HttpClient::HttpClient(int timeout /* = 5 */, int maxRedirect /* = 1 */,
42 bool use11 /* = true */, bool decompress /* = false */)
43 : m_timeout(timeout), m_maxRedirect(maxRedirect), m_use11(use11),
44 m_decompress(decompress), m_response(nullptr), m_responseHeaders(nullptr),
45 m_proxyPort(0) {
46 if (m_timeout <= 0) {
47 m_timeout = g_context->getSocketDefaultTimeout();
51 size_t HttpClient::curl_write(char *data, size_t size, size_t nmemb,
52 void *ctx) {
53 return ((HttpClient*)ctx)->write(data, size, nmemb);
55 size_t HttpClient::write(char *data, size_t size, size_t nmemb) {
56 size_t length = size * nmemb;
57 if (length > 0 && m_response) {
58 m_response->append(data, (int)length);
60 return length;
63 size_t HttpClient::curl_header(char *data, size_t size, size_t nmemb,
64 void *ctx) {
65 return ((HttpClient*)ctx)->header(data, size, nmemb);
67 size_t HttpClient::header(char *data, size_t size, size_t nmemb) {
68 size_t length = size * nmemb;
69 if (length > 2 && data[length - 2] == '\r' && data[length - 1] == '\n' &&
70 m_responseHeaders) {
71 m_responseHeaders->push_back(String(data, length - 2, CopyString));
73 return length;
76 void HttpClient::auth(const std::string &username,
77 const std::string &password, bool basic /* = true */) {
78 m_basic = true;
79 m_username = username;
80 m_password = password;
83 void HttpClient::proxy(const std::string &host, int port,
84 const std::string &username /* = "" */,
85 const std::string &password /* = "" */) {
86 m_proxyHost = host;
87 m_proxyPort = port;
88 m_proxyUsername = username;
89 m_proxyPassword = password;
92 int HttpClient::get(const char *url, StringBuffer &response,
93 const HeaderMap *requestHeaders /* = NULL */,
94 std::vector<String> *responseHeaders /* = NULL */) {
95 return impl(url, nullptr, 0, response, requestHeaders, responseHeaders);
98 int HttpClient::post(const char *url, const char *data, int size,
99 StringBuffer &response,
100 const HeaderMap *requestHeaders /* = NULL */,
101 std::vector<String> *responseHeaders /* = NULL */) {
102 return impl(url, data, size, response, requestHeaders, responseHeaders);
105 const StaticString
106 s_ssl("ssl"),
107 s_verify_peer("verify_peer"),
108 s_capath("capath"),
109 s_cafile("cafile"),
110 s_local_cert("local_cert"),
111 s_passphrase("passphrase");
113 int HttpClient::impl(const char *url, const char *data, int size,
114 StringBuffer &response, const HeaderMap *requestHeaders,
115 std::vector<String> *responseHeaders) {
116 SlowTimer timer(RuntimeOption::HttpSlowQueryThreshold, "curl", url);
118 m_response = &response;
120 char error_str[CURL_ERROR_SIZE + 1];
121 memset(error_str, 0, sizeof(error_str));
123 CURL *cp = curl_easy_init();
124 curl_easy_setopt(cp, CURLOPT_URL, url);
125 curl_easy_setopt(cp, CURLOPT_WRITEFUNCTION, curl_write);
126 curl_easy_setopt(cp, CURLOPT_WRITEDATA, (void*)this);
127 curl_easy_setopt(cp, CURLOPT_ERRORBUFFER, error_str);
128 curl_easy_setopt(cp, CURLOPT_NOPROGRESS, 1);
129 curl_easy_setopt(cp, CURLOPT_VERBOSE, 0);
130 curl_easy_setopt(cp, CURLOPT_NOSIGNAL, 1);
131 curl_easy_setopt(cp, CURLOPT_DNS_USE_GLOBAL_CACHE, 0); // for thread-safe
132 curl_easy_setopt(cp, CURLOPT_DNS_CACHE_TIMEOUT, 120);
133 curl_easy_setopt(cp, CURLOPT_NOSIGNAL, 1); // for multithreading mode
134 curl_easy_setopt(cp, CURLOPT_SSL_VERIFYPEER, 0);
135 curl_easy_setopt(cp, CURLOPT_SSL_CTX_FUNCTION, curl_tls_workarounds_cb);
137 curl_easy_setopt(cp, CURLOPT_TIMEOUT, m_timeout);
138 if (m_maxRedirect > 1) {
139 curl_easy_setopt(cp, CURLOPT_FOLLOWLOCATION, 1);
140 curl_easy_setopt(cp, CURLOPT_MAXREDIRS, m_maxRedirect);
141 } else {
142 curl_easy_setopt(cp, CURLOPT_FOLLOWLOCATION, 0);
144 if (!m_use11) {
145 curl_easy_setopt(cp, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
147 if (m_decompress) {
148 curl_easy_setopt(cp, CURLOPT_ENCODING, "");
151 if (!m_username.empty()) {
152 curl_easy_setopt(cp, CURLOPT_HTTPAUTH,
153 m_basic ? CURLAUTH_BASIC : CURLAUTH_DIGEST);
154 curl_easy_setopt(cp, CURLOPT_USERNAME, m_username.c_str());
155 curl_easy_setopt(cp, CURLOPT_PASSWORD, m_password.c_str());
158 if (!m_proxyHost.empty() && m_proxyPort) {
159 curl_easy_setopt(cp, CURLOPT_PROXY, m_proxyHost.c_str());
160 curl_easy_setopt(cp, CURLOPT_PROXYPORT, m_proxyPort);
161 if (!m_proxyUsername.empty()) {
162 curl_easy_setopt(cp, CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
163 curl_easy_setopt(cp, CURLOPT_PROXYUSERNAME, m_proxyUsername.c_str());
164 curl_easy_setopt(cp, CURLOPT_PROXYPASSWORD, m_proxyPassword.c_str());
168 std::vector<String> headers; // holding those temporary strings
169 curl_slist *slist = nullptr;
170 if (requestHeaders) {
171 for (HeaderMap::const_iterator iter = requestHeaders->begin();
172 iter != requestHeaders->end(); ++iter) {
173 for (unsigned int i = 0; i < iter->second.size(); i++) {
174 String header = iter->first + ": " + iter->second[i];
175 headers.push_back(header);
176 slist = curl_slist_append(slist, header.data());
179 if (slist) {
180 curl_easy_setopt(cp, CURLOPT_HTTPHEADER, slist);
184 if (data && size) {
185 curl_easy_setopt(cp, CURLOPT_POST, 1);
186 curl_easy_setopt(cp, CURLOPT_POSTFIELDS, data);
187 curl_easy_setopt(cp, CURLOPT_POSTFIELDSIZE, size);
190 if (responseHeaders) {
191 m_responseHeaders = responseHeaders;
192 curl_easy_setopt(cp, CURLOPT_HEADERFUNCTION, curl_header);
193 curl_easy_setopt(cp, CURLOPT_WRITEHEADER, (void*)this);
196 if (m_stream_context_options[s_ssl].isArray()) {
197 const Array ssl = m_stream_context_options[s_ssl].toArray();
198 if (ssl[s_verify_peer].toBoolean()) {
199 curl_easy_setopt(cp, CURLOPT_SSL_VERIFYPEER, 1);
201 if (ssl.exists(s_capath)) {
202 curl_easy_setopt(cp, CURLOPT_CAPATH,
203 ssl[s_capath].toString().data());
205 if (ssl.exists(s_cafile)) {
206 curl_easy_setopt(cp, CURLOPT_CAINFO,
207 ssl[s_cafile].toString().data());
209 if (ssl.exists(s_local_cert)) {
210 curl_easy_setopt(cp, CURLOPT_SSLKEY,
211 ssl[s_local_cert].toString().data());
212 curl_easy_setopt(cp, CURLOPT_SSLKEYTYPE, "PEM");
214 if (ssl.exists(s_passphrase)) {
215 curl_easy_setopt(cp, CURLOPT_KEYPASSWD,
216 ssl[s_passphrase].toString().data());
220 long code = 0;
222 IOStatusHelper io("http", url);
223 CURLcode error_no = curl_easy_perform(cp);
224 if (error_no != CURLE_OK) {
225 m_error = error_str;
226 } else {
227 curl_easy_getinfo(cp, CURLINFO_RESPONSE_CODE, &code);
231 set_curl_statuses(cp, url);
233 if (slist) {
234 curl_slist_free_all(slist);
237 curl_easy_cleanup(cp);
238 return code;
241 ///////////////////////////////////////////////////////////////////////////////