Line data Source code
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 : typedef struct php_http_header_parser_state_spec {
16 : php_http_header_parser_state_t state;
17 : unsigned need_data:1;
18 : } php_http_header_parser_state_spec_t;
19 :
20 : static const php_http_header_parser_state_spec_t php_http_header_parser_states[] = {
21 : {PHP_HTTP_HEADER_PARSER_STATE_START, 1},
22 : {PHP_HTTP_HEADER_PARSER_STATE_KEY, 1},
23 : {PHP_HTTP_HEADER_PARSER_STATE_VALUE, 1},
24 : {PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX, 1},
25 : {PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE, 0},
26 : {PHP_HTTP_HEADER_PARSER_STATE_DONE, 0}
27 : };
28 :
29 170 : php_http_header_parser_t *php_http_header_parser_init(php_http_header_parser_t *parser TSRMLS_DC)
30 : {
31 170 : if (!parser) {
32 0 : parser = emalloc(sizeof(*parser));
33 : }
34 170 : memset(parser, 0, sizeof(*parser));
35 :
36 : TSRMLS_SET_CTX(parser->ts);
37 :
38 170 : return parser;
39 : }
40 :
41 2240 : php_http_header_parser_state_t php_http_header_parser_state_push(php_http_header_parser_t *parser, unsigned argc, ...)
42 : {
43 : va_list va_args;
44 : unsigned i;
45 2240 : php_http_header_parser_state_t state = 0;
46 :
47 : /* short circuit */
48 2240 : ZEND_PTR_STACK_RESIZE_IF_NEEDED((&parser->stack), argc);
49 :
50 2240 : va_start(va_args, argc);
51 4480 : for (i = 0; i < argc; ++i) {
52 2240 : state = va_arg(va_args, php_http_header_parser_state_t);
53 2240 : zend_ptr_stack_push(&parser->stack, (void *) state);
54 : }
55 2240 : va_end(va_args);
56 :
57 2240 : return state;
58 : }
59 :
60 344 : php_http_header_parser_state_t php_http_header_parser_state_is(php_http_header_parser_t *parser)
61 : {
62 344 : if (parser->stack.top) {
63 328 : return (php_http_header_parser_state_t) parser->stack.elements[parser->stack.top - 1];
64 : }
65 :
66 16 : return PHP_HTTP_HEADER_PARSER_STATE_START;
67 : }
68 :
69 2322 : php_http_header_parser_state_t php_http_header_parser_state_pop(php_http_header_parser_t *parser)
70 : {
71 2322 : if (parser->stack.top) {
72 2193 : return (php_http_header_parser_state_t) zend_ptr_stack_pop(&parser->stack);
73 : }
74 :
75 129 : return PHP_HTTP_HEADER_PARSER_STATE_START;
76 : }
77 :
78 170 : void php_http_header_parser_dtor(php_http_header_parser_t *parser)
79 : {
80 170 : zend_ptr_stack_destroy(&parser->stack);
81 170 : php_http_info_dtor(&parser->info);
82 170 : STR_FREE(parser->_key.str);
83 170 : STR_FREE(parser->_val.str);
84 170 : }
85 :
86 0 : void php_http_header_parser_free(php_http_header_parser_t **parser)
87 : {
88 0 : if (*parser) {
89 0 : php_http_header_parser_dtor(*parser);
90 0 : efree(*parser);
91 0 : *parser = NULL;
92 : }
93 0 : }
94 :
95 507 : STATUS php_http_header_parser_parse(php_http_header_parser_t *parser, php_http_buffer_t *buffer, unsigned flags, HashTable *headers, php_http_info_callback_t callback_func, void *callback_arg)
96 : {
97 : TSRMLS_FETCH_FROM_CTX(parser->ts);
98 :
99 2923 : while (buffer->used || !php_http_header_parser_states[php_http_header_parser_state_is(parser)].need_data) {
100 : #if 0
101 : const char *state[] = {"START", "KEY", "VALUE", "HEADER_DONE", "DONE"};
102 : fprintf(stderr, "#HP: %s (avail:%zu, num:%d)\n", php_http_header_parser_state_is(parser) < 0 ? "FAILURE" : state[php_http_header_parser_state_is(parser)], buffer->used, headers?zend_hash_num_elements(headers):0);
103 : _dpf(0, buffer->data, buffer->used);
104 : #endif
105 2322 : switch (php_http_header_parser_state_pop(parser)) {
106 : case PHP_HTTP_HEADER_PARSER_STATE_FAILURE:
107 0 : return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE);
108 :
109 : case PHP_HTTP_HEADER_PARSER_STATE_START: {
110 129 : char *ptr = buffer->data;
111 :
112 258 : while (ptr - buffer->data < buffer->used && PHP_HTTP_IS_CTYPE(space, *ptr)) {
113 0 : ++ptr;
114 : }
115 :
116 129 : php_http_buffer_cut(buffer, 0, ptr - buffer->data);
117 129 : php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_KEY);
118 129 : break;
119 : }
120 :
121 : case PHP_HTTP_HEADER_PARSER_STATE_KEY: {
122 692 : const char *colon, *eol_str = NULL;
123 692 : int eol_len = 0;
124 :
125 692 : if (buffer->data == (eol_str = php_http_locate_bin_eol(buffer->data, buffer->used, &eol_len))) {
126 : /* end of headers */
127 82 : php_http_buffer_cut(buffer, 0, eol_len);
128 82 : php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_DONE);
129 610 : } else if (php_http_info_parse(&parser->info, php_http_buffer_fix(buffer)->data TSRMLS_CC)) {
130 : /* new message starting with request/response line */
131 121 : if (callback_func) {
132 121 : callback_func(callback_arg, &headers, &parser->info TSRMLS_CC);
133 : }
134 121 : php_http_info_dtor(&parser->info);
135 121 : php_http_buffer_cut(buffer, 0, eol_str + eol_len - buffer->data);
136 121 : php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE);
137 489 : } else if ((colon = memchr(buffer->data, ':', buffer->used)) && (!eol_str || eol_str > colon)) {
138 : /* header: string */
139 484 : parser->_key.str = estrndup(buffer->data, parser->_key.len = colon - buffer->data);
140 484 : while (PHP_HTTP_IS_CTYPE(space, *++colon) && *colon != '\n' && *colon != '\r');
141 484 : php_http_buffer_cut(buffer, 0, colon - buffer->data);
142 484 : php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE);
143 : } else {
144 : /* neither reqeust/response line nor header: string */
145 5 : return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE);
146 : }
147 687 : break;
148 : }
149 :
150 : case PHP_HTTP_HEADER_PARSER_STATE_VALUE: {
151 : const char *eol_str;
152 : int eol_len;
153 :
154 : #define SET_ADD_VAL(slen, eol_len) \
155 : do { \
156 : const char *ptr = buffer->data; \
157 : size_t len = slen; \
158 : \
159 : while (len > 0 && PHP_HTTP_IS_CTYPE(space, *ptr)) { \
160 : ++ptr; \
161 : --len; \
162 : } \
163 : while (len > 0 && PHP_HTTP_IS_CTYPE(space, ptr[len - 1])) { \
164 : --len; \
165 : } \
166 : \
167 : if (len > 0) { \
168 : if (parser->_val.str) { \
169 : parser->_val.str = erealloc(parser->_val.str, parser->_val.len + len + 2); \
170 : parser->_val.str[parser->_val.len++] = ' '; \
171 : memcpy(&parser->_val.str[parser->_val.len], ptr, len); \
172 : parser->_val.len += len; \
173 : parser->_val.str[parser->_val.len] = '\0'; \
174 : } else { \
175 : parser->_val.len = len; \
176 : parser->_val.str = estrndup(ptr, len); \
177 : } \
178 : } \
179 : php_http_buffer_cut(buffer, 0, slen + eol_len); \
180 : } while (0)
181 :
182 489 : if ((eol_str = php_http_locate_bin_eol(buffer->data, buffer->used, &eol_len))) {
183 487 : SET_ADD_VAL(eol_str - buffer->data, eol_len);
184 :
185 487 : if (buffer->used) {
186 144 : if (*buffer->data != '\t' && *buffer->data != ' ') {
187 141 : php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE);
188 304 : break;
189 : } else {
190 3 : php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE);
191 3 : break;
192 : }
193 : }
194 : }
195 :
196 345 : if (flags & PHP_HTTP_HEADER_PARSER_CLEANUP) {
197 19 : if (buffer->used) {
198 2 : SET_ADD_VAL(buffer->used, 0);
199 : }
200 19 : php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE);
201 : } else {
202 326 : return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX);
203 : }
204 19 : break;
205 : }
206 :
207 : case PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX:
208 326 : if (*buffer->data == ' ' || *buffer->data == '\t') {
209 3 : php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE);
210 : } else {
211 323 : php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE);
212 : }
213 326 : break;
214 :
215 : case PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE:
216 604 : if (parser->_key.str && parser->_val.str) {
217 : zval array, **exist;
218 :
219 483 : if (!headers && callback_func) {
220 1 : callback_func(callback_arg, &headers, NULL TSRMLS_CC);
221 : }
222 :
223 483 : INIT_PZVAL_ARRAY(&array, headers);
224 483 : php_http_pretty_key(parser->_key.str, parser->_key.len, 1, 1);
225 483 : if (SUCCESS == zend_symtable_find(headers, parser->_key.str, parser->_key.len + 1, (void *) &exist)) {
226 8 : convert_to_array(*exist);
227 8 : add_next_index_stringl(*exist, parser->_val.str, parser->_val.len, 0);
228 : } else {
229 475 : add_assoc_stringl_ex(&array, parser->_key.str, parser->_key.len + 1, parser->_val.str, parser->_val.len, 0);
230 : }
231 483 : parser->_val.str = NULL;
232 : }
233 :
234 604 : STR_SET(parser->_key.str, NULL);
235 604 : STR_SET(parser->_val.str, NULL);
236 :
237 604 : php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_KEY);
238 604 : break;
239 :
240 : case PHP_HTTP_HEADER_PARSER_STATE_DONE:
241 82 : return PHP_HTTP_HEADER_PARSER_STATE_DONE;
242 : }
243 : }
244 :
245 94 : return php_http_header_parser_state_is(parser);
246 : }
247 :
248 : /*
249 : * Local variables:
250 : * tab-width: 4
251 : * c-basic-offset: 4
252 : * End:
253 : * vim600: noet sw=4 ts=4 fdm=marker
254 : * vim<600: noet sw=4 ts=4
255 : */
256 :
|