prepare v4.2.5
[m6w6/ext-http] / src / php_http_client_curl_event.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: http |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2004-2014, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 #include "php_http_api.h"
14
15 #if PHP_HTTP_HAVE_LIBCURL
16 #if PHP_HTTP_HAVE_LIBEVENT
17
18 #include <event.h>
19 #ifndef DBG_EVENTS
20 # define DBG_EVENTS 0
21 #endif
22
23 typedef struct php_http_client_curl_event_context {
24 php_http_client_t *client;
25 struct event_base *evbase;
26 struct event *timeout;
27 } php_http_client_curl_event_context_t;
28
29 typedef struct php_http_client_curl_event_ev {
30 struct event evnt;
31 php_http_client_curl_event_context_t *context;
32 } php_http_client_curl_event_ev_t;
33
34 static inline int etoca(short action) {
35 switch (action & (EV_READ|EV_WRITE)) {
36 case EV_READ:
37 return CURL_CSELECT_IN;
38 break;
39 case EV_WRITE:
40 return CURL_CSELECT_OUT;
41 break;
42 case EV_READ|EV_WRITE:
43 return CURL_CSELECT_IN|CURL_CSELECT_OUT;
44 break;
45 default:
46 return 0;
47 }
48 }
49
50 static void php_http_client_curl_event_handler(void *context, curl_socket_t s, int curl_action)
51 {
52 CURLMcode rc;
53 php_http_client_curl_event_context_t *ctx = context;
54 php_http_client_curl_t *curl = ctx->client->ctx;
55
56 #if DBG_EVENTS
57 fprintf(stderr, "H");
58 #endif
59
60 do {
61 rc = curl_multi_socket_action(curl->handle->multi, s, curl_action, &curl->unfinished);
62 } while (CURLM_CALL_MULTI_PERFORM == rc);
63
64 if (CURLM_OK != rc) {
65 php_error_docref(NULL, E_WARNING, "%s", curl_multi_strerror(rc));
66 }
67
68 php_http_client_curl_responsehandler(ctx->client);
69 }
70
71 static void php_http_client_curl_event_timeout_callback(int socket, short action, void *event_data)
72 {
73 #if DBG_EVENTS
74 fprintf(stderr, "T");
75 #endif
76
77 /* ignore and use -1,0 on timeout */
78 (void) socket;
79 (void) action;
80
81 php_http_client_curl_event_handler(event_data, CURL_SOCKET_TIMEOUT, 0);
82 }
83
84 static void php_http_client_curl_event_timer(CURLM *multi, long timeout_ms, void *timer_data)
85 {
86 php_http_client_curl_event_context_t *context = timer_data;
87 struct timeval timeout;
88
89 #if DBG_EVENTS
90 fprintf(stderr, "(%ld)", timeout_ms);
91 #endif
92
93 switch (timeout_ms) {
94 case -1:
95 if (event_initialized(context->timeout) && event_pending(context->timeout, EV_TIMEOUT, NULL)) {
96 event_del(context->timeout);
97 }
98 break;
99 case 0:
100 default:
101 if (!event_initialized(context->timeout)) {
102 event_assign(context->timeout, context->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_client_curl_event_timeout_callback, context);
103 }
104
105 timeout.tv_sec = timeout_ms / 1000;
106 timeout.tv_usec = (timeout_ms % 1000) * 1000;
107
108 if (!event_pending(context->timeout, EV_TIMEOUT, &timeout)) {
109 event_add(context->timeout, &timeout);
110 }
111 break;
112 }
113 }
114
115 static void php_http_client_curl_event_callback(int socket, short action, void *event_data)
116 {
117 php_http_client_curl_event_context_t *ctx = event_data;
118 php_http_client_curl_t *curl = ctx->client->ctx;
119
120 #if DBG_EVENTS
121 fprintf(stderr, "E");
122 #endif
123
124 php_http_client_curl_event_handler(event_data, socket, etoca(action));
125
126 /* remove timeout if there are no transfers left */
127 if (!curl->unfinished && event_initialized(ctx->timeout) && event_pending(ctx->timeout, EV_TIMEOUT, NULL)) {
128 event_del(ctx->timeout);
129 }
130 }
131
132 static int php_http_client_curl_event_socket(CURL *easy, curl_socket_t sock, int action, void *socket_data, void *assign_data)
133 {
134 php_http_client_curl_event_context_t *ctx = socket_data;
135 php_http_client_curl_t *curl = ctx->client->ctx;
136 int events = EV_PERSIST;
137 php_http_client_curl_event_ev_t *ev = assign_data;
138
139 #if DBG_EVENTS
140 fprintf(stderr, "S");
141 #endif
142
143 if (!ev) {
144 ev = ecalloc(1, sizeof(*ev));
145 ev->context = ctx;
146 curl_multi_assign(curl->handle->multi, sock, ev);
147 } else {
148 event_del(&ev->evnt);
149 }
150
151 switch (action) {
152 case CURL_POLL_IN:
153 events |= EV_READ;
154 break;
155 case CURL_POLL_OUT:
156 events |= EV_WRITE;
157 break;
158 case CURL_POLL_INOUT:
159 events |= EV_READ|EV_WRITE;
160 break;
161
162 case CURL_POLL_REMOVE:
163 efree(ev);
164 /* no break */
165 case CURL_POLL_NONE:
166 return 0;
167
168 default:
169 php_error_docref(NULL, E_WARNING, "Unknown socket action %d", action);
170 return -1;
171 }
172
173 event_assign(&ev->evnt, ctx->evbase, sock, events, php_http_client_curl_event_callback, ctx);
174 event_add(&ev->evnt, NULL);
175
176 return 0;
177 }
178
179 static ZEND_RESULT_CODE php_http_client_curl_event_once(void *context)
180 {
181 php_http_client_curl_event_context_t *ctx = context;
182
183 #if DBG_EVENTS
184 fprintf(stderr, "O");
185 #endif
186
187 if (0 > event_base_loop(ctx->evbase, EVLOOP_NONBLOCK)) {
188 return FAILURE;
189 }
190 return SUCCESS;
191 }
192
193 static ZEND_RESULT_CODE php_http_client_curl_event_wait(void *context, struct timeval *custom_timeout)
194 {
195 php_http_client_curl_event_context_t *ctx = context;
196 struct timeval timeout;
197
198 #if DBG_EVENTS
199 fprintf(stderr, "W");
200 #endif
201
202 if (!event_initialized(ctx->timeout)) {
203 if (0 > event_assign(ctx->timeout, ctx->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_client_curl_event_timeout_callback, ctx)) {
204 return FAILURE;
205 }
206 } else if (custom_timeout && timerisset(custom_timeout)) {
207 if (0 > event_add(ctx->timeout, custom_timeout)) {
208 return FAILURE;
209 }
210 } else if (!event_pending(ctx->timeout, EV_TIMEOUT, NULL)) {
211 php_http_client_curl_get_timeout(ctx->client->ctx, 1000, &timeout);
212 if (0 > event_add(ctx->timeout, &timeout)) {
213 return FAILURE;
214 }
215 }
216
217 if (0 > event_base_loop(ctx->evbase, EVLOOP_ONCE)) {
218 return FAILURE;
219 }
220
221 return SUCCESS;
222 }
223
224 static ZEND_RESULT_CODE php_http_client_curl_event_exec(void *context)
225 {
226 php_http_client_curl_event_context_t *ctx = context;
227 php_http_client_curl_t *curl = ctx->client->ctx;
228
229 #if DBG_EVENTS
230 fprintf(stderr, "E");
231 #endif
232
233 /* kickstart */
234 php_http_client_curl_event_handler(ctx, CURL_SOCKET_TIMEOUT, 0);
235
236 do {
237 if (0 > event_base_dispatch(ctx->evbase)) {
238 return FAILURE;
239 }
240 } while (curl->unfinished && !EG(exception));
241
242 return SUCCESS;
243 }
244
245 static void *php_http_client_curl_event_init(php_http_client_t *client)
246 {
247 php_http_client_curl_t *curl = client->ctx;
248 php_http_client_curl_event_context_t *ctx;
249 struct event_base *evb = event_base_new();
250
251 #if DBG_EVENTS
252 fprintf(stderr, "I");
253 #endif
254
255 if (!evb) {
256 return NULL;
257 }
258
259 ctx = ecalloc(1, sizeof(*ctx));
260 ctx->client = client;
261 ctx->evbase = evb;
262 ctx->timeout = ecalloc(1, sizeof(struct event));
263
264 curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETDATA, ctx);
265 curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETFUNCTION, php_http_client_curl_event_socket);
266 curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERDATA, ctx);
267 curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERFUNCTION, php_http_client_curl_event_timer);
268
269 return ctx;
270 }
271
272 static void php_http_client_curl_event_dtor(void **context)
273 {
274 php_http_client_curl_event_context_t *ctx = *context;
275 php_http_client_curl_t *curl;
276
277 #if DBG_EVENTS
278 fprintf(stderr, "D");
279 #endif
280
281 curl = ctx->client->ctx;
282
283 curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETDATA, NULL);
284 curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETFUNCTION, NULL);
285 curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERDATA, NULL);
286 curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERFUNCTION, NULL);
287
288 if (event_initialized(ctx->timeout) && event_pending(ctx->timeout, EV_TIMEOUT, NULL)) {
289 event_del(ctx->timeout);
290 }
291 efree(ctx->timeout);
292 event_base_free(ctx->evbase);
293
294 efree(ctx);
295 *context = NULL;
296 }
297
298 static php_http_client_curl_ops_t php_http_client_curl_event_ops = {
299 &php_http_client_curl_event_init,
300 &php_http_client_curl_event_dtor,
301 &php_http_client_curl_event_once,
302 &php_http_client_curl_event_wait,
303 &php_http_client_curl_event_exec,
304 };
305
306 php_http_client_curl_ops_t *php_http_client_curl_event_ops_get()
307 {
308 return &php_http_client_curl_event_ops;
309 }
310
311 #endif /* PHP_HTTP_HAVE_LIBEVENT */
312 #endif /* PHP_HTTP_HAVE_LIBCURL */
313
314 /*
315 * Local variables:
316 * tab-width: 4
317 * c-basic-offset: 4
318 * End:
319 * vim600: noet sw=4 ts=4 fdm=marker
320 * vim<600: noet sw=4 ts=4
321 */