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