fix problem with dechunk/inflate stream usage
[m6w6/ext-http] / php_http_header_parser.c
1
2 #include "php_http.h"
3
4 typedef struct php_http_header_parser_state_spec {
5 php_http_header_parser_state_t state;
6 unsigned need_data:1;
7 } php_http_header_parser_state_spec_t;
8
9 static const php_http_header_parser_state_spec_t php_http_header_parser_states[] = {
10 {PHP_HTTP_HEADER_PARSER_STATE_START, 1},
11 {PHP_HTTP_HEADER_PARSER_STATE_KEY, 1},
12 {PHP_HTTP_HEADER_PARSER_STATE_VALUE, 1},
13 {PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE, 0},
14 {PHP_HTTP_HEADER_PARSER_STATE_DONE, 0}
15 };
16
17
18 PHP_HTTP_API php_http_header_parser_t *php_http_header_parser_init(php_http_header_parser_t *parser TSRMLS_CC)
19 {
20 if (!parser) {
21 parser = emalloc(sizeof(*parser));
22 }
23 memset(parser, 0, sizeof(*parser));
24
25 TSRMLS_SET_CTX(parser->ts);
26
27 return parser;
28 }
29
30
31 PHP_HTTP_API php_http_header_parser_state_t php_http_header_parser_state_push(php_http_header_parser_t *parser, unsigned argc, ...)
32 {
33 va_list va_args;
34 unsigned i;
35 php_http_header_parser_state_t state = 0;
36
37 va_start(va_args, argc);
38 for (i = 0; i < argc; ++i) {
39 state = va_arg(va_args, php_http_header_parser_state_t);
40 zend_stack_push(&parser->stack, &state, sizeof(state));
41 }
42 va_end(va_args);
43
44 return state;
45 }
46
47 PHP_HTTP_API php_http_header_parser_state_t php_http_header_parser_state_is(php_http_header_parser_t *parser)
48 {
49 php_http_header_parser_state_t *state;
50
51 if (SUCCESS == zend_stack_top(&parser->stack, (void *) &state)) {
52 return *state;
53 }
54 return PHP_HTTP_HEADER_PARSER_STATE_START;
55 }
56
57 PHP_HTTP_API php_http_header_parser_state_t php_http_header_parser_state_pop(php_http_header_parser_t *parser)
58 {
59 php_http_header_parser_state_t state, *state_ptr;
60 if (SUCCESS == zend_stack_top(&parser->stack, (void *) &state_ptr)) {
61 state = *state_ptr;
62 zend_stack_del_top(&parser->stack);
63 return state;
64 }
65 return PHP_HTTP_HEADER_PARSER_STATE_START;
66 }
67
68 PHP_HTTP_API void php_http_header_parser_dtor(php_http_header_parser_t *parser)
69 {
70 zend_stack_destroy(&parser->stack);
71 php_http_info_dtor(&parser->info);
72 STR_FREE(parser->_key.str);
73 STR_FREE(parser->_val.str);
74 }
75
76 PHP_HTTP_API void php_http_header_parser_free(php_http_header_parser_t **parser)
77 {
78 if (*parser) {
79 php_http_header_parser_dtor(*parser);
80 efree(*parser);
81 *parser = NULL;
82 }
83 }
84
85 PHP_HTTP_API STATUS php_http_header_parser_parse(php_http_header_parser_t *parser, php_http_buffer *buffer, unsigned flags, HashTable *headers, php_http_info_callback_t callback_func, void *callback_arg)
86 {
87 TSRMLS_FETCH_FROM_CTX(parser->ts);
88
89 while (buffer->used || !php_http_header_parser_states[php_http_header_parser_state_is(parser)].need_data) {
90 #if 0
91 const char *state[] = {"START", "KEY", "VALUE", "HEADER_DONE", "DONE"};
92 fprintf(stderr, "#HP: %s (%d) %zu\n",
93 state[php_http_header_parser_state_is(parser)], zend_hash_num_elements(headers), buffer->used);
94 #endif
95 switch (php_http_header_parser_state_pop(parser)) {
96 case PHP_HTTP_HEADER_PARSER_STATE_FAILURE:
97 return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE);
98
99 case PHP_HTTP_HEADER_PARSER_STATE_START: {
100 char *ptr = buffer->data;
101
102 while (ptr - buffer->data < buffer->used && PHP_HTTP_IS_CTYPE(space, *ptr)) {
103 ++ptr;
104 }
105
106 php_http_buffer_cut(buffer, 0, ptr - buffer->data);
107 php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_KEY);
108 break;
109 }
110
111 case PHP_HTTP_HEADER_PARSER_STATE_KEY: {
112 const char *colon, *eol_str;
113 int eol_len;
114
115 if (buffer->data == (eol_str = php_http_locate_bin_eol(buffer->data, buffer->used, &eol_len))) {
116 /* end of headers */
117 php_http_buffer_cut(buffer, 0, eol_len);
118 php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_DONE);
119 } else if (php_http_info_parse(&parser->info, php_http_buffer_fix(buffer)->data TSRMLS_CC)) {
120 /* new message starting with request/response line */
121 if (callback_func) {
122 callback_func(callback_arg, &headers, &parser->info TSRMLS_CC);
123 }
124 php_http_info_dtor(&parser->info);
125 php_http_buffer_cut(buffer, 0, eol_str + eol_len - buffer->data);
126 php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE);
127 } else if ((colon = memchr(buffer->data, ':', buffer->used)) && (!eol_str || eol_str > colon)) {
128 /* header: string */
129 parser->_key.str = estrndup(buffer->data, parser->_key.len = colon - buffer->data);
130 while (PHP_HTTP_IS_CTYPE(space, *++colon));
131 php_http_buffer_cut(buffer, 0, colon - buffer->data);
132 php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE);
133 } else {
134 /* neither reqeust/response line nor header: string */
135 return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE);
136 }
137 break;
138 /*
139 if (colon && (!eol_str || colon < eol_str)) {
140 parser->_key.str = estrndup(buffer->data, parser->_key.len = colon - buffer->data);
141 while (PHP_HTTP_IS_CTYPE(space, *++colon));
142 php_http_buffer_cut(buffer, 0, colon - buffer->data);
143 php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE);
144 } else if (eol_str) {
145 if (eol_str == buffer->data) {
146 php_http_buffer_cut(buffer, 0, eol_len);
147 php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_DONE);
148 } else if (php_http_info_parse(&parser->info, php_http_buffer_fix(buffer)->data TSRMLS_CC)) {
149 if (callback_func) {
150 callback_func(callback_arg, &headers, &parser->info TSRMLS_CC);
151 }
152 php_http_info_dtor(&parser->info);
153 php_http_buffer_cut(buffer, 0, eol_str + eol_len - buffer->data);
154 php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE);
155 } else {
156 return PHP_HTTP_HEADER_PARSER_STATE_FAILURE;
157 }
158 } else {
159 php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_KEY);
160 return PHP_HTTP_HEADER_PARSER_STATE_KEY;
161 }
162 break;
163 */
164 }
165
166 case PHP_HTTP_HEADER_PARSER_STATE_VALUE: {
167 const char *eol_str;
168 int eol_len;
169
170 do {
171 if ((eol_str = php_http_locate_bin_eol(buffer->data, buffer->used, &eol_len))) {
172 if (eol_str + eol_len - buffer->data < buffer->used) {
173 char nextline = *(eol_str + eol_len);
174
175 if (nextline == '\t' || nextline == ' ') {
176 php_http_buffer_cut(buffer, eol_str - buffer->data, eol_len);
177 continue;
178 }
179 }
180
181 parser->_val.str = estrndup(buffer->data, parser->_val.len = eol_str - buffer->data);
182 php_http_buffer_cut(buffer, 0, eol_str + eol_len - buffer->data);
183 php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE);
184 } else if (flags & PHP_HTTP_HEADER_PARSER_CLEANUP) {
185 if (buffer->used) {
186 parser->_val.str = estrndup(buffer->data, parser->_val.len = buffer->used);
187 php_http_buffer_reset(buffer);
188 }
189 php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE);
190 } else {
191 return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE);
192 }
193 } while (0);
194
195 break;
196 }
197
198 case PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE:
199 if (parser->_key.str && parser->_val.str) {
200 zval array, **exist;
201
202 INIT_PZVAL_ARRAY(&array, headers);
203 php_http_pretty_key(parser->_key.str, parser->_key.len, 1, 1);
204 if (SUCCESS == zend_hash_find(headers, parser->_key.str, parser->_key.len + 1, (void *) &exist)) {
205 convert_to_array(*exist);
206 add_next_index_stringl(&array, parser->_val.str, parser->_val.len, 0);
207 } else {
208 add_assoc_stringl_ex(&array, parser->_key.str, parser->_key.len + 1, parser->_val.str, parser->_val.len, 0);
209 }
210 parser->_val.str = NULL;
211 }
212
213 STR_SET(parser->_key.str, NULL);
214 STR_SET(parser->_val.str, NULL);
215
216 php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_KEY);
217 break;
218
219 case PHP_HTTP_HEADER_PARSER_STATE_DONE:
220 return PHP_HTTP_HEADER_PARSER_STATE_DONE;
221 }
222 }
223
224 return php_http_header_parser_state_is(parser);
225 }