Fix http headers parsing + Remove unused code + Fix indentation
[hiphop-php.git] / hphp / runtime / base / http-client.cpp
blobf2401b82685ec9b4438c491f64fac59572202fae
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 <vector>
26 #include "hphp/util/logger.h"
27 #include "hphp/util/ssl-init.h"
29 namespace HPHP {
30 ///////////////////////////////////////////////////////////////////////////////
32 //so that curl_global_init() is called ahead of time, avoiding crash
33 class StaticInitializer {
34 public:
35 StaticInitializer() {
36 curl_global_init(CURL_GLOBAL_ALL);
37 SSLInit::Init();
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),
46 m_proxyPort(0) {
47 if (m_timeout <= 0) {
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,
54 void *ctx) {
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);
62 return length;
65 size_t HttpClient::curl_header(char *data, size_t size, size_t nmemb,
66 void *ctx) {
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' &&
72 m_responseHeaders) {
73 m_responseHeaders->push_back(String(data, length - 2, CopyString));
75 return length;
78 void HttpClient::auth(const std::string &username,
79 const std::string &password, bool basic /* = true */) {
80 m_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 /* = "" */) {
88 m_proxyHost = host;
89 m_proxyPort = port;
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);
109 const StaticString
110 s_ssl("ssl"),
111 s_verify_peer("verify_peer"),
112 s_capath("capath"),
113 s_cafile("cafile"),
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);
147 } else {
148 curl_easy_setopt(cp, CURLOPT_FOLLOWLOCATION, 0);
150 if (!m_use11) {
151 curl_easy_setopt(cp, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
153 if (m_decompress) {
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());
183 if (slist) {
184 curl_easy_setopt(cp, CURLOPT_HTTPHEADER, slist);
188 if (data && size) {
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());
232 long code = 0;
234 IOStatusHelper io("http", url);
235 CURLcode error_no = curl_easy_perform(cp);
236 if (error_no != CURLE_OK) {
237 m_error = error_str;
238 } else {
239 curl_easy_getinfo(cp, CURLINFO_RESPONSE_CODE, &code);
243 set_curl_statuses(cp, url);
245 if (slist) {
246 curl_slist_free_all(slist);
249 curl_easy_cleanup(cp);
250 return code;
253 ///////////////////////////////////////////////////////////////////////////////