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