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-2014, 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_header_parser_t
*php_http_header_parser_init(php_http_header_parser_t
*parser
)
32 parser
= emalloc(sizeof(*parser
));
34 memset(parser
, 0, sizeof(*parser
));
39 php_http_header_parser_state_t
php_http_header_parser_state_push(php_http_header_parser_t
*parser
, unsigned argc
, ...)
43 php_http_header_parser_state_t state
= 0;
46 ZEND_PTR_STACK_RESIZE_IF_NEEDED((&parser
->stack
), argc
);
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
);
58 php_http_header_parser_state_t
php_http_header_parser_state_is(php_http_header_parser_t
*parser
)
60 if (parser
->stack
.top
) {
61 return (php_http_header_parser_state_t
) parser
->stack
.elements
[parser
->stack
.top
- 1];
64 return PHP_HTTP_HEADER_PARSER_STATE_START
;
67 php_http_header_parser_state_t
php_http_header_parser_state_pop(php_http_header_parser_t
*parser
)
69 if (parser
->stack
.top
) {
70 return (php_http_header_parser_state_t
) zend_ptr_stack_pop(&parser
->stack
);
73 return PHP_HTTP_HEADER_PARSER_STATE_START
;
76 void php_http_header_parser_dtor(php_http_header_parser_t
*parser
)
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
);
84 void php_http_header_parser_free(php_http_header_parser_t
**parser
)
87 php_http_header_parser_dtor(*parser
);
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
)
95 while (buffer
->used
|| !php_http_header_parser_states
[php_http_header_parser_state_is(parser
)].need_data
) {
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
);
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
);
108 case PHP_HTTP_HEADER_PARSER_STATE_START
: {
109 char *ptr
= buffer
->data
;
111 while (ptr
- buffer
->data
< buffer
->used
&& PHP_HTTP_IS_CTYPE(space
, *ptr
)) {
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
);
120 case PHP_HTTP_HEADER_PARSER_STATE_KEY
: {
121 const char *colon
, *eol_str
= NULL
;
124 if (buffer
->data
== (eol_str
= php_http_locate_bin_eol(buffer
->data
, buffer
->used
, &eol_len
))) {
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 */
131 callback_func(callback_arg
, &headers
, &parser
->info
);
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
)) {
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
);
143 /* neither reqeust/response line nor header: string */
144 return php_http_header_parser_state_push(parser
, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE
);
149 case PHP_HTTP_HEADER_PARSER_STATE_VALUE
: {
153 #define SET_ADD_VAL(slen, eol_len) \
155 const char *ptr = buffer->data; \
158 while (len > 0 && PHP_HTTP_IS_CTYPE(space, *ptr)) { \
162 while (len > 0 && PHP_HTTP_IS_CTYPE(space, ptr[len - 1])) { \
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'; \
174 parser->_val.len = len; \
175 parser->_val.str = estrndup(ptr, len); \
178 php_http_buffer_cut(buffer, 0, slen + eol_len); \
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
);
185 if (*buffer
->data
!= '\t' && *buffer
->data
!= ' ') {
186 php_http_header_parser_state_push(parser
, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE
);
189 php_http_header_parser_state_push(parser
, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE
);
195 if (flags
& PHP_HTTP_HEADER_PARSER_CLEANUP
) {
197 SET_ADD_VAL(buffer
->used
, 0);
199 php_http_header_parser_state_push(parser
, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE
);
201 return php_http_header_parser_state_push(parser
, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX
);
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
);
210 php_http_header_parser_state_push(parser
, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE
);
214 case PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE
:
215 if (parser
->_key
.str
&& parser
->_val
.str
) {
218 if (!headers
&& callback_func
) {
219 callback_func(callback_arg
, &headers
, NULL
);
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
));
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
);
230 parser
->_val
.str
= NULL
;
233 PTR_SET(parser
->_key
.str
, NULL
);
234 PTR_SET(parser
->_val
.str
, NULL
);
236 php_http_header_parser_state_push(parser
, 1, PHP_HTTP_HEADER_PARSER_STATE_KEY
);
239 case PHP_HTTP_HEADER_PARSER_STATE_DONE
:
240 return PHP_HTTP_HEADER_PARSER_STATE_DONE
;
244 return php_http_header_parser_state_is(parser
);
252 * vim600: noet sw=4 ts=4 fdm=marker
253 * vim<600: noet sw=4 ts=4