4 typedef struct php_http_header_parser_state_spec
{
5 php_http_header_parser_state_t state
;
7 } php_http_header_parser_state_spec_t
;
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}
18 PHP_HTTP_API php_http_header_parser_t
*php_http_header_parser_init(php_http_header_parser_t
*parser TSRMLS_CC
)
21 parser
= emalloc(sizeof(*parser
));
23 memset(parser
, 0, sizeof(*parser
));
25 TSRMLS_SET_CTX(parser
->ts
);
31 PHP_HTTP_API php_http_header_parser_state_t
php_http_header_parser_state_push(php_http_header_parser_t
*parser
, unsigned argc
, ...)
35 php_http_header_parser_state_t state
= 0;
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
));
47 PHP_HTTP_API php_http_header_parser_state_t
php_http_header_parser_state_is(php_http_header_parser_t
*parser
)
49 php_http_header_parser_state_t
*state
;
51 if (SUCCESS
== zend_stack_top(&parser
->stack
, (void *) &state
)) {
54 return PHP_HTTP_HEADER_PARSER_STATE_START
;
57 PHP_HTTP_API php_http_header_parser_state_t
php_http_header_parser_state_pop(php_http_header_parser_t
*parser
)
59 php_http_header_parser_state_t state
, *state_ptr
;
60 if (SUCCESS
== zend_stack_top(&parser
->stack
, (void *) &state_ptr
)) {
62 zend_stack_del_top(&parser
->stack
);
65 return PHP_HTTP_HEADER_PARSER_STATE_START
;
68 PHP_HTTP_API
void php_http_header_parser_dtor(php_http_header_parser_t
*parser
)
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
);
76 PHP_HTTP_API
void php_http_header_parser_free(php_http_header_parser_t
**parser
)
79 php_http_header_parser_dtor(*parser
);
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
)
87 TSRMLS_FETCH_FROM_CTX(parser
->ts
);
89 while (buffer
->used
|| !php_http_header_parser_states
[php_http_header_parser_state_is(parser
)].need_data
) {
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
);
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
);
99 case PHP_HTTP_HEADER_PARSER_STATE_START
: {
100 char *ptr
= buffer
->data
;
102 while (ptr
- buffer
->data
< buffer
->used
&& PHP_HTTP_IS_CTYPE(space
, *ptr
)) {
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
);
111 case PHP_HTTP_HEADER_PARSER_STATE_KEY
: {
112 const char *colon
, *eol_str
;
115 if (buffer
->data
== (eol_str
= php_http_locate_bin_eol(buffer
->data
, buffer
->used
, &eol_len
))) {
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 */
122 callback_func(callback_arg
, &headers
, &parser
->info TSRMLS_CC
);
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
)) {
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
);
134 /* neither reqeust/response line nor header: string */
135 return php_http_header_parser_state_push(parser
, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE
);
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)) {
150 callback_func(callback_arg, &headers, &parser->info TSRMLS_CC);
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);
156 return PHP_HTTP_HEADER_PARSER_STATE_FAILURE;
159 php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_KEY);
160 return PHP_HTTP_HEADER_PARSER_STATE_KEY;
166 case PHP_HTTP_HEADER_PARSER_STATE_VALUE
: {
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
);
175 if (nextline
== '\t' || nextline
== ' ') {
176 php_http_buffer_cut(buffer
, eol_str
- buffer
->data
, eol_len
);
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
) {
186 parser
->_val
.str
= estrndup(buffer
->data
, parser
->_val
.len
= buffer
->used
);
187 php_http_buffer_reset(buffer
);
189 php_http_header_parser_state_push(parser
, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE
);
191 return php_http_header_parser_state_push(parser
, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE
);
198 case PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE
:
199 if (parser
->_key
.str
&& parser
->_val
.str
) {
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);
208 add_assoc_stringl_ex(&array
, parser
->_key
.str
, parser
->_key
.len
+ 1, parser
->_val
.str
, parser
->_val
.len
, 0);
210 parser
->_val
.str
= NULL
;
213 STR_SET(parser
->_key
.str
, NULL
);
214 STR_SET(parser
->_val
.str
, NULL
);
216 php_http_header_parser_state_push(parser
, 1, PHP_HTTP_HEADER_PARSER_STATE_KEY
);
219 case PHP_HTTP_HEADER_PARSER_STATE_DONE
:
220 return PHP_HTTP_HEADER_PARSER_STATE_DONE
;
224 return php_http_header_parser_state_is(parser
);