02c40b4be1afe5479ea82b7147f95096e1863c1d
[m6w6/ext-http] / http_request_datashare_api.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-2007, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 /* $Id$ */
14
15 #define HTTP_WANT_CURL
16 #include "php_http.h"
17
18 #if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL)
19
20 #include "php_http_api.h"
21 #include "php_http_request_datashare_api.h"
22 #include "php_http_request_api.h"
23 #include "php_http_request_object.h"
24 #ifdef HTTP_HAVE_PERSISTENT_HANDLES
25 # include "php_http_persistent_handle_api.h"
26 #endif
27
28 #ifdef HTTP_HAVE_PERSISTENT_HANDLES
29 # define HTTP_CURL_SHARE_CTOR(ch) (SUCCESS == http_persistent_handle_acquire("http_request_datashare", &(ch)))
30 # define HTTP_CURL_SHARE_DTOR(chp) http_persistent_handle_release("http_request_datashare", (chp))
31 # define HTTP_CURL_SLOCK_CTOR(l) (SUCCESS == http_persistent_handle_acquire("http_request_datashare_lock", (void *) &(l)))
32 # define HTTP_CURL_SLOCK_DTOR(lp) http_persistent_handle_release("http_request_datashare_lock", (void *) (lp))
33 #else
34 # define HTTP_CURL_SHARE_CTOR(ch) ((ch) = curl_share_init())
35 # define HTTP_CURL_SHARE_DTOR(chp) curl_share_cleanup(*(chp)); *(chp) = NULL
36 # define HTTP_CURL_SLOCK_CTOR(l) ((l) = http_request_datashare_locks_init())
37 # define HTTP_CURL_SLOCK_DTOR(lp) http_request_datashare_locks_dtor(*(lp)); *(lp) = NULL
38 #endif
39
40 static HashTable http_request_datashare_options;
41 static http_request_datashare http_request_datashare_global;
42 static int http_request_datashare_compare_handles(void *h1, void *h2);
43 static void http_request_datashare_destroy_handles(void *el);
44 #ifdef ZTS
45 static void *http_request_datashare_locks_init(void);
46 static void http_request_datashare_locks_dtor(void *l);
47 static void http_request_datashare_lock_func(CURL *handle, curl_lock_data data, curl_lock_access locktype, void *userptr);
48 static void http_request_datashare_unlock_func(CURL *handle, curl_lock_data data, void *userptr);
49 #endif
50
51 http_request_datashare *_http_request_datashare_global_get(void)
52 {
53 return &http_request_datashare_global;
54 }
55
56 PHP_MINIT_FUNCTION(http_request_datashare)
57 {
58 curl_lock_data val;
59
60 #ifdef HTTP_HAVE_PERSISTENT_HANDLES
61 if (SUCCESS != http_persistent_handle_provide("http_request_datashare", curl_share_init, (http_persistent_handle_dtor) curl_share_cleanup)) {
62 return FAILURE;
63 }
64 # ifdef ZTS
65 if (SUCCESS != http_persistent_handle_provide("http_request_datashare_lock", http_request_datashare_locks_init, http_request_datashare_locks_dtor)) {
66 return FAILURE;
67 }
68 # endif
69 #endif
70
71 if (!http_request_datashare_init_ex(&http_request_datashare_global, 1)) {
72 return FAILURE;
73 }
74
75 zend_hash_init(&http_request_datashare_options, 4, NULL, NULL, 1);
76 #define ADD_DATASHARE_OPT(name, opt) \
77 val = opt; \
78 zend_hash_add(&http_request_datashare_options, name, sizeof(name), &val, sizeof(curl_lock_data), NULL)
79 ADD_DATASHARE_OPT("cookie", CURL_LOCK_DATA_COOKIE);
80 ADD_DATASHARE_OPT("dns", CURL_LOCK_DATA_DNS);
81 ADD_DATASHARE_OPT("ssl", CURL_LOCK_DATA_SSL_SESSION);
82 ADD_DATASHARE_OPT("connect", CURL_LOCK_DATA_CONNECT);
83
84 return SUCCESS;
85 }
86
87 PHP_MSHUTDOWN_FUNCTION(http_request_datashare)
88 {
89 http_request_datashare_dtor(&http_request_datashare_global);
90 zend_hash_destroy(&http_request_datashare_options);
91
92 return SUCCESS;
93 }
94
95 PHP_RINIT_FUNCTION(http_request_datashare)
96 {
97 zend_llist_init(&HTTP_G->request.datashare.handles, sizeof(zval *), http_request_datashare_destroy_handles, 0);
98
99 return SUCCESS;
100 }
101
102 PHP_RSHUTDOWN_FUNCTION(http_request_datashare)
103 {
104 zend_llist_destroy(&HTTP_G->request.datashare.handles);
105
106 return SUCCESS;
107 }
108
109 PHP_HTTP_API http_request_datashare *_http_request_datashare_init_ex(http_request_datashare *share, zend_bool persistent TSRMLS_DC)
110 {
111 zend_bool free_share;
112
113 if ((free_share = !share)) {
114 share = pemalloc(sizeof(http_request_datashare), persistent);
115 }
116 memset(share, 0, sizeof(http_request_datashare));
117
118 if (!HTTP_CURL_SHARE_CTOR(share->ch)) {
119 if (free_share) {
120 pefree(share, persistent);
121 }
122 return NULL;
123 }
124
125 if (!(share->persistent = persistent)) {
126 share->handle.list = emalloc(sizeof(zend_llist));
127 zend_llist_init(share->handle.list, sizeof(zval *), ZVAL_PTR_DTOR, 0);
128 #ifdef ZTS
129 } else {
130 if (HTTP_CURL_SLOCK_CTOR(share->handle.locks)) {
131 curl_share_setopt(share->ch, CURLSHOPT_LOCKFUNC, http_request_datashare_lock_func);
132 curl_share_setopt(share->ch, CURLSHOPT_UNLOCKFUNC, http_request_datashare_unlock_func);
133 curl_share_setopt(share->ch, CURLSHOPT_USERDATA, share);
134 }
135 #endif
136 }
137
138 return share;
139 }
140
141 PHP_HTTP_API STATUS _http_request_datashare_attach(http_request_datashare *share, zval *request TSRMLS_DC)
142 {
143 CURLcode rc;
144 getObjectEx(http_request_object, obj, request);
145
146 if (obj->share) {
147 if (obj->share == share) {
148 return SUCCESS;
149 } else if (SUCCESS != http_request_datashare_detach(obj->share, request)) {
150 return FAILURE;
151 }
152 }
153
154 HTTP_CHECK_CURL_INIT(obj->request->ch, http_curl_init_ex(obj->request->ch, obj->request), return FAILURE);
155 if (CURLE_OK != (rc = curl_easy_setopt(obj->request->ch, CURLOPT_SHARE, share->ch))) {
156 http_error_ex(HE_WARNING, HTTP_E_REQUEST, "Could not attach HttpRequest object(#%d) to the HttpRequestDataShare: %s", Z_OBJ_HANDLE_P(request), curl_easy_strerror(rc));
157 return FAILURE;
158 }
159
160 obj->share = share;
161 ZVAL_ADDREF(request);
162 zend_llist_add_element(HTTP_RSHARE_HANDLES(share), (void *) &request);
163
164 return SUCCESS;
165 }
166
167 PHP_HTTP_API STATUS _http_request_datashare_detach(http_request_datashare *share, zval *request TSRMLS_DC)
168 {
169 CURLcode rc;
170 getObjectEx(http_request_object, obj, request);
171
172 if (!obj->share) {
173 http_error_ex(HE_WARNING, HTTP_E_REQUEST, "HttpRequest object(#%d) is not attached to any HttpRequestDataShare", Z_OBJ_HANDLE_P(request));
174 } else if (obj->share != share) {
175 http_error_ex(HE_WARNING, HTTP_E_REQUEST, "HttpRequest object(#%d) is not attached to this HttpRequestDataShare", Z_OBJ_HANDLE_P(request));
176 } else if (CURLE_OK != (rc = curl_easy_setopt(obj->request->ch, CURLOPT_SHARE, NULL))) {
177 http_error_ex(HE_WARNING, HTTP_E_REQUEST, "Could not detach HttpRequest object(#%d) from the HttpRequestDataShare: %s", Z_OBJ_HANDLE_P(request), curl_share_strerror(rc));
178 } else {
179 obj->share = NULL;
180 zend_llist_del_element(HTTP_RSHARE_HANDLES(share), request, http_request_datashare_compare_handles);
181 return SUCCESS;
182 }
183 return FAILURE;
184 }
185
186 PHP_HTTP_API void _http_request_datashare_detach_all(http_request_datashare *share TSRMLS_DC)
187 {
188 zval **r;
189
190 while ((r = zend_llist_get_first(HTTP_RSHARE_HANDLES(share)))) {
191 http_request_datashare_detach(share, *r);
192 }
193 }
194
195 PHP_HTTP_API void _http_request_datashare_dtor(http_request_datashare *share TSRMLS_DC)
196 {
197 if (!share->persistent) {
198 zend_llist_destroy(share->handle.list);
199 efree(share->handle.list);
200 }
201 HTTP_CURL_SHARE_DTOR(&share->ch);
202 #ifdef ZTS
203 if (share->persistent) {
204 HTTP_CURL_SLOCK_DTOR(&share->handle.locks);
205 }
206 #endif
207 }
208
209 PHP_HTTP_API void _http_request_datashare_free(http_request_datashare **share TSRMLS_DC)
210 {
211 http_request_datashare_dtor(*share);
212 pefree(*share, (*share)->persistent);
213 *share = NULL;
214 }
215
216 PHP_HTTP_API STATUS _http_request_datashare_set(http_request_datashare *share, const char *option, size_t option_len, zend_bool enable TSRMLS_DC)
217 {
218 curl_lock_data *opt;
219 CURLSHcode rc;
220
221 if (SUCCESS == zend_hash_find(&http_request_datashare_options, (char *) option, option_len + 1, (void *) &opt)) {
222 if (CURLSHE_OK == (rc = curl_share_setopt(share->ch, enable ? CURLSHOPT_SHARE : CURLSHOPT_UNSHARE, *opt))) {
223 return SUCCESS;
224 }
225 http_error_ex(HE_WARNING, HTTP_E_REQUEST, "Could not %s sharing of %s data: %s", enable ? "enable" : "disable", option, curl_share_strerror(rc));
226 }
227 return FAILURE;
228 }
229
230 static int http_request_datashare_compare_handles(void *h1, void *h2)
231 {
232 return (Z_OBJ_HANDLE_PP((zval **) h1) == Z_OBJ_HANDLE_P((zval *) h2));
233 }
234
235 static void http_request_datashare_destroy_handles(void *el)
236 {
237 zval **r = (zval **) el;
238 TSRMLS_FETCH();
239
240 { /* gcc 2.95 needs these braces */
241 getObjectEx(http_request_object, obj, *r);
242
243 curl_easy_setopt(obj->request->ch, CURLOPT_SHARE, NULL);
244 zval_ptr_dtor(r);
245 }
246 }
247
248 #ifdef ZTS
249 static void *http_request_datashare_locks_init(void)
250 {
251 int i;
252 http_request_datashare_lock *locks = pecalloc(CURL_LOCK_DATA_LAST, sizeof(http_request_datashare_lock), 1);
253
254 if (locks) {
255 for (i = 0; i < CURL_LOCK_DATA_LAST; ++i) {
256 locks[i].mx = tsrm_mutex_alloc();
257 }
258 }
259
260 return locks;
261 }
262
263 static void http_request_datashare_locks_dtor(void *l)
264 {
265 int i;
266 http_request_datashare_lock *locks = (http_request_datashare_lock *) l;
267
268 for (i = 0; i < CURL_LOCK_DATA_LAST; ++i) {
269 tsrm_mutex_free(locks[i].mx);
270 }
271 pefree(locks, 1);
272 }
273
274 static void http_request_datashare_lock_func(CURL *handle, curl_lock_data data, curl_lock_access locktype, void *userptr)
275 {
276 http_request_datashare *share = (http_request_datashare *) userptr;
277
278 /* TSRM can't distinguish shared/exclusive locks */
279 tsrm_mutex_lock(share->handle.locks[data].mx);
280 share->handle.locks[data].ch = handle;
281 }
282
283 static void http_request_datashare_unlock_func(CURL *handle, curl_lock_data data, void *userptr)
284 {
285 http_request_datashare *share = (http_request_datashare *) userptr;
286
287 if (share->handle.locks[data].ch == handle) {
288 tsrm_mutex_unlock(share->handle.locks[data].mx);
289 }
290 }
291 #endif
292
293 #endif /* ZEND_ENGINE_2 && HTTP_HAVE_CURL */
294
295
296 /*
297 * Local variables:
298 * tab-width: 4
299 * c-basic-offset: 4
300 * End:
301 * vim600: noet sw=4 ts=4 fdm=marker
302 * vim<600: noet sw=4 ts=4
303 */
304