add CURLINFO_RETRY_AFTER
[m6w6/ext-http] / src / php_http_negotiate.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 #ifndef PHP_HTTP_DEBUG_NEG
16 # define PHP_HTTP_DEBUG_NEG 0
17 #endif
18
19 static int php_http_negotiate_sort(Bucket *b1, Bucket *b2)
20 {
21 int result = numeric_compare_function(&b1->val, &b2->val);
22
23 return (result > 0 ? -1 : (result < 0 ? 1 : 0));
24 }
25
26 #define M_PRI 5
27 #define M_SEC 2
28 #define M_ANY -1
29 #define M_NOT 0
30 #define M_ALL ~0
31 static inline unsigned php_http_negotiate_match(const char *param_str, size_t param_len, const char *supported_str, size_t supported_len, const char *sep_str, size_t sep_len)
32 {
33 unsigned match = M_NOT;
34
35 if (param_len == supported_len && !strncasecmp(param_str, supported_str, param_len)) {
36 /* that was easy */
37 match = M_ALL;
38 } else if (sep_str && sep_len) {
39 const char *param_sec = php_http_locate_str(param_str, param_len, sep_str, sep_len);
40 size_t param_pri_len = param_sec ? param_sec - param_str : param_len;
41 const char *supported_sec = php_http_locate_str(supported_str, supported_len, sep_str, sep_len);
42 size_t supported_pri_len = supported_sec ? supported_sec - supported_str : supported_len;
43 size_t cmp_len = MIN(param_pri_len, supported_pri_len);
44
45 if (((*param_str == '*') || (*supported_str == '*'))
46 || ((param_pri_len == supported_pri_len) && !strncasecmp(param_str, supported_str, param_pri_len))
47 || ((!param_sec || !supported_sec) && cmp_len && !strncasecmp(param_str, supported_str, cmp_len))
48 ) {
49 match += M_PRI;
50 }
51
52 if (param_sec && supported_sec
53 && ((*(param_sec + sep_len) == '*' || *(supported_sec + sep_len) == '*')
54 || !strcasecmp(param_sec, supported_sec)
55 )
56 ) {
57 match += M_SEC;
58 }
59
60 if ((param_sec && *(param_sec + sep_len) == '*')
61 || (supported_sec && *(supported_sec + sep_len) == '*')
62 || ((*param_str == '*') || (*supported_str == '*'))
63 ) {
64 match += M_ANY;
65 }
66 }
67 #if PHP_HTTP_DEBUG_NEG
68 fprintf(stderr, "match: %s == %s => %u\n", supported_str, param_str, match);
69 #endif
70 return match;
71 }
72 static int php_http_negotiate_reduce(zval *p, int num_args, va_list args, zend_hash_key *hash_key)
73 {
74 unsigned best_match = 0;
75 double q = 0;
76 php_http_arrkey_t key;
77 zval *value;
78 zend_string *supported = zval_get_string(p);
79 HashTable *params = va_arg(args, HashTable *);
80 HashTable *result = va_arg(args, HashTable *);
81 const char *sep_str = va_arg(args, const char *);
82 size_t sep_len = va_arg(args, size_t);
83
84 ZEND_HASH_FOREACH_KEY_VAL(params, key.h, key.key, value)
85 {
86 unsigned match;
87
88 #if PHP_HTTP_DEBUG_NEG
89 fprintf(stderr, "match(%u) > best_match(%u) = %u (q=%f)\n", match, best_match, match>best_match, Z_DVAL_PP(val));
90 #endif
91 php_http_arrkey_stringify(&key, NULL);
92 match = php_http_negotiate_match(key.key->val, key.key->len, supported->val, supported->len, sep_str, sep_len);
93
94 if (match > best_match) {
95 best_match = match;
96 q = Z_DVAL_P(value) - 0.1 / match;
97 }
98 php_http_arrkey_dtor(&key);
99 }
100 ZEND_HASH_FOREACH_END();
101
102 if (q > 0) {
103 zval tmp;
104
105 ZVAL_DOUBLE(&tmp, q);
106 zend_hash_update(result, supported, &tmp);
107 }
108
109 zend_string_release(supported);
110 return ZEND_HASH_APPLY_KEEP;
111 }
112
113 HashTable *php_http_negotiate(const char *value_str, size_t value_len, HashTable *supported, const char *primary_sep_str, size_t primary_sep_len)
114 {
115 HashTable *result = NULL;
116
117 if (value_str && value_len) {
118 unsigned i = 0;
119 zval arr, *val, *arg, *zq;
120 HashTable params;
121 php_http_arrkey_t key;
122 php_http_params_opts_t opts;
123
124 zend_hash_init(&params, 10, NULL, ZVAL_PTR_DTOR, 0);
125 php_http_params_opts_default_get(&opts);
126 opts.input.str = estrndup(value_str, value_len);
127 opts.input.len = value_len;
128 opts.flags &= ~PHP_HTTP_PARAMS_RFC5987;
129 php_http_params_parse(&params, &opts);
130 efree(opts.input.str);
131
132 array_init(&arr);
133
134 ZEND_HASH_FOREACH_KEY_VAL(&params, key.h, key.key, val)
135 {
136 double q;
137
138 if ((arg = zend_hash_str_find(Z_ARRVAL_P(val), ZEND_STRL("arguments")))
139 && (IS_ARRAY == Z_TYPE_P(arg))
140 && (zq = zend_hash_str_find(Z_ARRVAL_P(arg), ZEND_STRL("q")))) {
141 q = zval_get_double(zq);
142 } else {
143 q = 1.0 - (((double) ++i) / 100.0);
144 }
145
146 #if 0
147 fprintf(stderr, "Q: %s=%1.3f\n", key.key->val, q);
148 #endif
149
150 if (key.key) {
151 add_assoc_double_ex(&arr, key.key->val, key.key->len, q);
152 } else {
153 add_index_double(&arr, key.h, q);
154 }
155
156 }
157 ZEND_HASH_FOREACH_END();
158
159 #if PHP_HTTP_DEBUG_NEG
160 zend_print_zval_r(&arr, 1);
161 #endif
162
163 ALLOC_HASHTABLE(result);
164 zend_hash_init(result, zend_hash_num_elements(supported), NULL, ZVAL_PTR_DTOR, 0);
165 zend_hash_apply_with_arguments(supported, php_http_negotiate_reduce, 4, Z_ARRVAL(arr), result, primary_sep_str, primary_sep_len);
166 zend_hash_destroy(&params);
167 zval_dtor(&arr);
168 zend_hash_sort(result, php_http_negotiate_sort, 0);
169 }
170
171 return result;
172 }
173
174
175
176 /*
177 * Local variables:
178 * tab-width: 4
179 * c-basic-offset: 4
180 * End:
181 * vim600: noet sw=4 ts=4 fdm=marker
182 * vim<600: noet sw=4 ts=4
183 */
184
185