Clean up VectorEffects::init
[hiphop-php.git] / hphp / runtime / ext / ext_curl.cpp
blob3d2c4dee3e09026b8524a7bf7193e7fd3ca49fe1
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1997-2010 The PHP Group |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
18 #include "hphp/runtime/ext/ext_curl.h"
19 #include "hphp/runtime/ext/ext_function.h"
20 #include "hphp/runtime/base/util/string_buffer.h"
21 #include "hphp/runtime/base/util/libevent_http_client.h"
22 #include "hphp/runtime/base/util/curl_tls_workarounds.h"
23 #include "hphp/runtime/base/runtime_option.h"
24 #include "hphp/runtime/base/server/server_stats.h"
25 #include "hphp/runtime/vm/jit/translator-inline.h"
27 #define CURLOPT_RETURNTRANSFER 19913
28 #define CURLOPT_BINARYTRANSFER 19914
29 #define PHP_CURL_STDOUT 0
30 #define PHP_CURL_FILE 1
31 #define PHP_CURL_USER 2
32 #define PHP_CURL_DIRECT 3
33 #define PHP_CURL_RETURN 4
34 #define PHP_CURL_ASCII 5
35 #define PHP_CURL_BINARY 6
36 #define PHP_CURL_IGNORE 7
39 namespace HPHP {
40 IMPLEMENT_DEFAULT_EXTENSION(curl);
42 static StaticString s_exception("exception");
43 static StaticString s_previous("previous");
45 ///////////////////////////////////////////////////////////////////////////////
46 // helper data structure
48 class CurlResource : public SweepableResourceData {
49 private:
50 DECLARE_OBJECT_ALLOCATION(CurlResource)
52 class WriteHandler {
53 public:
54 WriteHandler() : method(0), type(0) {}
56 int method;
57 Variant callback;
58 SmartResource<File> fp;
59 StringBuffer buf;
60 String content;
61 int type;
64 class ReadHandler {
65 public:
66 ReadHandler() : method(0) {}
68 int method;
69 Variant callback;
70 SmartResource<File> fp;
73 DECLARE_BOOST_TYPES(ToFree);
74 class ToFree {
75 public:
76 vector<char*> str;
77 vector<curl_httppost*> post;
78 vector<curl_slist*> slist;
80 ~ToFree() {
81 for (unsigned int i = 0; i < str.size(); i++) {
82 free(str[i]);
84 for (unsigned int i = 0; i < post.size(); i++) {
85 curl_formfree(post[i]);
87 for (unsigned int i = 0; i < slist.size(); i++) {
88 curl_slist_free_all(slist[i]);
93 public:
94 static StaticString s_class_name;
95 // overriding ResourceData
96 virtual CStrRef o_getClassNameHook() const { return s_class_name; }
98 explicit CurlResource(CStrRef url)
99 : m_exception(nullptr), m_phpException(false), m_emptyPost(true) {
100 m_cp = curl_easy_init();
101 m_url = url;
103 memset(m_error_str, 0, sizeof(m_error_str));
104 m_error_no = CURLE_OK;
105 m_to_free = ToFreePtr(new ToFree());
107 m_write.method = PHP_CURL_STDOUT;
108 m_write.type = PHP_CURL_ASCII;
109 m_read.method = PHP_CURL_DIRECT;
110 m_write_header.method = PHP_CURL_IGNORE;
112 curl_easy_setopt(m_cp, CURLOPT_NOPROGRESS, 1);
113 curl_easy_setopt(m_cp, CURLOPT_VERBOSE, 0);
114 curl_easy_setopt(m_cp, CURLOPT_ERRORBUFFER, m_error_str);
115 curl_easy_setopt(m_cp, CURLOPT_WRITEFUNCTION, curl_write);
116 curl_easy_setopt(m_cp, CURLOPT_FILE, (void*)this);
117 curl_easy_setopt(m_cp, CURLOPT_READFUNCTION, curl_read);
118 curl_easy_setopt(m_cp, CURLOPT_INFILE, (void*)this);
119 curl_easy_setopt(m_cp, CURLOPT_HEADERFUNCTION, curl_write_header);
120 curl_easy_setopt(m_cp, CURLOPT_WRITEHEADER, (void*)this);
121 curl_easy_setopt(m_cp, CURLOPT_DNS_USE_GLOBAL_CACHE, 0); // for thread-safe
122 curl_easy_setopt(m_cp, CURLOPT_DNS_CACHE_TIMEOUT, 120);
123 curl_easy_setopt(m_cp, CURLOPT_MAXREDIRS, 20); // no infinite redirects
124 curl_easy_setopt(m_cp, CURLOPT_NOSIGNAL, 1); // for multithreading mode
125 curl_easy_setopt(m_cp, CURLOPT_SSL_CTX_FUNCTION, curl_tls_workarounds_cb);
127 curl_easy_setopt(m_cp, CURLOPT_TIMEOUT,
128 RuntimeOption::HttpDefaultTimeout);
129 curl_easy_setopt(m_cp, CURLOPT_CONNECTTIMEOUT,
130 RuntimeOption::HttpDefaultTimeout);
132 if (!url.empty()) {
133 #if LIBCURL_VERSION_NUM >= 0x071100
134 /* Strings passed to libcurl as 'char *' arguments, are copied by
135 the library... NOTE: before 7.17.0 strings were not copied. */
136 curl_easy_setopt(m_cp, CURLOPT_URL, url.c_str());
137 #else
138 char *urlcopy = strndup(url.data(), url.size());
139 curl_easy_setopt(m_cp, CURLOPT_URL, urlcopy);
140 m_to_free->str.push_back(urlcopy);
141 #endif
145 explicit CurlResource(CurlResource *src)
146 : m_exception(nullptr), m_phpException(false) {
147 assert(src && src != this);
148 assert(!src->m_exception);
150 m_cp = curl_easy_duphandle(src->get());
151 m_url = src->m_url;
153 memset(m_error_str, 0, sizeof(m_error_str));
154 m_error_no = CURLE_OK;
156 m_write.method = src->m_write.method;
157 m_write.type = src->m_write.type;
158 m_read.method = src->m_read.method;
159 m_write_header.method = src->m_write_header.method;
161 m_write.fp = src->m_write.fp;
162 m_write_header.fp = src->m_write_header.fp;
163 m_read.fp = src->m_read.fp;
165 m_write.callback = src->m_write.callback;
166 m_read.callback = src->m_read.callback;
167 m_write_header.callback = src->m_write_header.callback;
169 curl_easy_setopt(m_cp, CURLOPT_ERRORBUFFER, m_error_str);
170 curl_easy_setopt(m_cp, CURLOPT_FILE, (void*)this);
171 curl_easy_setopt(m_cp, CURLOPT_INFILE, (void*)this);
172 curl_easy_setopt(m_cp, CURLOPT_WRITEHEADER, (void*)this);
174 m_to_free = src->m_to_free;
175 m_emptyPost = src->m_emptyPost;
178 ~CurlResource() {
179 close();
182 void closeForSweep() {
183 assert(!m_exception);
184 if (m_cp) {
185 curl_easy_cleanup(m_cp);
186 m_cp = NULL;
188 m_to_free.reset();
191 void close() {
192 closeForSweep();
193 m_opts.clear();
196 void check_exception() {
197 if (m_exception) {
198 if (m_phpException) {
199 Object e((ObjectData*)m_exception);
200 m_exception = NULL;
201 e.get()->decRefCount();
202 throw e;
203 } else {
204 Exception *e = (Exception*)m_exception;
205 m_exception = NULL;
206 e->throwException();
211 ObjectData* getAndClearPhpException() {
212 if (m_exception && m_phpException) {
213 ObjectData* ret = (ObjectData*)m_exception;
214 m_exception = nullptr;
215 return ret;
217 return nullptr;
220 Exception* getAndClearCppException() {
221 if (!m_phpException) {
222 Exception* e = (Exception*)m_exception;
223 m_exception = nullptr;
224 return e;
226 return nullptr;
229 Variant execute() {
230 assert(!m_exception);
231 if (m_cp == NULL) {
232 return false;
234 if (m_emptyPost) {
235 // As per curl docs, an empty post must set POSTFIELDSIZE to be 0 or
236 // the reader function will be called
237 curl_easy_setopt(m_cp, CURLOPT_POSTFIELDSIZE, 0);
239 m_write.buf.reset();
240 m_write.content.clear();
241 m_header.clear();
242 memset(m_error_str, 0, sizeof(m_error_str));
245 IOStatusHelper io("curl_easy_perform", m_url.data());
246 SYNC_VM_REGS_SCOPED();
247 m_error_no = curl_easy_perform(m_cp);
248 check_exception();
250 set_curl_statuses(m_cp, m_url.data());
252 /* CURLE_PARTIAL_FILE is returned by HEAD requests */
253 if (m_error_no != CURLE_OK && m_error_no != CURLE_PARTIAL_FILE) {
254 m_write.buf.reset();
255 m_write.content.clear();
256 return false;
259 if (m_write.method == PHP_CURL_RETURN) {
260 if (!m_write.buf.empty()) {
261 m_write.content = m_write.buf.detach();
263 if (!m_write.content.empty()) {
264 return m_write.content;
267 if (m_write.method == PHP_CURL_RETURN) {
268 return String("");
270 return true;
273 String getUrl() {
274 return m_url;
277 String getHeader() {
278 return m_header;
281 String getContents() {
282 if (m_write.method == PHP_CURL_RETURN) {
283 if (!m_write.buf.empty()) {
284 m_write.content = m_write.buf.detach();
286 return m_write.content;
288 return String();
291 bool setOption(long option, CVarRef value) {
292 if (m_cp == NULL) {
293 return false;
295 m_error_no = CURLE_OK;
297 switch (option) {
298 case CURLOPT_INFILESIZE:
299 case CURLOPT_VERBOSE:
300 case CURLOPT_HEADER:
301 case CURLOPT_NOPROGRESS:
302 case CURLOPT_NOBODY:
303 case CURLOPT_FAILONERROR:
304 case CURLOPT_UPLOAD:
305 case CURLOPT_POST:
306 case CURLOPT_FTPLISTONLY:
307 case CURLOPT_FTPAPPEND:
308 case CURLOPT_NETRC:
309 case CURLOPT_PUT:
310 case CURLOPT_TIMEOUT:
311 #if LIBCURL_VERSION_NUM >= 0x071002
312 case CURLOPT_TIMEOUT_MS:
313 #endif
314 case CURLOPT_FTP_USE_EPSV:
315 case CURLOPT_LOW_SPEED_LIMIT:
316 case CURLOPT_SSLVERSION:
317 case CURLOPT_LOW_SPEED_TIME:
318 case CURLOPT_RESUME_FROM:
319 case CURLOPT_TIMEVALUE:
320 case CURLOPT_TIMECONDITION:
321 case CURLOPT_TRANSFERTEXT:
322 case CURLOPT_HTTPPROXYTUNNEL:
323 case CURLOPT_FILETIME:
324 case CURLOPT_MAXREDIRS:
325 case CURLOPT_MAXCONNECTS:
326 case CURLOPT_CLOSEPOLICY:
327 case CURLOPT_FRESH_CONNECT:
328 case CURLOPT_FORBID_REUSE:
329 case CURLOPT_CONNECTTIMEOUT:
330 #if LIBCURL_VERSION_NUM >= 0x071002
331 case CURLOPT_CONNECTTIMEOUT_MS:
332 #endif
333 case CURLOPT_SSL_VERIFYHOST:
334 case CURLOPT_SSL_VERIFYPEER:
335 //case CURLOPT_DNS_USE_GLOBAL_CACHE: not thread-safe when set to true
336 case CURLOPT_NOSIGNAL:
337 case CURLOPT_PROXYTYPE:
338 case CURLOPT_BUFFERSIZE:
339 case CURLOPT_HTTPGET:
340 case CURLOPT_HTTP_VERSION:
341 case CURLOPT_CRLF:
342 case CURLOPT_DNS_CACHE_TIMEOUT:
343 case CURLOPT_PROXYPORT:
344 case CURLOPT_FTP_USE_EPRT:
345 case CURLOPT_HTTPAUTH:
346 case CURLOPT_PROXYAUTH:
347 case CURLOPT_FTP_CREATE_MISSING_DIRS:
348 case CURLOPT_FTPSSLAUTH:
349 case CURLOPT_FTP_SSL:
350 case CURLOPT_UNRESTRICTED_AUTH:
351 case CURLOPT_PORT:
352 case CURLOPT_AUTOREFERER:
353 case CURLOPT_COOKIESESSION:
354 case CURLOPT_TCP_NODELAY:
355 case CURLOPT_IPRESOLVE:
356 case CURLOPT_FOLLOWLOCATION:
357 m_error_no = curl_easy_setopt(m_cp, (CURLoption)option, value.toInt64());
358 break;
359 case CURLOPT_RETURNTRANSFER:
360 m_write.method = value.toBoolean() ? PHP_CURL_RETURN : PHP_CURL_STDOUT;
361 break;
362 case CURLOPT_BINARYTRANSFER:
363 m_write.type = value.toBoolean() ? PHP_CURL_BINARY : PHP_CURL_ASCII;
364 break;
365 case CURLOPT_PRIVATE:
366 case CURLOPT_URL:
367 case CURLOPT_PROXY:
368 case CURLOPT_USERPWD:
369 case CURLOPT_PROXYUSERPWD:
370 case CURLOPT_RANGE:
371 case CURLOPT_CUSTOMREQUEST:
372 case CURLOPT_USERAGENT:
373 case CURLOPT_FTPPORT:
374 case CURLOPT_COOKIE:
375 case CURLOPT_REFERER:
376 case CURLOPT_INTERFACE:
377 case CURLOPT_KRB4LEVEL:
378 case CURLOPT_EGDSOCKET:
379 case CURLOPT_CAINFO:
380 case CURLOPT_CAPATH:
381 case CURLOPT_SSL_CIPHER_LIST:
382 case CURLOPT_SSLKEY:
383 case CURLOPT_SSLKEYTYPE:
384 case CURLOPT_SSLKEYPASSWD:
385 case CURLOPT_SSLENGINE:
386 case CURLOPT_SSLENGINE_DEFAULT:
387 case CURLOPT_SSLCERTTYPE:
388 case CURLOPT_ENCODING:
389 case CURLOPT_COOKIEJAR:
390 case CURLOPT_SSLCERT:
391 case CURLOPT_RANDOM_FILE:
392 case CURLOPT_COOKIEFILE:
394 String svalue = value.toString();
395 #if LIBCURL_VERSION_NUM >= 0x071100
396 /* Strings passed to libcurl as 'char *' arguments, are copied
397 by the library... NOTE: before 7.17.0 strings were not copied. */
398 m_error_no = curl_easy_setopt(m_cp, (CURLoption)option, svalue.c_str());
399 #else
400 char *copystr = strndup(svalue.data(), svalue.size());
401 m_to_free->str.push_back(copystr);
402 m_error_no = curl_easy_setopt(m_cp, (CURLoption)option, copystr);
403 #endif
404 if (option == CURLOPT_URL) m_url = value;
406 break;
407 case CURLOPT_FILE:
408 case CURLOPT_INFILE:
409 case CURLOPT_WRITEHEADER:
410 case CURLOPT_STDERR:
412 if (!value.isResource()) {
413 return false;
416 Resource obj = value.toResource();
417 if (obj.isNull() || obj.getTyped<File>(true) == NULL) {
418 return false;
421 switch (option) {
422 case CURLOPT_FILE:
423 m_write.fp = obj;
424 m_write.method = PHP_CURL_FILE;
425 break;
426 case CURLOPT_WRITEHEADER:
427 m_write_header.fp = obj;
428 m_write_header.method = PHP_CURL_FILE;
429 break;
430 case CURLOPT_INFILE:
431 m_read.fp = obj;
432 m_emptyPost = false;
433 break;
434 default: {
435 if (obj.getTyped<PlainFile>(true) == NULL) {
436 return false;
438 FILE *fp = obj.getTyped<PlainFile>()->getStream();
439 if (!fp) {
440 return false;
442 m_error_no = curl_easy_setopt(m_cp, (CURLoption)option, fp);
443 break;
447 break;
448 case CURLOPT_WRITEFUNCTION:
449 m_write.callback = value;
450 m_write.method = PHP_CURL_USER;
451 break;
452 case CURLOPT_READFUNCTION:
453 m_read.callback = value;
454 m_read.method = PHP_CURL_USER;
455 m_emptyPost = false;
456 break;
457 case CURLOPT_HEADERFUNCTION:
458 m_write_header.callback = value;
459 m_write_header.method = PHP_CURL_USER;
460 break;
461 case CURLOPT_POSTFIELDS:
462 m_emptyPost = false;
463 if (value.is(KindOfArray) || value.is(KindOfObject)) {
464 Array arr = value.toArray();
465 curl_httppost *first = NULL;
466 curl_httppost *last = NULL;
467 for (ArrayIter iter(arr); iter; ++iter) {
468 String key = iter.first().toString();
469 String val = iter.second().toString();
470 const char *postval = val.data();
472 /* The arguments after _NAMELENGTH and _CONTENTSLENGTH
473 * must be explicitly cast to long in curl_formadd
474 * use since curl needs a long not an int. */
475 if (*postval == '@') {
476 ++postval;
477 m_error_no = (CURLcode)curl_formadd
478 (&first, &last,
479 CURLFORM_COPYNAME, key.data(),
480 CURLFORM_NAMELENGTH, (long)key.size(),
481 CURLFORM_FILE, postval,
482 CURLFORM_END);
483 } else {
484 m_error_no = (CURLcode)curl_formadd
485 (&first, &last,
486 CURLFORM_COPYNAME, key.data(),
487 CURLFORM_NAMELENGTH, (long)key.size(),
488 CURLFORM_COPYCONTENTS, postval,
489 CURLFORM_CONTENTSLENGTH,(long)val.size(),
490 CURLFORM_END);
494 if (m_error_no != CURLE_OK) {
495 return false;
498 m_to_free->post.push_back(first);
499 m_error_no = curl_easy_setopt(m_cp, CURLOPT_HTTPPOST, first);
501 } else {
502 String svalue = value.toString();
503 #if LIBCURL_VERSION_NUM >= 0x071100
504 /* with curl 7.17.0 and later, we can use COPYPOSTFIELDS,
505 but we have to provide size before */
506 m_error_no = curl_easy_setopt(m_cp, CURLOPT_POSTFIELDSIZE,
507 svalue.size());
508 m_error_no = curl_easy_setopt(m_cp, CURLOPT_COPYPOSTFIELDS,
509 svalue.c_str());
510 #else
511 char *post = strndup(svalue.data(), svalue.size());
512 m_to_free->str.push_back(post);
514 m_error_no = curl_easy_setopt(m_cp, CURLOPT_POSTFIELDS, post);
515 m_error_no = curl_easy_setopt(m_cp, CURLOPT_POSTFIELDSIZE,
516 svalue.size());
517 #endif
519 break;
520 case CURLOPT_HTTPHEADER:
521 case CURLOPT_QUOTE:
522 case CURLOPT_HTTP200ALIASES:
523 case CURLOPT_POSTQUOTE:
524 if (value.is(KindOfArray) || value.is(KindOfObject)) {
525 Array arr = value.toArray();
526 curl_slist *slist = NULL;
527 for (ArrayIter iter(arr); iter; ++iter) {
528 String key = iter.first().toString();
529 String val = iter.second().toString();
531 slist = curl_slist_append(slist, val.c_str());
532 if (!slist) {
533 raise_warning("Could not build curl_slist");
534 return false;
538 m_to_free->slist.push_back(slist);
539 m_error_no = curl_easy_setopt(m_cp, (CURLoption)option, slist);
541 } else {
542 raise_warning("You must pass either an object or an array with "
543 "the CURLOPT_HTTPHEADER, CURLOPT_QUOTE, "
544 "CURLOPT_HTTP200ALIASES and CURLOPT_POSTQUOTE "
545 "arguments");
546 return false;
548 break;
550 case CURLINFO_HEADER_OUT:
551 if (value.toInt64() == 1) {
552 curl_easy_setopt(m_cp, CURLOPT_DEBUGFUNCTION, curl_debug);
553 curl_easy_setopt(m_cp, CURLOPT_DEBUGDATA, (void *)this);
554 curl_easy_setopt(m_cp, CURLOPT_VERBOSE, 1);
555 } else {
556 curl_easy_setopt(m_cp, CURLOPT_DEBUGFUNCTION, NULL);
557 curl_easy_setopt(m_cp, CURLOPT_DEBUGDATA, NULL);
558 curl_easy_setopt(m_cp, CURLOPT_VERBOSE, 0);
560 break;
562 default:
563 m_error_no = CURLE_FAILED_INIT;
564 throw_invalid_argument("option: %ld", option);
565 break;
568 m_opts.set(int64_t(option), value);
570 return m_error_no == CURLE_OK;
573 Variant getOption(long option) {
575 if (option != 0) {
576 if (!m_opts.exists(int64_t(option))) {
577 return false;
579 return m_opts[int64_t(option)];
582 return m_opts;
585 static int curl_debug(CURL *cp, curl_infotype type, char *buf,
586 size_t buf_len, void *ctx) {
587 CurlResource *ch = (CurlResource *)ctx;
588 if (type == CURLINFO_HEADER_OUT && buf_len > 0) {
589 ch->m_header = String(buf, buf_len, CopyString);
591 return 0;
594 Variant do_callback(CVarRef cb, CArrRef args) {
595 assert(!m_exception);
596 try {
597 return vm_call_user_func(cb, args);
598 } catch (Object &e) {
599 ObjectData *od = e.get();
600 od->incRefCount();
601 m_exception = od;
602 m_phpException = true;
603 } catch (Exception &e) {
604 m_exception = e.clone();
605 m_phpException = false;
607 return uninit_null();
610 static size_t curl_read(char *data, size_t size, size_t nmemb, void *ctx) {
611 CurlResource *ch = (CurlResource *)ctx;
612 ReadHandler *t = &ch->m_read;
614 int length = -1;
615 switch (t->method) {
616 case PHP_CURL_DIRECT:
617 if (!t->fp.isNull()) {
618 int data_size = size * nmemb;
619 String ret = t->fp->read(data_size);
620 length = ret.size();
621 if (length) {
622 memcpy(data, ret.data(), length);
625 break;
626 case PHP_CURL_USER:
628 int data_size = size * nmemb;
629 Variant ret = ch->do_callback(
630 t->callback, CREATE_VECTOR3(Resource(ch), t->fp->fd(), data_size));
631 if (ret.isString()) {
632 String sret = ret.toString();
633 length = data_size < sret.size() ? data_size : sret.size();
634 memcpy(data, sret.data(), length);
636 break;
639 return length;
642 static size_t curl_write(char *data, size_t size, size_t nmemb, void *ctx) {
643 CurlResource *ch = (CurlResource *)ctx;
644 WriteHandler *t = &ch->m_write;
645 size_t length = size * nmemb;
647 switch (t->method) {
648 case PHP_CURL_STDOUT:
649 g_context->write(data, length);
650 break;
651 case PHP_CURL_FILE:
652 return t->fp->write(String(data, length, AttachLiteral), length);
653 case PHP_CURL_RETURN:
654 if (length > 0) {
655 t->buf.append(data, (int)length);
657 break;
658 case PHP_CURL_USER:
660 Variant ret = ch->do_callback(
661 t->callback,
662 CREATE_VECTOR2(Resource(ch), String(data, length, CopyString)));
663 length = ret.toInt64();
665 break;
668 return length;
671 static size_t curl_write_header(char *data, size_t size, size_t nmemb,
672 void *ctx) {
673 CurlResource *ch = (CurlResource *)ctx;
674 WriteHandler *t = &ch->m_write_header;
675 size_t length = size * nmemb;
677 switch (t->method) {
678 case PHP_CURL_STDOUT:
679 // Handle special case write when we're returning the entire transfer
680 if (ch->m_write.method == PHP_CURL_RETURN && length > 0) {
681 ch->m_write.buf.append(data, (int)length);
682 } else {
683 g_context->write(data, length);
685 break;
686 case PHP_CURL_FILE:
687 return t->fp->write(String(data, length, AttachLiteral), length);
688 case PHP_CURL_USER:
690 Variant ret = ch->do_callback(
691 t->callback,
692 CREATE_VECTOR2(Resource(ch), String(data, length, CopyString)));
693 length = ret.toInt64();
695 break;
696 case PHP_CURL_IGNORE:
697 return length;
698 default:
699 return (size_t)-1;
702 return length;
705 CURL *get(bool nullOkay = false) {
706 if (m_cp == NULL && !nullOkay) {
707 throw NullPointerException();
709 return m_cp;
712 int getError() {
713 return m_error_no;
716 String getErrorString() {
717 return String(m_error_str, CopyString);
720 private:
721 CURL *m_cp;
722 void *m_exception;
724 char m_error_str[CURL_ERROR_SIZE + 1];
725 CURLcode m_error_no;
727 ToFreePtr m_to_free;
729 String m_url;
730 String m_header;
731 Array m_opts;
733 WriteHandler m_write;
734 WriteHandler m_write_header;
735 ReadHandler m_read;
737 bool m_phpException;
738 bool m_emptyPost;
740 IMPLEMENT_OBJECT_ALLOCATION_NO_DEFAULT_SWEEP(CurlResource);
741 void CurlResource::sweep() {
742 m_write.buf.release();
743 m_write_header.buf.release();
744 closeForSweep();
747 StaticString CurlResource::s_class_name("cURL handle");
749 ///////////////////////////////////////////////////////////////////////////////
751 #define CHECK_RESOURCE(curl) \
752 CurlResource *curl = ch.getTyped<CurlResource>(true, true); \
753 if (curl == NULL) { \
754 raise_warning("supplied argument is not a valid cURL handle resource"); \
755 return false; \
758 Variant f_curl_init(CStrRef url /* = null_string */) {
759 return NEWOBJ(CurlResource)(url);
762 Variant f_curl_copy_handle(CResRef ch) {
763 CHECK_RESOURCE(curl);
764 return NEWOBJ(CurlResource)(curl);
767 const StaticString
768 s_version_number("version_number"),
769 s_age("age"),
770 s_features("features"),
771 s_ssl_version_number("ssl_version_number"),
772 s_version("version"),
773 s_host("host"),
774 s_ssl_version("ssl_version"),
775 s_libz_version("libz_version"),
776 s_protocols("protocols");
778 Variant f_curl_version(int uversion /* = k_CURLVERSION_NOW */) {
779 curl_version_info_data *d = curl_version_info((CURLversion)uversion);
780 if (d == NULL) {
781 return false;
784 ArrayInit ret(9);
785 ret.set(s_version_number, (int)d->version_num);
786 ret.set(s_age, d->age);
787 ret.set(s_features, d->features);
788 ret.set(s_ssl_version_number, d->ssl_version_num);
789 ret.set(s_version, d->version);
790 ret.set(s_host, d->host);
791 ret.set(s_ssl_version, d->ssl_version);
792 ret.set(s_libz_version, d->libz_version);
794 // Add an array of protocols
795 char **p = (char **) d->protocols;
796 Array protocol_list;
797 while (*p != NULL) {
798 protocol_list.append(String(*p++, CopyString));
800 ret.set(s_protocols, protocol_list);
801 return ret.create();
804 bool f_curl_setopt(CResRef ch, int option, CVarRef value) {
805 CHECK_RESOURCE(curl);
806 return curl->setOption(option, value);
809 bool f_curl_setopt_array(CResRef ch, CArrRef options) {
810 CHECK_RESOURCE(curl);
811 for (ArrayIter iter(options); iter; ++iter) {
812 if (!curl->setOption(iter.first().toInt32(), iter.second())) {
813 return false;
816 return true;
819 Variant f_fb_curl_getopt(CResRef ch, int opt /* = 0 */) {
820 CHECK_RESOURCE(curl);
821 return curl->getOption(opt);
824 Variant f_curl_exec(CResRef ch) {
825 CHECK_RESOURCE(curl);
826 return curl->execute();
829 const StaticString
830 s_url("url"),
831 s_content_type("content_type"),
832 s_http_code("http_code"),
833 s_header_size("header_size"),
834 s_request_size("request_size"),
835 s_filetime("filetime"),
836 s_ssl_verify_result("ssl_verify_result"),
837 s_redirect_count("redirect_count"),
838 s_local_port("local_port"),
839 s_total_time("total_time"),
840 s_namelookup_time("namelookup_time"),
841 s_connect_time("connect_time"),
842 s_pretransfer_time("pretransfer_time"),
843 s_size_upload("size_upload"),
844 s_size_download("size_download"),
845 s_speed_download("speed_download"),
846 s_speed_upload("speed_upload"),
847 s_download_content_length("download_content_length"),
848 s_upload_content_length("upload_content_length"),
849 s_starttransfer_time("starttransfer_time"),
850 s_redirect_time("redirect_time"),
851 s_request_header("request_header");
853 Variant f_curl_getinfo(CResRef ch, int opt /* = 0 */) {
854 CHECK_RESOURCE(curl);
855 CURL *cp = curl->get();
857 if (opt == 0) {
858 char *s_code;
859 long l_code;
860 double d_code;
862 Array ret;
863 if (curl_easy_getinfo(cp, CURLINFO_EFFECTIVE_URL, &s_code) == CURLE_OK) {
864 ret.set(s_url, String(s_code, CopyString));
866 if (curl_easy_getinfo(cp, CURLINFO_CONTENT_TYPE, &s_code) == CURLE_OK) {
867 if (s_code != NULL) {
868 ret.set(s_content_type, String(s_code, CopyString));
869 } else {
870 ret.set(s_content_type, uninit_null());
873 if (curl_easy_getinfo(cp, CURLINFO_HTTP_CODE, &l_code) == CURLE_OK) {
874 ret.set(s_http_code, l_code);
876 if (curl_easy_getinfo(cp, CURLINFO_HEADER_SIZE, &l_code) == CURLE_OK) {
877 ret.set(s_header_size, l_code);
879 if (curl_easy_getinfo(cp, CURLINFO_REQUEST_SIZE, &l_code) == CURLE_OK) {
880 ret.set(s_request_size, l_code);
882 if (curl_easy_getinfo(cp, CURLINFO_FILETIME, &l_code) == CURLE_OK) {
883 ret.set(s_filetime, l_code);
885 if (curl_easy_getinfo(cp, CURLINFO_SSL_VERIFYRESULT, &l_code) ==
886 CURLE_OK) {
887 ret.set(s_ssl_verify_result, l_code);
889 if (curl_easy_getinfo(cp, CURLINFO_REDIRECT_COUNT, &l_code) == CURLE_OK) {
890 ret.set(s_redirect_count, l_code);
892 #if LIBCURL_VERSION_NUM >= 0x071500
893 if (curl_easy_getinfo(cp, CURLINFO_LOCAL_PORT, &l_code) == CURLE_OK) {
894 ret.set(s_local_port, l_code);
896 #endif
897 if (curl_easy_getinfo(cp, CURLINFO_TOTAL_TIME, &d_code) == CURLE_OK) {
898 ret.set(s_total_time, d_code);
900 if (curl_easy_getinfo(cp, CURLINFO_NAMELOOKUP_TIME, &d_code) == CURLE_OK) {
901 ret.set(s_namelookup_time, d_code);
903 if (curl_easy_getinfo(cp, CURLINFO_CONNECT_TIME, &d_code) == CURLE_OK) {
904 ret.set(s_connect_time, d_code);
906 if (curl_easy_getinfo(cp, CURLINFO_PRETRANSFER_TIME, &d_code) ==
907 CURLE_OK) {
908 ret.set(s_pretransfer_time, d_code);
910 if (curl_easy_getinfo(cp, CURLINFO_SIZE_UPLOAD, &d_code) == CURLE_OK) {
911 ret.set(s_size_upload, d_code);
913 if (curl_easy_getinfo(cp, CURLINFO_SIZE_DOWNLOAD, &d_code) == CURLE_OK) {
914 ret.set(s_size_download, d_code);
916 if (curl_easy_getinfo(cp, CURLINFO_SPEED_DOWNLOAD, &d_code) == CURLE_OK) {
917 ret.set(s_speed_download, d_code);
919 if (curl_easy_getinfo(cp, CURLINFO_SPEED_UPLOAD, &d_code) == CURLE_OK) {
920 ret.set(s_speed_upload, d_code);
922 if (curl_easy_getinfo(cp, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d_code) ==
923 CURLE_OK) {
924 ret.set(s_download_content_length, d_code);
926 if (curl_easy_getinfo(cp, CURLINFO_CONTENT_LENGTH_UPLOAD, &d_code) ==
927 CURLE_OK) {
928 ret.set(s_upload_content_length, d_code);
930 if (curl_easy_getinfo(cp, CURLINFO_STARTTRANSFER_TIME, &d_code) ==
931 CURLE_OK) {
932 ret.set(s_starttransfer_time, d_code);
934 if (curl_easy_getinfo(cp, CURLINFO_REDIRECT_TIME, &d_code) == CURLE_OK) {
935 ret.set(s_redirect_time, d_code);
937 String header = curl->getHeader();
938 if (!header.empty()) {
939 ret.set(s_request_header, header);
941 return ret;
944 switch (opt) {
945 case CURLINFO_PRIVATE:
946 case CURLINFO_EFFECTIVE_URL:
947 case CURLINFO_CONTENT_TYPE: {
948 char *s_code = NULL;
949 if (curl_easy_getinfo(cp, (CURLINFO)opt, &s_code) == CURLE_OK &&
950 s_code) {
951 return String(s_code, CopyString);
953 return false;
955 case CURLINFO_HTTP_CODE:
956 case CURLINFO_HEADER_SIZE:
957 case CURLINFO_REQUEST_SIZE:
958 case CURLINFO_FILETIME:
959 case CURLINFO_SSL_VERIFYRESULT:
960 #if LIBCURL_VERSION_NUM >= 0x071500
961 case CURLINFO_LOCAL_PORT:
962 #endif
963 case CURLINFO_REDIRECT_COUNT: {
964 long code = 0;
965 if (curl_easy_getinfo(cp, (CURLINFO)opt, &code) == CURLE_OK) {
966 return code;
968 return false;
970 case CURLINFO_TOTAL_TIME:
971 case CURLINFO_NAMELOOKUP_TIME:
972 case CURLINFO_CONNECT_TIME:
973 case CURLINFO_PRETRANSFER_TIME:
974 case CURLINFO_SIZE_UPLOAD:
975 case CURLINFO_SIZE_DOWNLOAD:
976 case CURLINFO_SPEED_DOWNLOAD:
977 case CURLINFO_SPEED_UPLOAD:
978 case CURLINFO_CONTENT_LENGTH_DOWNLOAD:
979 case CURLINFO_CONTENT_LENGTH_UPLOAD:
980 case CURLINFO_STARTTRANSFER_TIME:
981 case CURLINFO_REDIRECT_TIME: {
982 double code = 0.0;
983 if (curl_easy_getinfo(cp, (CURLINFO)opt, &code) == CURLE_OK) {
984 return code;
986 return false;
988 case CURLINFO_HEADER_OUT:
990 String header = curl->getHeader();
991 if (!header.empty()) {
992 return header;
994 return false;
998 return uninit_null();
1001 Variant f_curl_errno(CResRef ch) {
1002 CHECK_RESOURCE(curl);
1003 return curl->getError();
1006 Variant f_curl_error(CResRef ch) {
1007 CHECK_RESOURCE(curl);
1008 return curl->getErrorString();
1011 Variant f_curl_close(CResRef ch) {
1012 CHECK_RESOURCE(curl);
1013 curl->close();
1014 return uninit_null();
1017 ///////////////////////////////////////////////////////////////////////////////
1019 class CurlMultiResource : public SweepableResourceData {
1020 public:
1021 DECLARE_OBJECT_ALLOCATION(CurlMultiResource)
1023 static StaticString s_class_name;
1024 // overriding ResourceData
1025 CStrRef o_getClassNameHook() const { return s_class_name; }
1027 CurlMultiResource() {
1028 m_multi = curl_multi_init();
1031 ~CurlMultiResource() {
1032 close();
1035 void close() {
1036 if (m_multi) {
1037 curl_multi_cleanup(m_multi);
1038 m_easyh.clear();
1039 m_multi = NULL;
1043 void add(CResRef ch) {
1044 m_easyh.append(ch);
1047 void remove(CurlResource *curle) {
1048 for (ArrayIter iter(m_easyh); iter; ++iter) {
1049 if (iter.second().toResource().getTyped<CurlResource>()->get(true) ==
1050 curle->get()) {
1051 m_easyh.remove(iter.first());
1052 return;
1057 Resource find(CURL *cp) {
1058 for (ArrayIter iter(m_easyh); iter; ++iter) {
1059 if (iter.second().toResource().
1060 getTyped<CurlResource>()->get(true) == cp) {
1061 return iter.second().toResource();
1064 return Resource();
1067 void check_exceptions() {
1068 ObjectData* phpException = 0;
1069 Exception* cppException = 0;
1070 for (ArrayIter iter(m_easyh); iter; ++iter) {
1071 CurlResource* curl = iter.second().toResource().getTyped<CurlResource>();
1072 if (ObjectData* e = curl->getAndClearPhpException()) {
1073 if (phpException) {
1074 e->o_set(s_previous, Variant(phpException), s_exception);
1075 phpException->decRefCount();
1077 phpException = e;
1078 } else if (Exception *e = curl->getAndClearCppException()) {
1079 delete cppException;
1080 cppException = e;
1083 if (cppException) {
1084 if (phpException) decRefObj(phpException);
1085 cppException->throwException();
1087 if (phpException) {
1088 Object e(phpException);
1089 phpException->decRefCount();
1090 throw e;
1094 CURLM *get() {
1095 if (m_multi == NULL) {
1096 throw NullPointerException();
1098 return m_multi;
1101 private:
1102 CURLM *m_multi;
1103 Array m_easyh;
1105 IMPLEMENT_OBJECT_ALLOCATION_NO_DEFAULT_SWEEP(CurlMultiResource);
1106 void CurlMultiResource::sweep() {
1107 if (m_multi) {
1108 curl_multi_cleanup(m_multi);
1112 StaticString CurlMultiResource::s_class_name("cURL Multi Handle");
1114 ///////////////////////////////////////////////////////////////////////////////
1116 #define CHECK_MULTI_RESOURCE(curlm) \
1117 CurlMultiResource *curlm = mh.getTyped<CurlMultiResource>(true, true); \
1118 if (curlm == NULL) { \
1119 raise_warning("expects parameter 1 to be cURL multi resource"); \
1120 return uninit_null(); \
1123 Resource f_curl_multi_init() {
1124 return NEWOBJ(CurlMultiResource)();
1127 Variant f_curl_multi_add_handle(CResRef mh, CResRef ch) {
1128 CHECK_MULTI_RESOURCE(curlm);
1129 CurlResource *curle = ch.getTyped<CurlResource>();
1130 curlm->add(ch);
1131 return curl_multi_add_handle(curlm->get(), curle->get());
1134 Variant f_curl_multi_remove_handle(CResRef mh, CResRef ch) {
1135 CHECK_MULTI_RESOURCE(curlm);
1136 CurlResource *curle = ch.getTyped<CurlResource>();
1137 curlm->remove(curle);
1138 return curl_multi_remove_handle(curlm->get(), curle->get());
1141 Variant f_curl_multi_exec(CResRef mh, VRefParam still_running) {
1142 CHECK_MULTI_RESOURCE(curlm);
1143 int running = still_running;
1144 IOStatusHelper io("curl_multi_exec");
1145 SYNC_VM_REGS_SCOPED();
1146 int result = curl_multi_perform(curlm->get(), &running);
1147 curlm->check_exceptions();
1148 still_running = running;
1149 return result;
1152 /* Fallback implementation of curl_multi_select() for
1153 * libcurl < 7.28.0 without FB's curl_multi_select() patch
1155 * This allows the OSS build to work with older package
1156 * versions of libcurl, but will fail with file descriptors
1157 * over 1024.
1159 UNUSED
1160 static void hphp_curl_multi_select(CURLM *mh, int timeout_ms, int *ret) {
1161 fd_set read_fds, write_fds, except_fds;
1162 int maxfds, nfds = -1;
1163 struct timeval tv;
1165 FD_ZERO(&read_fds);
1166 FD_ZERO(&write_fds);
1167 FD_ZERO(&except_fds);
1169 tv.tv_sec = timeout_ms / 1000;
1170 tv.tv_usec = (timeout_ms * 1000) % 1000000;
1172 curl_multi_fdset(mh, &read_fds, &write_fds, &except_fds, &maxfds);
1173 if (maxfds < 1024) {
1174 nfds = select(maxfds + 1, &read_fds, &write_fds, &except_fds, &tv);
1175 } else {
1176 /* fd_set can only hold sockets from 0 to 1023,
1177 * anything higher is ignored by FD_SET()
1178 * avoid "unexplained" behavior by failing outright
1180 raise_warning("libcurl versions < 7.28.0 do not support selecting on "
1181 "file descriptors of 1024 or higher.");
1183 if (ret) {
1184 *ret = nfds;
1188 #ifndef HAVE_CURL_MULTI_SELECT
1189 # ifdef HAVE_CURL_MULTI_WAIT
1190 # define curl_multi_select(mh, tm, ret) curl_multi_wait((mh), NULL, 0, (tm), (ret))
1191 # else
1192 # define curl_multi_select hphp_curl_multi_select
1193 # endif
1194 #endif
1196 Variant f_curl_multi_select(CResRef mh, double timeout /* = 1.0 */) {
1197 CHECK_MULTI_RESOURCE(curlm);
1198 int ret;
1199 unsigned long timeout_ms = (unsigned long)(timeout * 1000.0);
1200 IOStatusHelper io("curl_multi_select");
1201 curl_multi_select(curlm->get(), timeout_ms, &ret);
1202 return ret;
1205 Variant f_curl_multi_getcontent(CResRef ch) {
1206 CHECK_RESOURCE(curl);
1207 return curl->getContents();
1210 Array f_curl_convert_fd_to_stream(fd_set *fd, int max_fd) {
1211 Array ret = Array::Create();
1212 for (int i=0; i<=max_fd; i++) {
1213 if (FD_ISSET(i, fd)) {
1214 BuiltinFile *file = NEWOBJ(BuiltinFile)(i);
1215 ret.append(file);
1218 return ret;
1221 Variant f_fb_curl_multi_fdset(CResRef mh,
1222 VRefParam read_fd_set,
1223 VRefParam write_fd_set,
1224 VRefParam exc_fd_set,
1225 VRefParam max_fd /* = null_object */) {
1226 CHECK_MULTI_RESOURCE(curlm);
1228 fd_set read_set;
1229 fd_set write_set;
1230 fd_set exc_set;
1231 int max = 0;
1233 FD_ZERO(&read_set);
1234 FD_ZERO(&write_set);
1235 FD_ZERO(&exc_set);
1237 int r = curl_multi_fdset(curlm->get(), &read_set, &write_set, &exc_set, &max);
1238 read_fd_set = f_curl_convert_fd_to_stream(&read_set, max);
1239 write_fd_set = f_curl_convert_fd_to_stream(&write_set, max);
1240 exc_fd_set = f_curl_convert_fd_to_stream(&exc_set, max);
1241 max_fd = max;
1243 return r;
1246 const StaticString
1247 s_msg("msg"),
1248 s_result("result"),
1249 s_handle("handle"),
1250 s_headers("headers"),
1251 s_requests("requests");
1253 Variant f_curl_multi_info_read(CResRef mh,
1254 VRefParam msgs_in_queue /* = null */) {
1255 CHECK_MULTI_RESOURCE(curlm);
1257 int queued_msgs;
1258 CURLMsg *tmp_msg = curl_multi_info_read(curlm->get(), &queued_msgs);
1259 curlm->check_exceptions();
1260 if (tmp_msg == NULL) {
1261 return false;
1263 msgs_in_queue = queued_msgs;
1265 Array ret;
1266 ret.set(s_msg, tmp_msg->msg);
1267 ret.set(s_result, tmp_msg->data.result);
1268 Resource curle = curlm->find(tmp_msg->easy_handle);
1269 if (!curle.isNull()) {
1270 ret.set(s_handle, curle);
1272 return ret;
1275 Variant f_curl_multi_close(CResRef mh) {
1276 CHECK_MULTI_RESOURCE(curlm);
1277 curlm->close();
1278 return uninit_null();
1281 ///////////////////////////////////////////////////////////////////////////////
1282 // evhttp functions
1284 class LibEventHttpHandle : public SweepableResourceData {
1285 public:
1286 DECLARE_OBJECT_ALLOCATION(LibEventHttpHandle)
1288 static StaticString s_class_name;
1289 // overriding ResourceData
1290 virtual CStrRef o_getClassNameHook() const { return s_class_name; }
1292 explicit LibEventHttpHandle(LibEventHttpClientPtr client) : m_client(client) {
1295 ~LibEventHttpHandle() {
1296 if (m_client) {
1297 m_client->release();
1301 LibEventHttpClientPtr m_client;
1303 IMPLEMENT_OBJECT_ALLOCATION(LibEventHttpHandle)
1305 StaticString LibEventHttpHandle::s_class_name("LibEventHttp");
1307 static LibEventHttpClientPtr prepare_client
1308 (CStrRef url, CStrRef data, CArrRef headers, int timeout,
1309 bool async, bool post) {
1310 string sUrl = url.data();
1311 if (sUrl.size() < 7 || sUrl.substr(0, 7) != "http://") {
1312 raise_warning("Invalid URL: %s", sUrl.c_str());
1313 return LibEventHttpClientPtr();
1316 // parsing server address
1317 size_t pos = sUrl.find('/', 7);
1318 string path;
1319 if (pos == string::npos) {
1320 pos = sUrl.length();
1321 path = "/";
1322 } else if (pos == 7) {
1323 raise_warning("Invalid URL: %s", sUrl.c_str());
1324 return LibEventHttpClientPtr();
1325 } else {
1326 path = sUrl.substr(pos);
1328 string address = sUrl.substr(7, pos - 7);
1330 // parsing server port
1331 pos = address.find(':');
1332 int port = 80;
1333 if (pos != string::npos) {
1334 if (pos < address.length() - 1) {
1335 string sport = address.substr(pos + 1, address.length() - pos - 1);
1336 port = atoi(sport.c_str());
1338 address = address.substr(0, pos);
1341 LibEventHttpClientPtr client = LibEventHttpClient::Get(address, port);
1342 if (!client) {
1343 return client;
1346 vector<string> sheaders;
1347 for (ArrayIter iter(headers); iter; ++iter) {
1348 sheaders.push_back(iter.second().toString().data());
1350 if (!client->send(path.c_str(), sheaders, timeout, async,
1351 post ? (void*)data.data() : NULL,
1352 post ? data.size() : 0)) {
1353 return LibEventHttpClientPtr();
1355 return client;
1358 const StaticString
1359 s_code("code"),
1360 s_response("response");
1362 static Array prepare_response(LibEventHttpClientPtr client) {
1363 int len = 0;
1364 char *res = client->recv(len); // block on return
1366 ArrayInit ret(4);
1367 ret.set(s_code, client->getCode());
1368 ret.set(s_response, String(res, len, AttachString));
1370 Array headers = Array::Create();
1371 const vector<string> &responseHeaders = client->getResponseHeaders();
1372 for (unsigned int i = 0; i < responseHeaders.size(); i++) {
1373 headers.append(String(responseHeaders[i]));
1375 ret.set(s_headers, headers);
1376 ret.set(s_requests, client->getRequests());
1377 return ret.create();
1380 ///////////////////////////////////////////////////////////////////////////////
1382 void f_evhttp_set_cache(CStrRef address, int max_conn, int port /* = 80 */) {
1383 if (RuntimeOption::ServerHttpSafeMode) {
1384 throw_fatal("evhttp_set_cache is disabled");
1386 LibEventHttpClient::SetCache(address.data(), port, max_conn);
1389 Variant f_evhttp_get(CStrRef url, CArrRef headers /* = null_array */,
1390 int timeout /* = 5 */) {
1391 if (RuntimeOption::ServerHttpSafeMode) {
1392 throw_fatal("evhttp_set_cache is disabled");
1394 LibEventHttpClientPtr client = prepare_client(url, "", headers, timeout,
1395 false, false);
1396 if (client) {
1397 Variant ret = prepare_response(client);
1398 client->release();
1399 return ret;
1401 return false;
1404 Variant f_evhttp_post(CStrRef url, CStrRef data,
1405 CArrRef headers /* = null_array */,
1406 int timeout /* = 5 */) {
1407 if (RuntimeOption::ServerHttpSafeMode) {
1408 throw_fatal("evhttp_post is disabled");
1410 LibEventHttpClientPtr client = prepare_client(url, data, headers, timeout,
1411 false, true);
1412 if (client) {
1413 Variant ret = prepare_response(client);
1414 client->release();
1415 return ret;
1417 return false;
1420 Variant f_evhttp_async_get(CStrRef url, CArrRef headers /* = null_array */,
1421 int timeout /* = 5 */) {
1422 if (RuntimeOption::ServerHttpSafeMode) {
1423 throw_fatal("evhttp_async_get is disabled");
1425 LibEventHttpClientPtr client = prepare_client(url, "", headers, timeout,
1426 true, false);
1427 if (client) {
1428 return Resource(NEWOBJ(LibEventHttpHandle)(client));
1430 return false;
1433 Variant f_evhttp_async_post(CStrRef url, CStrRef data,
1434 CArrRef headers /* = null_array */,
1435 int timeout /* = 5 */) {
1436 if (RuntimeOption::ServerHttpSafeMode) {
1437 throw_fatal("evhttp_async_post is disabled");
1439 LibEventHttpClientPtr client = prepare_client(url, data, headers, timeout,
1440 true, true);
1441 if (client) {
1442 return Resource(NEWOBJ(LibEventHttpHandle)(client));
1444 return false;
1447 Variant f_evhttp_recv(CResRef handle) {
1448 if (RuntimeOption::ServerHttpSafeMode) {
1449 throw_fatal("evhttp_recv is disabled");
1451 LibEventHttpHandle *obj = handle.getTyped<LibEventHttpHandle>();
1452 if (obj->m_client) {
1453 return prepare_response(obj->m_client);
1455 return false;
1458 #if LIBCURL_VERSION_NUM >= 0x071500
1459 const int64_t k_CURLINFO_LOCAL_PORT = CURLINFO_LOCAL_PORT;
1460 #else
1461 const int64_t k_CURLINFO_LOCAL_PORT = CURLINFO_NONE;
1462 #endif
1464 #if LIBCURL_VERSION_NUM >= 0x071002
1465 const int64_t k_CURLOPT_TIMEOUT_MS = CURLOPT_TIMEOUT_MS;
1466 const int64_t k_CURLOPT_CONNECTTIMEOUT_MS = CURLOPT_CONNECTTIMEOUT_MS;
1467 #else
1468 const int64_t k_CURLOPT_TIMEOUT_MS = CURLOPT_LASTENTRY;
1469 const int64_t k_CURLOPT_CONNECTTIMEOUT_MS = CURLOPT_LASTENTRY;
1470 #endif
1472 ///////////////////////////////////////////////////////////////////////////////