update config.m4; allow disabling idna; add idnkit
[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
88 #if DBG_EVENTS
89 fprintf(stderr, "\ntimer <- timeout_ms: %ld\n", timeout_ms);
90 #endif
91
92 if (timeout_ms < 0) {
93 php_http_client_curl_event_handler(context, CURL_SOCKET_TIMEOUT, 0);
94 } else if (timeout_ms > 0 || !event_initialized(context->timeout) || !event_pending(context->timeout, EV_TIMEOUT, NULL)) {
95 struct timeval timeout;
96
97 if (!event_initialized(context->timeout)) {
98 event_assign(context->timeout, context->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_client_curl_event_timeout_callback, context);
99 }
100
101 timeout.tv_sec = timeout_ms / 1000;
102 timeout.tv_usec = (timeout_ms % 1000) * 1000;
103
104 event_add(context->timeout, &timeout);
105 }
106 }
107
108 static void php_http_client_curl_event_callback(int socket, short action, void *event_data)
109 {
110 php_http_client_curl_event_context_t *ctx = event_data;
111 php_http_client_curl_t *curl = ctx->client->ctx;
112
113 #if DBG_EVENTS
114 fprintf(stderr, "E");
115 #endif
116
117 php_http_client_curl_event_handler(event_data, socket, etoca(action));
118
119 /* remove timeout if there are no transfers left */
120 if (!curl->unfinished && event_initialized(ctx->timeout) && event_pending(ctx->timeout, EV_TIMEOUT, NULL)) {
121 event_del(ctx->timeout);
122 }
123 }
124
125 static int php_http_client_curl_event_socket(CURL *easy, curl_socket_t sock, int action, void *socket_data, void *assign_data)
126 {
127 php_http_client_curl_event_context_t *ctx = socket_data;
128 php_http_client_curl_t *curl = ctx->client->ctx;
129 int events = EV_PERSIST;
130 php_http_client_curl_event_ev_t *ev = assign_data;
131
132 #if DBG_EVENTS
133 fprintf(stderr, "S");
134 #endif
135
136 if (!ev) {
137 ev = ecalloc(1, sizeof(*ev));
138 ev->context = ctx;
139 curl_multi_assign(curl->handle->multi, sock, ev);
140 } else {
141 event_del(&ev->evnt);
142 }
143
144 switch (action) {
145 case CURL_POLL_IN:
146 events |= EV_READ;
147 break;
148 case CURL_POLL_OUT:
149 events |= EV_WRITE;
150 break;
151 case CURL_POLL_INOUT:
152 events |= EV_READ|EV_WRITE;
153 break;
154
155 case CURL_POLL_REMOVE:
156 efree(ev);
157 /* no break */
158 case CURL_POLL_NONE:
159 return 0;
160
161 default:
162 php_error_docref(NULL, E_WARNING, "Unknown socket action %d", action);
163 return -1;
164 }
165
166 event_assign(&ev->evnt, ctx->evbase, sock, events, php_http_client_curl_event_callback, ctx);
167 event_add(&ev->evnt, NULL);
168
169 return 0;
170 }
171
172 static ZEND_RESULT_CODE php_http_client_curl_event_once(void *context)
173 {
174 php_http_client_curl_event_context_t *ctx = context;
175
176 #if DBG_EVENTS
177 fprintf(stderr, "O");
178 #endif
179
180 if (0 > event_base_loop(ctx->evbase, EVLOOP_NONBLOCK)) {
181 return FAILURE;
182 }
183 return SUCCESS;
184 }
185
186 static ZEND_RESULT_CODE php_http_client_curl_event_wait(void *context, struct timeval *custom_timeout)
187 {
188 php_http_client_curl_event_context_t *ctx = context;
189 struct timeval timeout;
190
191 #if DBG_EVENTS
192 fprintf(stderr, "W");
193 #endif
194
195 if (!event_initialized(ctx->timeout)) {
196 if (0 > event_assign(ctx->timeout, ctx->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_client_curl_event_timeout_callback, ctx)) {
197 return FAILURE;
198 }
199 } else if (custom_timeout && timerisset(custom_timeout)) {
200 if (0 > event_add(ctx->timeout, custom_timeout)) {
201 return FAILURE;
202 }
203 } else if (!event_pending(ctx->timeout, EV_TIMEOUT, NULL)) {
204 php_http_client_curl_get_timeout(ctx->client->ctx, 1000, &timeout);
205 if (0 > event_add(ctx->timeout, &timeout)) {
206 return FAILURE;
207 }
208 }
209
210 if (0 > event_base_loop(ctx->evbase, EVLOOP_ONCE)) {
211 return FAILURE;
212 }
213
214 return SUCCESS;
215 }
216
217 static ZEND_RESULT_CODE php_http_client_curl_event_exec(void *context)
218 {
219 php_http_client_curl_event_context_t *ctx = context;
220 php_http_client_curl_t *curl = ctx->client->ctx;
221
222 #if DBG_EVENTS
223 fprintf(stderr, "E");
224 #endif
225
226 /* kickstart */
227 php_http_client_curl_event_handler(ctx, CURL_SOCKET_TIMEOUT, 0);
228
229 do {
230 if (0 > event_base_dispatch(ctx->evbase)) {
231 return FAILURE;
232 }
233 } while (curl->unfinished && !EG(exception));
234
235 return SUCCESS;
236 }
237
238 static void *php_http_client_curl_event_init(php_http_client_t *client)
239 {
240 php_http_client_curl_t *curl = client->ctx;
241 php_http_client_curl_event_context_t *ctx;
242 struct event_base *evb = event_base_new();
243
244 #if DBG_EVENTS
245 fprintf(stderr, "I");
246 #endif
247
248 if (!evb) {
249 return NULL;
250 }
251
252 ctx = ecalloc(1, sizeof(*ctx));
253 ctx->client = client;
254 ctx->evbase = evb;
255 ctx->timeout = ecalloc(1, sizeof(struct event));
256
257 curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETDATA, ctx);
258 curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETFUNCTION, php_http_client_curl_event_socket);
259 curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERDATA, ctx);
260 curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERFUNCTION, php_http_client_curl_event_timer);
261
262 return ctx;
263 }
264
265 static void php_http_client_curl_event_dtor(void **context)
266 {
267 php_http_client_curl_event_context_t *ctx = *context;
268 php_http_client_curl_t *curl;
269
270 #if DBG_EVENTS
271 fprintf(stderr, "D");
272 #endif
273
274 curl = ctx->client->ctx;
275
276 curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETDATA, NULL);
277 curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETFUNCTION, NULL);
278 curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERDATA, NULL);
279 curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERFUNCTION, NULL);
280
281 if (event_initialized(ctx->timeout) && event_pending(ctx->timeout, EV_TIMEOUT, NULL)) {
282 event_del(ctx->timeout);
283 }
284 efree(ctx->timeout);
285 event_base_free(ctx->evbase);
286
287 efree(ctx);
288 *context = NULL;
289 }
290
291 static php_http_client_curl_ops_t php_http_client_curl_event_ops = {
292 &php_http_client_curl_event_init,
293 &php_http_client_curl_event_dtor,
294 &php_http_client_curl_event_once,
295 &php_http_client_curl_event_wait,
296 &php_http_client_curl_event_exec,
297 };
298
299 php_http_client_curl_ops_t *php_http_client_curl_event_ops_get()
300 {
301 return &php_http_client_curl_event_ops;
302 }
303
304 #endif /* PHP_HTTP_HAVE_LIBEVENT */
305 #endif /* PHP_HTTP_HAVE_LIBCURL */
306
307 /*
308 * Local variables:
309 * tab-width: 4
310 * c-basic-offset: 4
311 * End:
312 * vim600: noet sw=4 ts=4 fdm=marker
313 * vim<600: noet sw=4 ts=4
314 */