* Added generic http_negotiate() function (FR#17338)
[m6w6/ext-http] / http_persistent_handle_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-2010, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 /* $Id$ */
14
15 #include "php_http.h"
16 #include "php_http_api.h"
17
18 #include "php_http_persistent_handle_api.h"
19
20 #ifndef HTTP_DEBUG_PHANDLES
21 # define HTTP_DEBUG_PHANDLES 0
22 #endif
23 #if HTTP_DEBUG_PHANDLES
24 # undef inline
25 # define inline
26 #endif
27
28 static HashTable http_persistent_handles_hash;
29 #ifdef ZTS
30 # define LOCK() tsrm_mutex_lock(http_persistent_handles_lock)
31 # define UNLOCK() tsrm_mutex_unlock(http_persistent_handles_lock)
32 static MUTEX_T http_persistent_handles_lock;
33 #else
34 # define LOCK()
35 # define UNLOCK()
36 #endif
37
38 typedef struct _http_persistent_handle_list_t {
39 HashTable free;
40 ulong used;
41 } http_persistent_handle_list;
42
43 typedef struct _http_persistent_handle_provider_t {
44 http_persistent_handle_list list; /* "ident" => array(handles) entries */
45 http_persistent_handle_ctor ctor;
46 http_persistent_handle_dtor dtor;
47 http_persistent_handle_copy copy;
48 } http_persistent_handle_provider;
49
50 static inline http_persistent_handle_list *http_persistent_handle_list_init(http_persistent_handle_list *list)
51 {
52 int free_list;
53
54 if ((free_list = !list)) {
55 list = pemalloc(sizeof(http_persistent_handle_list), 1);
56 }
57
58 list->used = 0;
59
60 if (SUCCESS != zend_hash_init(&list->free, 0, NULL, NULL, 1)) {
61 if (free_list) {
62 pefree(list, 1);
63 }
64 list = NULL;
65 }
66
67 return list;
68 }
69
70 static inline void http_persistent_handle_list_dtor(http_persistent_handle_list *list, http_persistent_handle_dtor dtor)
71 {
72 HashPosition pos;
73 void **handle;
74
75 #if HTTP_DEBUG_PHANDLES
76 fprintf(stderr, "LSTDTOR: %p\n", list);
77 #endif
78 FOREACH_HASH_VAL(pos, &list->free, handle) {
79 #if HTTP_DEBUG_PHANDLES
80 fprintf(stderr, "DESTROY: %p\n", *handle);
81 #endif
82
83 dtor(*handle);
84 }
85 zend_hash_destroy(&list->free);
86 }
87
88 static inline void http_persistent_handle_list_free(http_persistent_handle_list **list, http_persistent_handle_dtor dtor)
89 {
90 http_persistent_handle_list_dtor(*list, dtor);
91 #if HTTP_DEBUG_PHANDLES
92 fprintf(stderr, "LSTFREE: %p\n", *list);
93 #endif
94 pefree(*list, 1);
95 *list = NULL;
96 }
97
98 static inline http_persistent_handle_list *http_persistent_handle_list_find(http_persistent_handle_provider *provider TSRMLS_DC)
99 {
100 http_persistent_handle_list **list, *new_list;
101
102 if (SUCCESS == zend_hash_quick_find(&provider->list.free, HTTP_G->persistent.handles.ident.s, HTTP_G->persistent.handles.ident.l, HTTP_G->persistent.handles.ident.h, (void *) &list)) {
103 #if HTTP_DEBUG_PHANDLES
104 fprintf(stderr, "LSTFIND: %p\n", *list);
105 #endif
106 return *list;
107 }
108
109 if ((new_list = http_persistent_handle_list_init(NULL))) {
110 if (SUCCESS == zend_hash_quick_add(&provider->list.free, HTTP_G->persistent.handles.ident.s, HTTP_G->persistent.handles.ident.l, HTTP_G->persistent.handles.ident.h, (void *) &new_list, sizeof(http_persistent_handle_list *), (void *) &list)) {
111 #if HTTP_DEBUG_PHANDLES
112 fprintf(stderr, "LSTFIND: %p (new)\n", *list);
113 #endif
114 return *list;
115 }
116 http_persistent_handle_list_free(&new_list, provider->dtor);
117 }
118
119 return NULL;
120 }
121
122 static inline STATUS http_persistent_handle_do_acquire(http_persistent_handle_provider *provider, void **handle TSRMLS_DC)
123 {
124 ulong index;
125 void **handle_ptr;
126 http_persistent_handle_list *list;
127
128 if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) {
129 zend_hash_internal_pointer_end(&list->free);
130 if (HASH_KEY_NON_EXISTANT != zend_hash_get_current_key(&list->free, NULL, &index, 0) && SUCCESS == zend_hash_get_current_data(&list->free, (void *) &handle_ptr)) {
131 *handle = *handle_ptr;
132 zend_hash_index_del(&list->free, index);
133 } else {
134 *handle = provider->ctor();
135 }
136
137 if (*handle) {
138 ++provider->list.used;
139 ++list->used;
140 return SUCCESS;
141 }
142 } else {
143 *handle = NULL;
144 }
145
146 return FAILURE;
147 }
148
149 static inline STATUS http_persistent_handle_do_release(http_persistent_handle_provider *provider, void **handle TSRMLS_DC)
150 {
151 http_persistent_handle_list *list;
152
153 if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) {
154 if (provider->list.used >= HTTP_G->persistent.handles.limit) {
155 provider->dtor(*handle);
156 } else {
157 if (SUCCESS != zend_hash_next_index_insert(&list->free, (void *) handle, sizeof(void *), NULL)) {
158 return FAILURE;
159 }
160 }
161
162 *handle = NULL;
163 --provider->list.used;
164 --list->used;
165 return SUCCESS;
166 }
167
168 return FAILURE;
169 }
170
171 static inline STATUS http_persistent_handle_do_accrete(http_persistent_handle_provider *provider, void *old_handle, void **new_handle TSRMLS_DC)
172 {
173 http_persistent_handle_list *list;
174
175 if (provider->copy && (*new_handle = provider->copy(old_handle))) {
176 if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) {
177 ++list->used;
178 }
179 ++provider->list.used;
180 return SUCCESS;
181 }
182 return FAILURE;
183 }
184
185 static void http_persistent_handles_hash_dtor(void *p)
186 {
187 http_persistent_handle_provider *provider = (http_persistent_handle_provider *) p;
188 http_persistent_handle_list **list, *list_tmp;
189 HashPosition pos;
190
191 FOREACH_HASH_VAL(pos, &provider->list.free, list) {
192 /* fix shutdown crash in PHP4 */
193 list_tmp = *list;
194 http_persistent_handle_list_free(&list_tmp, provider->dtor);
195 }
196
197 zend_hash_destroy(&provider->list.free);
198 }
199
200 PHP_MINIT_FUNCTION(http_persistent_handle)
201 {
202 zend_hash_init(&http_persistent_handles_hash, 0, NULL, http_persistent_handles_hash_dtor, 1);
203 #ifdef ZTS
204 http_persistent_handles_lock = tsrm_mutex_alloc();
205 #endif
206 return SUCCESS;
207 }
208
209 PHP_MSHUTDOWN_FUNCTION(http_persistent_handle)
210 {
211 zend_hash_destroy(&http_persistent_handles_hash);
212 #ifdef ZTS
213 tsrm_mutex_free(http_persistent_handles_lock);
214 #endif
215 return SUCCESS;
216 }
217
218 PHP_HTTP_API STATUS _http_persistent_handle_provide_ex(const char *name_str, size_t name_len, http_persistent_handle_ctor ctor, http_persistent_handle_dtor dtor, http_persistent_handle_copy copy)
219 {
220 STATUS status = FAILURE;
221 http_persistent_handle_provider provider;
222
223 LOCK();
224 if (http_persistent_handle_list_init(&provider.list)) {
225 provider.ctor = ctor;
226 provider.dtor = dtor;
227 provider.copy = copy;
228
229 #if HTTP_DEBUG_PHANDLES
230 fprintf(stderr, "PROVIDE: %s\n", name_str);
231 #endif
232
233 if (SUCCESS == zend_hash_add(&http_persistent_handles_hash, HTTP_ZAPI_CONST_CAST(char *) name_str, name_len+1, (void *) &provider, sizeof(http_persistent_handle_provider), NULL)) {
234 status = SUCCESS;
235 }
236 }
237 UNLOCK();
238
239 return status;
240 }
241
242 PHP_HTTP_API STATUS _http_persistent_handle_acquire_ex(const char *name_str, size_t name_len, void **handle TSRMLS_DC)
243 {
244 STATUS status = FAILURE;
245 http_persistent_handle_provider *provider;
246
247 *handle = NULL;
248 LOCK();
249 if (SUCCESS == zend_hash_find(&http_persistent_handles_hash, HTTP_ZAPI_CONST_CAST(char *) name_str, name_len+1, (void *) &provider)) {
250 status = http_persistent_handle_do_acquire(provider, handle TSRMLS_CC);
251 }
252 UNLOCK();
253
254 #if HTTP_DEBUG_PHANDLES
255 fprintf(stderr, "ACQUIRE: %p (%s)\n", *handle, name_str);
256 #endif
257
258 return status;
259 }
260
261 PHP_HTTP_API STATUS _http_persistent_handle_release_ex(const char *name_str, size_t name_len, void **handle TSRMLS_DC)
262 {
263 STATUS status = FAILURE;
264 http_persistent_handle_provider *provider;
265 #if HTTP_DEBUG_PHANDLES
266 void *handle_tmp = *handle;
267 #endif
268
269 LOCK();
270 if (SUCCESS == zend_hash_find(&http_persistent_handles_hash, HTTP_ZAPI_CONST_CAST(char *) name_str, name_len+1, (void *) &provider)) {
271 status = http_persistent_handle_do_release(provider, handle TSRMLS_CC);
272 }
273 UNLOCK();
274
275 #if HTTP_DEBUG_PHANDLES
276 fprintf(stderr, "RELEASE: %p (%s)\n", handle_tmp, name_str);
277 #endif
278
279 return status;
280 }
281
282 PHP_HTTP_API STATUS _http_persistent_handle_accrete_ex(const char *name_str, size_t name_len, void *old_handle, void **new_handle TSRMLS_DC)
283 {
284 STATUS status = FAILURE;
285 http_persistent_handle_provider *provider;
286
287 *new_handle = NULL;
288 LOCK();
289 if (SUCCESS == zend_hash_find(&http_persistent_handles_hash, HTTP_ZAPI_CONST_CAST(char *) name_str, name_len+1, (void *) &provider)) {
290 status = http_persistent_handle_do_accrete(provider, old_handle, new_handle TSRMLS_CC);
291 }
292 UNLOCK();
293
294 #if HTTP_DEBUG_PHANDLES
295 fprintf(stderr, "ACCRETE: %p > %p (%s)\n", old_handle, *new_handle, name_str);
296 #endif
297
298 return status;
299 }
300
301 PHP_HTTP_API void _http_persistent_handle_cleanup_ex(const char *name_str, size_t name_len, int current_ident_only TSRMLS_DC)
302 {
303 http_persistent_handle_provider *provider;
304 http_persistent_handle_list *list, **listp;
305 HashPosition pos1, pos2;
306
307 LOCK();
308 if (name_str && name_len) {
309 if (SUCCESS == zend_hash_find(&http_persistent_handles_hash, HTTP_ZAPI_CONST_CAST(char *) name_str, name_len+1, (void *) &provider)) {
310 if (current_ident_only) {
311 if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) {
312 http_persistent_handle_list_dtor(list, provider->dtor);
313 http_persistent_handle_list_init(list);
314 }
315 } else {
316 FOREACH_HASH_VAL(pos1, &provider->list.free, listp) {
317 http_persistent_handle_list_dtor(*listp, provider->dtor);
318 http_persistent_handle_list_init(*listp);
319 }
320 }
321 }
322 } else {
323 FOREACH_HASH_VAL(pos1, &http_persistent_handles_hash, provider) {
324 if (current_ident_only) {
325 if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) {
326 http_persistent_handle_list_dtor(list, provider->dtor);
327 http_persistent_handle_list_init(list);
328 }
329 } else {
330 FOREACH_HASH_VAL(pos2, &provider->list.free, listp) {
331 http_persistent_handle_list_dtor(*listp, provider->dtor);
332 http_persistent_handle_list_init(*listp);
333 }
334 }
335 }
336 }
337 UNLOCK();
338 }
339
340 PHP_HTTP_API HashTable *_http_persistent_handle_statall_ex(HashTable *ht TSRMLS_DC)
341 {
342 zval *zentry[2];
343 HashPosition pos1, pos2;
344 HashKey key1 = initHashKey(0), key2 = initHashKey(0);
345 http_persistent_handle_provider *provider;
346 http_persistent_handle_list **list;
347
348 LOCK();
349 if (zend_hash_num_elements(&http_persistent_handles_hash)) {
350 if (!ht) {
351 ALLOC_HASHTABLE(ht);
352 zend_hash_init(ht, 0, NULL, ZVAL_PTR_DTOR, 0);
353 }
354
355 FOREACH_HASH_KEYVAL(pos1, &http_persistent_handles_hash, key1, provider) {
356 MAKE_STD_ZVAL(zentry[0]);
357 array_init(zentry[0]);
358
359 FOREACH_HASH_KEYVAL(pos2, &provider->list.free, key2, list) {
360 MAKE_STD_ZVAL(zentry[1]);
361 array_init(zentry[1]);
362 add_assoc_long_ex(zentry[1], ZEND_STRS("used"), (*list)->used);
363 add_assoc_long_ex(zentry[1], ZEND_STRS("free"), zend_hash_num_elements(&(*list)->free));
364
365 /* use zend_hash_* not add_assoc_* (which is zend_symtable_*) as we want a string even for numbers */
366 zend_hash_add(Z_ARRVAL_P(zentry[0]), key2.str, key2.len, &zentry[1], sizeof(zval *), NULL);
367 }
368
369 zend_hash_add(ht, key1.str, key1.len, &zentry[0], sizeof(zval *), NULL);
370 }
371 } else if (ht) {
372 ht = NULL;
373 }
374 UNLOCK();
375
376 return ht;
377 }
378
379
380 /*
381 * Local variables:
382 * tab-width: 4
383 * c-basic-offset: 4
384 * End:
385 * vim600: noet sw=4 ts=4 fdm=marker
386 * vim<600: noet sw=4 ts=4
387 */
388