1 /* 2 +----------------------------------------------------------------------+ 3 | Swoole | 4 +----------------------------------------------------------------------+ 5 | This source file is subject to version 2.0 of the Apache license, | 6 | that is bundled with this package in the file LICENSE, and is | 7 | available through the world-wide-web at the following url: | 8 | http://www.apache.org/licenses/LICENSE-2.0.html | 9 | If you did not receive a copy of the Apache2.0 license and are unable| 10 | to obtain it through the world-wide-web, please send a note to | 11 | license@swoole.com so we can mail you a copy immediately. | 12 +----------------------------------------------------------------------+ 13 | Author: Tianfeng Han <mikan.tenny@gmail.com> | 14 +----------------------------------------------------------------------+ 15 */ 16 17 #pragma once 18 19 #include "php_swoole_cxx.h" 20 21 #ifdef SW_USE_CURL 22 #include "swoole_util.h" 23 24 SW_EXTERN_C_BEGIN 25 #include <curl/curl.h> 26 #include <curl/multi.h> 27 #include "thirdparty/php/curl/curl_private.h" 28 SW_EXTERN_C_END 29 30 namespace swoole { 31 32 using network::Socket; 33 34 namespace curl { 35 36 class Multi; 37 38 struct Handle { 39 CURL *cp; 40 Socket *socket; 41 Multi *multi; 42 int event_bitmask; 43 int event_fd; 44 int action; 45 }; 46 47 struct Selector { 48 bool timer_callback = false; 49 std::set<Handle *> active_handles; 50 }; 51 52 class Multi { 53 CURLM *multi_handle_; 54 TimerNode *timer = nullptr; 55 long timeout_ms_ = 0; 56 Coroutine *co = nullptr; 57 int running_handles_ = 0; 58 int last_sockfd; 59 int event_count_ = 0; 60 bool defer_callback = false; 61 std::unique_ptr<Selector> selector; 62 63 CURLcode read_info(); 64 65 Socket *create_socket(CURL *cp, curl_socket_t sockfd); 66 get_handle(CURL * cp)67 Handle *get_handle(CURL *cp) { 68 Handle *handle = nullptr; 69 curl_easy_getinfo(cp, CURLINFO_PRIVATE, &handle); 70 return handle; 71 } 72 73 void set_event(CURL *easy, void *socket_ptr, curl_socket_t sockfd, int action); 74 void del_event(CURL *easy, void *socket_ptr, curl_socket_t sockfd); 75 add_timer(long timeout_ms)76 void add_timer(long timeout_ms) { 77 if (timer && swoole_timer_is_available()) { 78 swoole_timer_del(timer); 79 } 80 timeout_ms_ = timeout_ms; 81 timer = swoole_timer_add(timeout_ms, false, [this](Timer *timer, TimerNode *tnode) { 82 this->timer = nullptr; 83 callback(nullptr, 0); 84 }); 85 } 86 del_timer()87 void del_timer() { 88 if (timer && swoole_timer_is_available()) { 89 swoole_timer_del(timer); 90 timeout_ms_ = -1; 91 timer = nullptr; 92 } 93 } 94 set_timer()95 void set_timer() { 96 long _timeout_ms = 0; 97 curl_multi_timeout(multi_handle_, &_timeout_ms); 98 handle_timeout(multi_handle_, _timeout_ms, this); 99 } 100 101 public: Multi()102 Multi() { 103 multi_handle_ = curl_multi_init(); 104 co = nullptr; 105 curl_multi_setopt(multi_handle_, CURLMOPT_SOCKETFUNCTION, handle_socket); 106 curl_multi_setopt(multi_handle_, CURLMOPT_TIMERFUNCTION, handle_timeout); 107 curl_multi_setopt(multi_handle_, CURLMOPT_SOCKETDATA, this); 108 curl_multi_setopt(multi_handle_, CURLMOPT_TIMERDATA, this); 109 } 110 ~Multi()111 ~Multi() { 112 curl_multi_cleanup(multi_handle_); 113 } 114 get_multi_handle()115 CURLM *get_multi_handle() { 116 return multi_handle_; 117 } 118 get_running_handles()119 int get_running_handles() { 120 return running_handles_; 121 } 122 set_selector(Selector * _selector)123 void set_selector(Selector *_selector) { 124 selector.reset(_selector); 125 } 126 127 CURLMcode add_handle(CURL *cp); 128 CURLMcode remove_handle(CURL *cp); 129 perform()130 CURLMcode perform() { 131 return curl_multi_perform(multi_handle_, &running_handles_); 132 } 133 get_event(int action)134 int get_event(int action) { 135 return action == CURL_POLL_IN ? SW_EVENT_READ : SW_EVENT_WRITE; 136 } 137 check_bound_co()138 Coroutine *check_bound_co() { 139 if (co) { 140 swoole_fatal_error(SW_ERROR_CO_HAS_BEEN_BOUND, "cURL is executing, cannot be operated"); 141 return nullptr; 142 } 143 return Coroutine::get_current_safe(); 144 } 145 146 CURLcode exec(php_curl *ch); 147 long select(php_curlm *mh, double timeout = -1); 148 void callback(Handle *handle, int event_bitmask); 149 150 static int cb_readable(Reactor *reactor, Event *event); 151 static int cb_writable(Reactor *reactor, Event *event); 152 static int cb_error(Reactor *reactor, Event *event); 153 static int handle_socket(CURL *easy, curl_socket_t s, int action, void *userp, void *socketp); 154 static int handle_timeout(CURLM *multi, long timeout_ms, void *userp); 155 }; 156 }; // namespace curl 157 } // namespace swoole 158 #endif 159