2 +--------------------------------------------------------------------+
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-2013, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
13 #include "php_http_api.h"
15 typedef struct php_http_header_parser_state_spec
{
16 php_http_header_parser_state_t state
;
18 } php_http_header_parser_state_spec_t
;
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}
29 PHP_HTTP_API php_http_header_parser_t
*php_http_header_parser_init(php_http_header_parser_t
*parser TSRMLS_DC
)
32 parser
= emalloc(sizeof(*parser
));
34 memset(parser
, 0, sizeof(*parser
));
36 TSRMLS_SET_CTX(parser
->ts
);
41 PHP_HTTP_API php_http_header_parser_state_t
php_http_header_parser_state_push(php_http_header_parser_t
*parser
, unsigned argc
, ...)
45 php_http_header_parser_state_t state
= 0;
47 va_start(va_args
, argc
);
48 for (i
= 0; i
< argc
; ++i
) {
49 state
= va_arg(va_args
, php_http_header_parser_state_t
);
50 zend_stack_push(&parser
->stack
, &state
, sizeof(state
));
57 PHP_HTTP_API php_http_header_parser_state_t
php_http_header_parser_state_is(php_http_header_parser_t
*parser
)
59 php_http_header_parser_state_t
*state
;
61 if (SUCCESS
== zend_stack_top(&parser
->stack
, (void *) &state
)) {
64 return PHP_HTTP_HEADER_PARSER_STATE_START
;
67 PHP_HTTP_API php_http_header_parser_state_t
php_http_header_parser_state_pop(php_http_header_parser_t
*parser
)
69 php_http_header_parser_state_t state
, *state_ptr
;
70 if (SUCCESS
== zend_stack_top(&parser
->stack
, (void *) &state_ptr
)) {
72 zend_stack_del_top(&parser
->stack
);
75 return PHP_HTTP_HEADER_PARSER_STATE_START
;
78 PHP_HTTP_API
void php_http_header_parser_dtor(php_http_header_parser_t
*parser
)
80 zend_stack_destroy(&parser
->stack
);
81 php_http_info_dtor(&parser
->info
);
82 STR_FREE(parser
->_key
.str
);
83 STR_FREE(parser
->_val
.str
);
86 PHP_HTTP_API
void php_http_header_parser_free(php_http_header_parser_t
**parser
)
89 php_http_header_parser_dtor(*parser
);
95 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 TSRMLS_FETCH_FROM_CTX(parser
->ts
);
99 while (buffer
->used
|| !php_http_header_parser_states
[php_http_header_parser_state_is(parser
)].need_data
) {
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
);
105 switch (php_http_header_parser_state_pop(parser
)) {
106 case PHP_HTTP_HEADER_PARSER_STATE_FAILURE
:
107 return php_http_header_parser_state_push(parser
, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE
);
109 case PHP_HTTP_HEADER_PARSER_STATE_START
: {
110 char *ptr
= buffer
->data
;
112 while (ptr
- buffer
->data
< buffer
->used
&& PHP_HTTP_IS_CTYPE(space
, *ptr
)) {
116 php_http_buffer_cut(buffer
, 0, ptr
- buffer
->data
);
117 php_http_header_parser_state_push(parser
, 1, PHP_HTTP_HEADER_PARSER_STATE_KEY
);
121 case PHP_HTTP_HEADER_PARSER_STATE_KEY
: {
122 const char *colon
, *eol_str
= NULL
;
125 if (buffer
->data
== (eol_str
= php_http_locate_bin_eol(buffer
->data
, buffer
->used
, &eol_len
))) {
127 php_http_buffer_cut(buffer
, 0, eol_len
);
128 php_http_header_parser_state_push(parser
, 1, PHP_HTTP_HEADER_PARSER_STATE_DONE
);
129 } else if (php_http_info_parse(&parser
->info
, php_http_buffer_fix(buffer
)->data TSRMLS_CC
)) {
130 /* new message starting with request/response line */
132 callback_func(callback_arg
, &headers
, &parser
->info TSRMLS_CC
);
134 php_http_info_dtor(&parser
->info
);
135 php_http_buffer_cut(buffer
, 0, eol_str
+ eol_len
- buffer
->data
);
136 php_http_header_parser_state_push(parser
, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE
);
137 } else if ((colon
= memchr(buffer
->data
, ':', buffer
->used
)) && (!eol_str
|| eol_str
> colon
)) {
139 parser
->_key
.str
= estrndup(buffer
->data
, parser
->_key
.len
= colon
- buffer
->data
);
140 while (PHP_HTTP_IS_CTYPE(space
, *++colon
) && *colon
!= '\n' && *colon
!= '\r');
141 php_http_buffer_cut(buffer
, 0, colon
- buffer
->data
);
142 php_http_header_parser_state_push(parser
, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE
);
144 /* neither reqeust/response line nor header: string */
145 return php_http_header_parser_state_push(parser
, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE
);
150 case PHP_HTTP_HEADER_PARSER_STATE_VALUE
: {
154 #define SET_ADD_VAL(slen, eol_len) \
156 const char *ptr = buffer->data; \
159 while (len > 0 && PHP_HTTP_IS_CTYPE(space, *ptr)) { \
163 while (len > 0 && PHP_HTTP_IS_CTYPE(space, ptr[len - 1])) { \
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'; \
175 parser->_val.len = len; \
176 parser->_val.str = estrndup(ptr, len); \
179 php_http_buffer_cut(buffer, 0, slen + eol_len); \
182 if ((eol_str
= php_http_locate_bin_eol(buffer
->data
, buffer
->used
, &eol_len
))) {
183 SET_ADD_VAL(eol_str
- buffer
->data
, eol_len
);
186 if (*buffer
->data
!= '\t' && *buffer
->data
!= ' ') {
187 php_http_header_parser_state_push(parser
, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE
);
190 php_http_header_parser_state_push(parser
, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE
);
196 if (flags
& PHP_HTTP_HEADER_PARSER_CLEANUP
) {
198 SET_ADD_VAL(buffer
->used
, 0);
200 php_http_header_parser_state_push(parser
, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE
);
202 return php_http_header_parser_state_push(parser
, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX
);
207 case PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX
:
208 if (*buffer
->data
== ' ' || *buffer
->data
== '\t') {
209 php_http_header_parser_state_push(parser
, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE
);
211 php_http_header_parser_state_push(parser
, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE
);
215 case PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE
:
216 if (parser
->_key
.str
&& parser
->_val
.str
) {
219 if (!headers
&& callback_func
) {
220 callback_func(callback_arg
, &headers
, NULL TSRMLS_CC
);
223 INIT_PZVAL_ARRAY(&array
, headers
);
224 php_http_pretty_key(parser
->_key
.str
, parser
->_key
.len
, 1, 1);
225 if (SUCCESS
== zend_symtable_find(headers
, parser
->_key
.str
, parser
->_key
.len
+ 1, (void *) &exist
)) {
226 convert_to_array(*exist
);
227 add_next_index_stringl(*exist
, parser
->_val
.str
, parser
->_val
.len
, 0);
229 add_assoc_stringl_ex(&array
, parser
->_key
.str
, parser
->_key
.len
+ 1, parser
->_val
.str
, parser
->_val
.len
, 0);
231 parser
->_val
.str
= NULL
;
234 STR_SET(parser
->_key
.str
, NULL
);
235 STR_SET(parser
->_val
.str
, NULL
);
237 php_http_header_parser_state_push(parser
, 1, PHP_HTTP_HEADER_PARSER_STATE_KEY
);
240 case PHP_HTTP_HEADER_PARSER_STATE_DONE
:
241 return PHP_HTTP_HEADER_PARSER_STATE_DONE
;
245 return php_http_header_parser_state_is(parser
);
253 * vim600: noet sw=4 ts=4 fdm=marker
254 * vim<600: noet sw=4 ts=4