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