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