2 +----------------------------------------------------------------------+
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>
26 #include "hphp/util/logger.h"
27 #include "hphp/util/ssl-init.h"
30 ///////////////////////////////////////////////////////////////////////////////
32 //so that curl_global_init() is called ahead of time, avoiding crash
33 class StaticInitializer
{
36 curl_global_init(CURL_GLOBAL_ALL
);
40 static StaticInitializer s_initCurl
;
42 HttpClient::HttpClient(int timeout
/* = 5 */, int maxRedirect
/* = 1 */,
43 bool use11
/* = true */, bool decompress
/* = false */)
44 : m_timeout(timeout
), m_maxRedirect(maxRedirect
), m_use11(use11
),
45 m_decompress(decompress
), m_response(nullptr), m_responseHeaders(nullptr),
48 m_timeout
= ThreadInfo::s_threadInfo
.getNoCheck()->
49 m_reqInjectionData
.getSocketDefaultTimeout();
53 size_t HttpClient::curl_write(char *data
, size_t size
, size_t nmemb
,
55 return ((HttpClient
*)ctx
)->write(data
, size
, nmemb
);
57 size_t HttpClient::write(char *data
, size_t size
, size_t nmemb
) {
58 size_t length
= size
* nmemb
;
59 if (length
> 0 && m_response
) {
60 m_response
->append(data
, (int)length
);
65 size_t HttpClient::curl_header(char *data
, size_t size
, size_t nmemb
,
67 return ((HttpClient
*)ctx
)->header(data
, size
, nmemb
);
69 size_t HttpClient::header(char *data
, size_t size
, size_t nmemb
) {
70 size_t length
= size
* nmemb
;
71 if (length
> 2 && data
[length
- 2] == '\r' && data
[length
- 1] == '\n' &&
73 m_responseHeaders
->push_back(String(data
, length
- 2, CopyString
));
78 void HttpClient::auth(const std::string
&username
,
79 const std::string
&password
, bool basic
/* = true */) {
81 m_username
= username
;
82 m_password
= password
;
85 void HttpClient::proxy(const std::string
&host
, int port
,
86 const std::string
&username
/* = "" */,
87 const std::string
&password
/* = "" */) {
90 m_proxyUsername
= username
;
91 m_proxyPassword
= password
;
94 int HttpClient::get(const char *url
, StringBuffer
&response
,
95 const HeaderMap
*requestHeaders
/* = NULL */,
96 std::vector
<String
> *responseHeaders
/* = NULL */) {
97 return request(nullptr,
98 url
, nullptr, 0, response
, requestHeaders
, responseHeaders
);
101 int HttpClient::post(const char *url
, const char *data
, int size
,
102 StringBuffer
&response
,
103 const HeaderMap
*requestHeaders
/* = NULL */,
104 std::vector
<String
> *responseHeaders
/* = NULL */) {
105 return request(nullptr,
106 url
, data
, size
, response
, requestHeaders
, responseHeaders
);
111 s_verify_peer("verify_peer"),
114 s_local_cert("local_cert"),
115 s_passphrase("passphrase");
117 int HttpClient::request(const char* verb
,
118 const char *url
, const char *data
, int size
,
119 StringBuffer
&response
, const HeaderMap
*requestHeaders
,
120 std::vector
<String
> *responseHeaders
) {
121 SlowTimer
timer(RuntimeOption::HttpSlowQueryThreshold
, "curl", url
);
123 m_response
= &response
;
125 char error_str
[CURL_ERROR_SIZE
+ 1];
126 memset(error_str
, 0, sizeof(error_str
));
128 CURL
*cp
= curl_easy_init();
129 curl_easy_setopt(cp
, CURLOPT_URL
, url
);
130 curl_easy_setopt(cp
, CURLOPT_WRITEFUNCTION
, curl_write
);
131 curl_easy_setopt(cp
, CURLOPT_WRITEDATA
, (void*)this);
132 curl_easy_setopt(cp
, CURLOPT_ERRORBUFFER
, error_str
);
133 curl_easy_setopt(cp
, CURLOPT_NOPROGRESS
, 1);
134 curl_easy_setopt(cp
, CURLOPT_VERBOSE
, 0);
135 curl_easy_setopt(cp
, CURLOPT_NOSIGNAL
, 1);
136 curl_easy_setopt(cp
, CURLOPT_DNS_USE_GLOBAL_CACHE
, 0); // for thread-safe
137 curl_easy_setopt(cp
, CURLOPT_DNS_CACHE_TIMEOUT
, 120);
138 curl_easy_setopt(cp
, CURLOPT_NOSIGNAL
, 1); // for multithreading mode
139 curl_easy_setopt(cp
, CURLOPT_SSL_VERIFYPEER
, 0);
140 curl_easy_setopt(cp
, CURLOPT_SSL_CTX_FUNCTION
, curl_tls_workarounds_cb
);
141 curl_easy_setopt(cp
, CURLOPT_SSL_CIPHER_LIST
, "ALL");
143 curl_easy_setopt(cp
, CURLOPT_TIMEOUT
, m_timeout
);
144 if (m_maxRedirect
> 1) {
145 curl_easy_setopt(cp
, CURLOPT_FOLLOWLOCATION
, 1);
146 curl_easy_setopt(cp
, CURLOPT_MAXREDIRS
, m_maxRedirect
);
148 curl_easy_setopt(cp
, CURLOPT_FOLLOWLOCATION
, 0);
151 curl_easy_setopt(cp
, CURLOPT_HTTP_VERSION
, CURL_HTTP_VERSION_1_0
);
154 curl_easy_setopt(cp
, CURLOPT_ENCODING
, "");
157 if (!m_username
.empty()) {
158 curl_easy_setopt(cp
, CURLOPT_HTTPAUTH
,
159 m_basic
? CURLAUTH_BASIC
: CURLAUTH_DIGEST
);
160 curl_easy_setopt(cp
, CURLOPT_USERNAME
, m_username
.c_str());
161 curl_easy_setopt(cp
, CURLOPT_PASSWORD
, m_password
.c_str());
164 if (!m_proxyHost
.empty() && m_proxyPort
) {
165 curl_easy_setopt(cp
, CURLOPT_PROXY
, m_proxyHost
.c_str());
166 curl_easy_setopt(cp
, CURLOPT_PROXYPORT
, m_proxyPort
);
167 if (!m_proxyUsername
.empty()) {
168 curl_easy_setopt(cp
, CURLOPT_PROXYAUTH
, CURLAUTH_BASIC
);
169 curl_easy_setopt(cp
, CURLOPT_PROXYUSERNAME
, m_proxyUsername
.c_str());
170 curl_easy_setopt(cp
, CURLOPT_PROXYPASSWORD
, m_proxyPassword
.c_str());
174 curl_slist
*slist
= nullptr;
175 if (requestHeaders
) {
176 for (HeaderMap::const_iterator iter
= requestHeaders
->begin();
177 iter
!= requestHeaders
->end(); ++iter
) {
178 for (unsigned int i
= 0; i
< iter
->second
.size(); i
++) {
179 String header
= iter
->first
+ ": " + iter
->second
[i
];
180 slist
= curl_slist_append(slist
, header
.data());
184 curl_easy_setopt(cp
, CURLOPT_HTTPHEADER
, slist
);
189 curl_easy_setopt(cp
, CURLOPT_POST
, 1);
190 curl_easy_setopt(cp
, CURLOPT_POSTFIELDS
, data
);
191 curl_easy_setopt(cp
, CURLOPT_POSTFIELDSIZE
, size
);
194 if (verb
!= nullptr) {
195 curl_easy_setopt(cp
, CURLOPT_CUSTOMREQUEST
, verb
);
197 if (strcasecmp(verb
, "HEAD") == 0) {
198 curl_easy_setopt(cp
, CURLOPT_NOBODY
, 1);
202 if (responseHeaders
) {
203 m_responseHeaders
= responseHeaders
;
204 curl_easy_setopt(cp
, CURLOPT_HEADERFUNCTION
, curl_header
);
205 curl_easy_setopt(cp
, CURLOPT_WRITEHEADER
, (void*)this);
208 if (m_stream_context_options
[s_ssl
].isArray()) {
209 const Array ssl
= m_stream_context_options
[s_ssl
].toArray();
210 if (ssl
[s_verify_peer
].toBoolean()) {
211 curl_easy_setopt(cp
, CURLOPT_SSL_VERIFYPEER
, 1);
213 if (ssl
.exists(s_capath
)) {
214 curl_easy_setopt(cp
, CURLOPT_CAPATH
,
215 ssl
[s_capath
].toString().data());
217 if (ssl
.exists(s_cafile
)) {
218 curl_easy_setopt(cp
, CURLOPT_CAINFO
,
219 ssl
[s_cafile
].toString().data());
221 if (ssl
.exists(s_local_cert
)) {
222 curl_easy_setopt(cp
, CURLOPT_SSLKEY
,
223 ssl
[s_local_cert
].toString().data());
224 curl_easy_setopt(cp
, CURLOPT_SSLKEYTYPE
, "PEM");
226 if (ssl
.exists(s_passphrase
)) {
227 curl_easy_setopt(cp
, CURLOPT_KEYPASSWD
,
228 ssl
[s_passphrase
].toString().data());
234 IOStatusHelper
io("http", url
);
235 CURLcode error_no
= curl_easy_perform(cp
);
236 if (error_no
!= CURLE_OK
) {
239 curl_easy_getinfo(cp
, CURLINFO_RESPONSE_CODE
, &code
);
243 set_curl_statuses(cp
, url
);
246 curl_slist_free_all(slist
);
249 curl_easy_cleanup(cp
);
253 ///////////////////////////////////////////////////////////////////////////////