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>
25 #include "hphp/util/logger.h"
26 #include "hphp/util/ssl-init.h"
29 ///////////////////////////////////////////////////////////////////////////////
31 //so that curl_global_init() is called ahead of time, avoiding crash
32 class StaticInitializer
{
35 curl_global_init(CURL_GLOBAL_ALL
);
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),
47 m_timeout
= g_context
->getSocketDefaultTimeout();
51 size_t HttpClient::curl_write(char *data
, size_t size
, size_t nmemb
,
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
);
63 size_t HttpClient::curl_header(char *data
, size_t size
, size_t nmemb
,
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' &&
71 m_responseHeaders
->push_back(String(data
, length
- 2, CopyString
));
76 void HttpClient::auth(const std::string
&username
,
77 const std::string
&password
, bool 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
/* = "" */) {
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
);
107 s_verify_peer("verify_peer"),
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
);
142 curl_easy_setopt(cp
, CURLOPT_FOLLOWLOCATION
, 0);
145 curl_easy_setopt(cp
, CURLOPT_HTTP_VERSION
, CURL_HTTP_VERSION_1_0
);
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());
180 curl_easy_setopt(cp
, CURLOPT_HTTPHEADER
, slist
);
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());
222 IOStatusHelper
io("http", url
);
223 CURLcode error_no
= curl_easy_perform(cp
);
224 if (error_no
!= CURLE_OK
) {
227 curl_easy_getinfo(cp
, CURLINFO_RESPONSE_CODE
, &code
);
231 set_curl_statuses(cp
, url
);
234 curl_slist_free_all(slist
);
237 curl_easy_cleanup(cp
);
241 ///////////////////////////////////////////////////////////////////////////////