d09b0b46f4cd874ccc485b0e00b533521f1acde3
[m6w6/ext-http] / src / php_http_message.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 static void message_headers(php_http_message_t *msg, php_http_buffer_t *str);
16
17 zend_bool php_http_message_info_callback(php_http_message_t **message, HashTable **headers, php_http_info_t *info)
18 {
19 php_http_message_t *old = *message;
20
21 /* advance message */
22 if (!old || old->type || zend_hash_num_elements(&old->hdrs)) {
23 (*message) = php_http_message_init(NULL, 0, NULL);
24 (*message)->parent = old;
25 if (headers) {
26 (*headers) = &((*message)->hdrs);
27 }
28 }
29
30 if (info) {
31 php_http_message_set_info(*message, info);
32 }
33
34 return old != *message;
35 }
36
37 php_http_message_t *php_http_message_init(php_http_message_t *message, php_http_message_type_t type, php_http_message_body_t *body)
38 {
39 if (!message) {
40 message = emalloc(sizeof(*message));
41 }
42 memset(message, 0, sizeof(*message));
43
44 php_http_message_set_type(message, type);
45 message->http.version.major = 1;
46 message->http.version.minor = 1;
47 zend_hash_init(&message->hdrs, 0, NULL, ZVAL_PTR_DTOR, 0);
48 message->body = body ? body : php_http_message_body_init(NULL, NULL);
49
50 return message;
51 }
52
53 php_http_message_t *php_http_message_init_env(php_http_message_t *message, php_http_message_type_t type)
54 {
55 int free_msg = !message;
56 zval *sval, tval;
57 php_http_message_body_t *mbody;
58
59 switch (type) {
60 case PHP_HTTP_REQUEST:
61 mbody = php_http_env_get_request_body();
62 php_http_message_body_addref(mbody);
63 message = php_http_message_init(message, type, mbody);
64 if ((sval = php_http_env_get_server_var(ZEND_STRL("SERVER_PROTOCOL"), 1)) && !strncmp(Z_STRVAL_P(sval), "HTTP/", lenof("HTTP/"))) {
65 php_http_version_parse(&message->http.version, Z_STRVAL_P(sval));
66 }
67 if ((sval = php_http_env_get_server_var(ZEND_STRL("REQUEST_METHOD"), 1))) {
68 message->http.info.request.method = estrdup(Z_STRVAL_P(sval));
69 }
70 if ((sval = php_http_env_get_server_var(ZEND_STRL("REQUEST_URI"), 1))) {
71 message->http.info.request.url = php_http_url_parse(Z_STRVAL_P(sval), Z_STRLEN_P(sval), PHP_HTTP_URL_STDFLAGS);
72 }
73
74 php_http_env_get_request_headers(&message->hdrs);
75 break;
76
77 case PHP_HTTP_RESPONSE:
78 message = php_http_message_init(message, type, NULL);
79 if (!SG(sapi_headers).http_status_line || !php_http_info_parse((php_http_info_t *) &message->http, SG(sapi_headers).http_status_line)) {
80 if (!(message->http.info.response.code = SG(sapi_headers).http_response_code)) {
81 message->http.info.response.code = 200;
82 }
83 message->http.info.response.status = estrdup(php_http_env_get_response_status_for_code(message->http.info.response.code));
84 }
85
86 php_http_env_get_response_headers(&message->hdrs);
87 if (php_output_get_level()) {
88 if (php_output_get_status() & PHP_OUTPUT_SENT) {
89 php_error_docref(NULL, E_WARNING, "Could not fetch response body, output has already been sent at %s:%d", php_output_get_start_filename(), php_output_get_start_lineno());
90
91 goto error;
92 } else if (SUCCESS != php_output_get_contents(&tval)) {
93 php_error_docref(NULL, E_WARNING, "Could not fetch response body");
94 goto error;
95 } else {
96 php_http_message_body_append(message->body, Z_STRVAL(tval), Z_STRLEN(tval));
97 zval_dtor(&tval);
98 }
99 }
100 break;
101
102 default:
103 error:
104 if (free_msg) {
105 if (message) {
106 php_http_message_free(&message);
107 }
108 } else {
109 message = NULL;
110 }
111 break;
112 }
113
114 return message;
115 }
116
117 php_http_message_t *php_http_message_parse(php_http_message_t *msg, const char *str, size_t len, zend_bool greedy)
118 {
119 php_http_message_parser_t p;
120 php_http_buffer_t buf;
121 unsigned flags = PHP_HTTP_MESSAGE_PARSER_CLEANUP;
122 int free_msg;
123
124 php_http_buffer_from_string_ex(&buf, str, len);
125 php_http_message_parser_init(&p);
126
127 if ((free_msg = !msg)) {
128 msg = php_http_message_init(NULL, 0, NULL);
129 }
130
131 if (greedy) {
132 flags |= PHP_HTTP_MESSAGE_PARSER_GREEDY;
133 }
134 if (PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE == php_http_message_parser_parse(&p, &buf, flags, &msg)) {
135 if (free_msg) {
136 php_http_message_free(&msg);
137 }
138 msg = NULL;
139 }
140
141 php_http_message_parser_dtor(&p);
142 php_http_buffer_dtor(&buf);
143
144 return msg;
145 }
146
147 zval *php_http_message_header(php_http_message_t *msg, const char *key_str, size_t key_len)
148 {
149 zval *ret;
150 char *key;
151 ALLOCA_FLAG(free_key);
152
153 key = do_alloca(key_len + 1, free_key);
154
155 memcpy(key, key_str, key_len);
156 key[key_len] = '\0';
157 php_http_pretty_key(key, key_len, 1, 1);
158
159 ret = zend_symtable_str_find(&msg->hdrs, key, key_len);
160
161 free_alloca(key, free_key);
162
163 return ret;
164 }
165
166 zend_bool php_http_message_is_multipart(php_http_message_t *msg, char **boundary)
167 {
168 zend_string *ct = php_http_message_header_string(msg, ZEND_STRL("Content-Type"));
169 zend_bool is_multipart = 0;
170
171 if (ct) {
172 php_http_params_opts_t popts;
173 HashTable params;
174
175 ZEND_INIT_SYMTABLE(&params);
176 php_http_params_opts_default_get(&popts);
177 popts.input.str = ct->val;
178 popts.input.len = ct->len;
179
180 if (EXPECTED(php_http_params_parse(&params, &popts))) {
181 zval *cur, *arg;
182 zend_string *ct_str;
183 zend_ulong index;
184
185 zend_hash_internal_pointer_reset(&params);
186
187 if (EXPECTED((cur = zend_hash_get_current_data(&params))
188 && (Z_TYPE_P(cur) == IS_ARRAY)
189 && (HASH_KEY_IS_STRING == zend_hash_get_current_key(&params, &ct_str, &index)))
190 ) {
191 if (php_http_match(ct_str->val, "multipart", PHP_HTTP_MATCH_WORD)) {
192 is_multipart = 1;
193
194 /* get boundary */
195 if (EXPECTED(boundary
196 && (arg = zend_hash_str_find(Z_ARRVAL_P(cur), ZEND_STRL("arguments")))
197 && Z_TYPE_P(arg) == IS_ARRAY)
198 ) {
199 zval *val;
200 php_http_arrkey_t key;
201
202 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(arg), key.h, key.key, val)
203 {
204 if (key.key && key.key->len == lenof("boundary") && !strcasecmp(key.key->val, "boundary")) {
205 zend_string *bnd = zval_get_string(val);
206
207 if (EXPECTED(bnd->len)) {
208 *boundary = estrndup(bnd->val, bnd->len);
209 }
210 zend_string_release(bnd);
211 }
212 }
213 ZEND_HASH_FOREACH_END();
214 }
215 }
216 }
217 }
218 zend_hash_destroy(&params);
219 zend_string_release(ct);
220 }
221
222 return is_multipart;
223 }
224
225 /* */
226 void php_http_message_set_type(php_http_message_t *message, php_http_message_type_t type)
227 {
228 /* just act if different */
229 if (type != message->type) {
230
231 /* free request info */
232 switch (message->type) {
233 case PHP_HTTP_REQUEST:
234 PTR_FREE(message->http.info.request.method);
235 PTR_FREE(message->http.info.request.url);
236 break;
237
238 case PHP_HTTP_RESPONSE:
239 PTR_FREE(message->http.info.response.status);
240 break;
241
242 default:
243 break;
244 }
245
246 message->type = type;
247 memset(&message->http, 0, sizeof(message->http));
248 }
249 }
250
251 void php_http_message_set_info(php_http_message_t *message, php_http_info_t *info)
252 {
253 php_http_message_set_type(message, info->type);
254 message->http.version = info->http.version;
255 switch (message->type) {
256 case PHP_HTTP_REQUEST:
257 PTR_SET(PHP_HTTP_INFO(message).request.url, PHP_HTTP_INFO(info).request.url ? php_http_url_copy(PHP_HTTP_INFO(info).request.url, 0) : NULL);
258 PTR_SET(PHP_HTTP_INFO(message).request.method, PHP_HTTP_INFO(info).request.method ? estrdup(PHP_HTTP_INFO(info).request.method) : NULL);
259 break;
260
261 case PHP_HTTP_RESPONSE:
262 PHP_HTTP_INFO(message).response.code = PHP_HTTP_INFO(info).response.code;
263 PTR_SET(PHP_HTTP_INFO(message).response.status, PHP_HTTP_INFO(info).response.status ? estrdup(PHP_HTTP_INFO(info).response.status) : NULL);
264 break;
265
266 default:
267 break;
268 }
269 }
270
271 void php_http_message_update_headers(php_http_message_t *msg)
272 {
273 zval h;
274 size_t size;
275 zend_string *cl;
276
277 if (php_http_message_body_stream(msg->body)->readfilters.head) {
278 /* if a read stream filter is attached to the body the caller must also care for the headers */
279 } else if (php_http_message_header(msg, ZEND_STRL("Content-Range"))) {
280 /* don't mess around with a Content-Range message */
281 } else if ((size = php_http_message_body_size(msg->body))) {
282 ZVAL_LONG(&h, size);
283 zend_hash_str_update(&msg->hdrs, "Content-Length", lenof("Content-Length"), &h);
284
285 if (msg->body->boundary) {
286 char *str;
287 size_t len;
288 zend_string *ct;
289
290 if (!(ct = php_http_message_header_string(msg, ZEND_STRL("Content-Type")))) {
291 len = spprintf(&str, 0, "multipart/form-data; boundary=\"%s\"", msg->body->boundary);
292 ZVAL_STR(&h, php_http_cs2zs(str, len));
293 zend_hash_str_update(&msg->hdrs, "Content-Type", lenof("Content-Type"), &h);
294 } else if (!php_http_match(ct->val, "boundary=", PHP_HTTP_MATCH_WORD)) {
295 len = spprintf(&str, 0, "%s; boundary=\"%s\"", ct->val, msg->body->boundary);
296 ZVAL_STR(&h, php_http_cs2zs(str, len));
297 zend_hash_str_update(&msg->hdrs, "Content-Type", lenof("Content-Type"), &h);
298 zend_string_release(ct);
299 } else {
300 zend_string_release(ct);
301 }
302 }
303 } else if ((cl = php_http_message_header_string(msg, ZEND_STRL("Content-Length")))) {
304 if (!zend_string_equals_literal(cl, "0")) {
305 /* body->size == 0, so get rid of old Content-Length */
306 zend_hash_str_del(&msg->hdrs, ZEND_STRL("Content-Length"));
307 }
308 zend_string_release(cl);
309 } else if (msg->type == PHP_HTTP_REQUEST) {
310 if (!php_http_message_header(msg, ZEND_STRL("Transfer-Encoding"))) {
311 /* no filter, no CR, no size, no TE, no CL */
312 if (0 <= php_http_select_str(msg->http.info.request.method, 3, "POST", "PUT", "PATCH")) {
313 /* quoting RFC7230#section-3.3.2
314 A user agent SHOULD send a Content-Length in a request message when
315 no Transfer-Encoding is sent and the request method defines a meaning
316 for an enclosed payload body. For example, a Content-Length header
317 field is normally sent in a POST request even when the value is 0
318 (indicating an empty payload body). A user agent SHOULD NOT send a
319 Content-Length header field when the request message does not contain
320 a payload body and the method semantics do not anticipate such a
321 body.
322 */
323 ZVAL_LONG(&h, 0);
324 zend_hash_str_update(&msg->hdrs, "Content-Length", lenof("Content-Length"), &h);
325 }
326 }
327 }
328 }
329
330 static void message_headers(php_http_message_t *msg, php_http_buffer_t *str)
331 {
332 char *tmp = NULL;
333 size_t len = 0;
334
335 php_http_info_to_string((php_http_info_t *) msg, &tmp, &len, PHP_HTTP_CRLF);
336 php_http_message_update_headers(msg);
337
338 php_http_buffer_append(str, tmp, len);
339 php_http_header_to_string(str, &msg->hdrs);
340 PTR_FREE(tmp);
341 }
342
343 void php_http_message_to_callback(php_http_message_t *msg, php_http_pass_callback_t cb, void *cb_arg)
344 {
345 php_http_buffer_t str;
346
347 php_http_buffer_init_ex(&str, 0x1000, 0);
348 message_headers(msg, &str);
349 cb(cb_arg, str.data, str.used);
350 php_http_buffer_dtor(&str);
351
352 if (php_http_message_body_size(msg->body)) {
353 cb(cb_arg, ZEND_STRL(PHP_HTTP_CRLF));
354 php_http_message_body_to_callback(msg->body, cb, cb_arg, 0, 0);
355 }
356 }
357
358 void php_http_message_to_string(php_http_message_t *msg, char **string, size_t *length)
359 {
360 php_http_buffer_t str;
361 char *data;
362
363 php_http_buffer_init_ex(&str, 0x1000, 0);
364 message_headers(msg, &str);
365 if (php_http_message_body_size(msg->body)) {
366 php_http_buffer_appends(&str, PHP_HTTP_CRLF);
367 php_http_message_body_to_callback(msg->body, (php_http_pass_callback_t) php_http_buffer_append, &str, 0, 0);
368 }
369
370 data = php_http_buffer_data(&str, string, length);
371 if (!string) {
372 efree(data);
373 }
374
375 php_http_buffer_dtor(&str);
376 }
377
378 void php_http_message_serialize(php_http_message_t *message, char **string, size_t *length)
379 {
380 char *buf;
381 php_http_buffer_t str;
382 php_http_message_t *msg;
383
384 php_http_buffer_init(&str);
385
386 msg = message = php_http_message_reverse(message);
387 do {
388 php_http_message_to_callback(message, (php_http_pass_callback_t) php_http_buffer_append, &str);
389 php_http_buffer_appends(&str, PHP_HTTP_CRLF);
390 } while ((message = message->parent));
391 php_http_message_reverse(msg);
392
393 buf = php_http_buffer_data(&str, string, length);
394 if (!string) {
395 efree(buf);
396 }
397
398 php_http_buffer_dtor(&str);
399 }
400
401 php_http_message_t *php_http_message_reverse(php_http_message_t *msg)
402 {
403 size_t i, c = php_http_message_count(msg);
404
405 if (c > 1) {
406 php_http_message_t *tmp = msg, **arr;
407
408 arr = ecalloc(c, sizeof(*arr));
409 for (i = 0; i < c; ++i) {
410 arr[i] = tmp;
411 tmp = tmp->parent;
412 }
413 arr[0]->parent = NULL;
414 for (i = 0; i < c-1; ++i) {
415 arr[i+1]->parent = arr[i];
416 }
417
418 msg = arr[c-1];
419 efree(arr);
420 }
421
422 return msg;
423 }
424
425 php_http_message_t *php_http_message_zip(php_http_message_t *dst, php_http_message_t *src)
426 {
427 php_http_message_t *tmp_dst, *tmp_src, *ret = dst;
428
429 while (dst && src) {
430 tmp_dst = dst->parent;
431 tmp_src = src->parent;
432 dst->parent = src;
433 if (tmp_dst) {
434 src->parent = tmp_dst;
435 }
436 src = tmp_src;
437 dst = tmp_dst;
438 }
439
440 return ret;
441 }
442
443 php_http_message_t *php_http_message_copy_ex(php_http_message_t *from, php_http_message_t *to, zend_bool parents)
444 {
445 php_http_message_t *temp, *copy = NULL;
446 php_http_info_t info;
447
448 if (from) {
449 info.type = from->type;
450 info.http = from->http;
451
452 copy = temp = php_http_message_init(to, 0, php_http_message_body_copy(from->body, NULL));
453 php_http_message_set_info(temp, &info);
454 array_copy(&from->hdrs, &temp->hdrs);
455
456 if (parents) while (from->parent) {
457 info.type = from->parent->type;
458 info.http = from->parent->http;
459
460 temp->parent = php_http_message_init(NULL, 0, php_http_message_body_copy(from->parent->body, NULL));
461 php_http_message_set_info(temp->parent, &info);
462 array_copy(&from->parent->hdrs, &temp->parent->hdrs);
463
464 temp = temp->parent;
465 from = from->parent;
466 }
467 }
468
469 return copy;
470 }
471
472 void php_http_message_dtor(php_http_message_t *message)
473 {
474 if (EXPECTED(message)) {
475 zend_hash_destroy(&message->hdrs);
476 php_http_message_body_free(&message->body);
477
478 switch (message->type) {
479 case PHP_HTTP_REQUEST:
480 PTR_SET(message->http.info.request.method, NULL);
481 PTR_SET(message->http.info.request.url, NULL);
482 break;
483
484 case PHP_HTTP_RESPONSE:
485 PTR_SET(message->http.info.response.status, NULL);
486 break;
487
488 default:
489 break;
490 }
491 }
492 }
493
494 void php_http_message_free(php_http_message_t **message)
495 {
496 if (EXPECTED(*message)) {
497 if ((*message)->parent) {
498 php_http_message_free(&(*message)->parent);
499 }
500 php_http_message_dtor(*message);
501 efree(*message);
502 *message = NULL;
503 }
504 }
505
506 static zend_class_entry *php_http_message_class_entry;
507 zend_class_entry *php_http_message_get_class_entry(void)
508 {
509 return php_http_message_class_entry;
510 }
511
512 static zval *php_http_message_object_read_prop(zend_object *object, zend_string *member, int type, void **cache_slot, zval *rv);
513 static zval *php_http_message_object_write_prop(zend_object *object, zend_string *member, zval *value, void **cache_slot);
514
515 static zend_object_handlers php_http_message_object_handlers;
516 static HashTable php_http_message_object_prophandlers;
517
518 static void php_http_message_object_prophandler_hash_dtor(zval *pData)
519 {
520 pefree(Z_PTR_P(pData), 1);
521 }
522
523 typedef void (*php_http_message_object_prophandler_func_t)(php_http_message_object_t *o, zval *v);
524
525 typedef struct php_http_message_object_prophandler {
526 php_http_message_object_prophandler_func_t read;
527 php_http_message_object_prophandler_func_t write;
528 } php_http_message_object_prophandler_t;
529
530 static ZEND_RESULT_CODE php_http_message_object_add_prophandler(const char *prop_str, size_t prop_len, php_http_message_object_prophandler_func_t read, php_http_message_object_prophandler_func_t write) {
531 php_http_message_object_prophandler_t h = { read, write };
532 if (!zend_hash_str_add_mem(&php_http_message_object_prophandlers, prop_str, prop_len, (void *) &h, sizeof(h))) {
533 return FAILURE;
534 }
535 return SUCCESS;
536 }
537 static php_http_message_object_prophandler_t *php_http_message_object_get_prophandler(zend_string *name_str) {
538 return zend_hash_str_find_ptr(&php_http_message_object_prophandlers, name_str->val, name_str->len);
539 }
540 static void php_http_message_object_prophandler_get_type(php_http_message_object_t *obj, zval *return_value) {
541 zval_ptr_dtor(return_value);
542 RETVAL_LONG(obj->message->type);
543 }
544 static void php_http_message_object_prophandler_set_type(php_http_message_object_t *obj, zval *value) {
545 php_http_message_set_type(obj->message, zval_get_long(value));
546 }
547 static void php_http_message_object_prophandler_get_request_method(php_http_message_object_t *obj, zval *return_value) {
548 zval_ptr_dtor(return_value);
549 if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message) && obj->message->http.info.request.method) {
550 RETVAL_STRING(obj->message->http.info.request.method);
551 } else {
552 RETVAL_NULL();
553 }
554 }
555 static void php_http_message_object_prophandler_set_request_method(php_http_message_object_t *obj, zval *value) {
556 if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message)) {
557 zend_string *zs = zval_get_string(value);
558 PTR_SET(obj->message->http.info.request.method, estrndup(zs->val, zs->len));
559 zend_string_release(zs);
560 }
561 }
562 static void php_http_message_object_prophandler_get_request_url(php_http_message_object_t *obj, zval *return_value) {
563 char *url_str;
564 size_t url_len;
565
566 zval_ptr_dtor(return_value);
567 if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message) && obj->message->http.info.request.url && php_http_url_to_string(obj->message->http.info.request.url, &url_str, &url_len, 0)) {
568 RETVAL_STR(php_http_cs2zs(url_str, url_len));
569 } else {
570 RETVAL_NULL();
571 }
572 }
573 static void php_http_message_object_prophandler_set_request_url(php_http_message_object_t *obj, zval *value) {
574 if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message)) {
575 PTR_SET(obj->message->http.info.request.url, php_http_url_from_zval(value, PHP_HTTP_URL_STDFLAGS));
576 }
577 }
578 static void php_http_message_object_prophandler_get_response_status(php_http_message_object_t *obj, zval *return_value) {
579 zval_ptr_dtor(return_value);
580 if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, obj->message) && obj->message->http.info.response.status) {
581 RETVAL_STRING(obj->message->http.info.response.status);
582 } else {
583 RETVAL_NULL();
584 }
585 }
586 static void php_http_message_object_prophandler_set_response_status(php_http_message_object_t *obj, zval *value) {
587 if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, obj->message)) {
588 zend_string *zs = zval_get_string(value);
589 PTR_SET(obj->message->http.info.response.status, estrndup(zs->val, zs->len));
590 zend_string_release(zs);
591 }
592 }
593 static void php_http_message_object_prophandler_get_response_code(php_http_message_object_t *obj, zval *return_value) {
594 zval_ptr_dtor(return_value);
595 if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, obj->message)) {
596 RETVAL_LONG(obj->message->http.info.response.code);
597 } else {
598 RETVAL_NULL();
599 }
600 }
601 static void php_http_message_object_prophandler_set_response_code(php_http_message_object_t *obj, zval *value) {
602 if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, obj->message)) {
603 obj->message->http.info.response.code = zval_get_long(value);
604 PTR_SET(obj->message->http.info.response.status, estrdup(php_http_env_get_response_status_for_code(obj->message->http.info.response.code)));
605 }
606 }
607 static void php_http_message_object_prophandler_get_http_version(php_http_message_object_t *obj, zval *return_value) {
608 char *version_str;
609 size_t version_len;
610
611 zval_ptr_dtor(return_value);
612 php_http_version_to_string(&obj->message->http.version, &version_str, &version_len, NULL, NULL);
613 RETVAL_STR(php_http_cs2zs(version_str, version_len));
614 }
615 static void php_http_message_object_prophandler_set_http_version(php_http_message_object_t *obj, zval *value) {
616 zend_string *zs = zval_get_string(value);
617 php_http_version_parse(&obj->message->http.version, zs->val);
618 zend_string_release(zs);
619 }
620 static void php_http_message_object_prophandler_get_headers(php_http_message_object_t *obj, zval *return_value ) {
621 zval tmp;
622
623 ZVAL_COPY_VALUE(&tmp, return_value);
624 array_init(return_value);
625 array_copy(&obj->message->hdrs, Z_ARRVAL_P(return_value));
626 zval_ptr_dtor(&tmp);
627 }
628 static void php_http_message_object_prophandler_set_headers(php_http_message_object_t *obj, zval *value) {
629 int converted = 0;
630 HashTable garbage, *src;
631
632 if (Z_TYPE_P(value) != IS_ARRAY && Z_TYPE_P(value) != IS_OBJECT) {
633 converted = 1;
634 SEPARATE_ZVAL(value);
635 convert_to_array(value);
636 }
637 src = HASH_OF(value);
638
639 garbage = obj->message->hdrs;
640 zend_hash_init(&obj->message->hdrs, zend_hash_num_elements(src), NULL, ZVAL_PTR_DTOR, 0);
641 array_copy(HASH_OF(value), &obj->message->hdrs);
642
643 zend_hash_destroy(&garbage);
644
645 if (converted) {
646 zval_ptr_dtor(value);
647 }
648 }
649 static void php_http_message_object_prophandler_get_body(php_http_message_object_t *obj, zval *return_value) {
650 zval tmp;
651
652 if (!obj->body) {
653 RETURN_NULL();
654 }
655
656 ZVAL_COPY_VALUE(&tmp, return_value);
657 RETVAL_OBJECT(&obj->body->zo, 1);
658 zval_ptr_dtor(&tmp);
659 }
660 static void php_http_message_object_prophandler_set_body(php_http_message_object_t *obj, zval *value) {
661 php_http_message_object_set_body(obj, value);
662 }
663 static void php_http_message_object_prophandler_get_parent_message(php_http_message_object_t *obj, zval *return_value) {
664 if (obj->message->parent) {
665 zval tmp;
666
667 ZVAL_COPY_VALUE(&tmp, return_value);
668 RETVAL_OBJECT(&obj->parent->zo, 1);
669 zval_ptr_dtor(&tmp);
670 } else {
671 RETVAL_NULL();
672 }
673 }
674 static void php_http_message_object_prophandler_set_parent_message(php_http_message_object_t *obj, zval *value) {
675 if (Z_TYPE_P(value) == IS_OBJECT && instanceof_function(Z_OBJCE_P(value), php_http_message_class_entry)) {
676 php_http_message_object_t *parent_obj = PHP_HTTP_OBJ(NULL, value);
677
678 Z_ADDREF_P(value);
679 if (obj->message->parent) {
680 zend_object_release(&obj->parent->zo);
681 }
682 obj->parent = parent_obj;
683 obj->message->parent = parent_obj->message;
684 }
685 }
686
687 #define PHP_HTTP_MESSAGE_OBJECT_INIT(obj) \
688 do { \
689 if (!obj->message) { \
690 obj->message = php_http_message_init(NULL, 0, NULL); \
691 } \
692 } while(0)
693
694
695 void php_http_message_object_reverse(zval *zmsg, zval *return_value)
696 {
697 size_t i;
698 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, zmsg);
699
700 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
701
702 /* count */
703 i = php_http_message_count(obj->message);
704
705 if (i > 1) {
706 php_http_message_object_t **objects;
707 int last;
708
709 objects = ecalloc(i, sizeof(*objects));
710
711 /* we are the first message */
712 objects[0] = obj;
713
714 /* fetch parents */
715 for (i = 1; obj->parent; ++i) {
716 objects[i] = obj = obj->parent;
717 }
718
719 /* reorder parents */
720 for (last = --i; i; --i) {
721 objects[i]->message->parent = objects[i-1]->message;
722 objects[i]->parent = objects[i-1];
723 }
724
725 objects[0]->message->parent = NULL;
726 objects[0]->parent = NULL;
727
728 /* add ref, because we previously have not been a parent message */
729 Z_ADDREF_P(zmsg);
730 /* no addref, because we've been a parent message previously */
731 RETVAL_OBJECT(&objects[last]->zo, 0);
732
733 efree(objects);
734 } else {
735 RETURN_ZVAL(zmsg, 1, 0);
736 }
737 }
738
739 void php_http_message_object_prepend(zval *this_ptr, zval *prepend, zend_bool top)
740 {
741 php_http_message_t *save_parent_msg = NULL;
742 php_http_message_object_t *save_parent_obj = NULL, *obj = PHP_HTTP_OBJ(NULL, this_ptr);
743 php_http_message_object_t *prepend_obj = PHP_HTTP_OBJ(NULL, prepend);
744
745 if (!top) {
746 save_parent_obj = obj->parent;
747 save_parent_msg = obj->message->parent;
748 } else {
749 /* iterate to the most parent object */
750 while (obj->parent) {
751 obj = obj->parent;
752 }
753 }
754
755 /* prepend */
756 obj->parent = prepend_obj;
757 obj->message->parent = prepend_obj->message;
758
759 /* add ref */
760 Z_ADDREF_P(prepend);
761
762 if (!top) {
763 prepend_obj->parent = save_parent_obj;
764 prepend_obj->message->parent = save_parent_msg;
765 }
766 }
767
768 ZEND_RESULT_CODE php_http_message_object_set_body(php_http_message_object_t *msg_obj, zval *zbody)
769 {
770 php_stream *s;
771 zend_string *body_str;
772 php_http_message_body_t *body;
773 php_http_message_body_object_t *body_obj;
774
775 switch (Z_TYPE_P(zbody)) {
776 case IS_RESOURCE:
777 php_stream_from_zval_no_verify(s, zbody);
778 if (!s) {
779 php_http_throw(unexpected_val, "The stream is not a valid resource");
780 return FAILURE;
781 }
782
783 is_resource:
784
785 body = php_http_message_body_init(NULL, s);
786 if (!(body_obj = php_http_message_body_object_new_ex(php_http_get_message_body_class_entry(), body))) {
787 php_http_message_body_free(&body);
788 return FAILURE;
789 }
790 break;
791
792 case IS_OBJECT:
793 if (instanceof_function(Z_OBJCE_P(zbody), php_http_get_message_body_class_entry())) {
794 Z_ADDREF_P(zbody);
795 body_obj = PHP_HTTP_OBJ(NULL, zbody);
796 break;
797 }
798 /* no break */
799
800 default:
801 body_str = zval_get_string(zbody);
802 s = php_stream_temp_new();
803 php_stream_write(s, body_str->val, body_str->len);
804 zend_string_release(body_str);
805 goto is_resource;
806
807 }
808
809 if (!body_obj->body) {
810 body_obj->body = php_http_message_body_init(NULL, NULL);
811 }
812 if (msg_obj->body) {
813 zend_object_release(&msg_obj->body->zo);
814 }
815 if (msg_obj->message) {
816 php_http_message_body_free(&msg_obj->message->body);
817 msg_obj->message->body = body_obj->body;
818 } else {
819 msg_obj->message = php_http_message_init(NULL, 0, body_obj->body);
820 }
821 php_http_message_body_addref(body_obj->body);
822 msg_obj->body = body_obj;
823
824 return SUCCESS;
825 }
826
827 ZEND_RESULT_CODE php_http_message_object_init_body_object(php_http_message_object_t *obj)
828 {
829 php_http_message_body_addref(obj->message->body);
830 return php_http_new((void *) &obj->body, php_http_get_message_body_class_entry(), (php_http_new_t) php_http_message_body_object_new_ex, NULL, obj->message->body);
831 }
832
833 zend_object *php_http_message_object_new(zend_class_entry *ce)
834 {
835 return &php_http_message_object_new_ex(ce, NULL)->zo;
836 }
837
838 php_http_message_object_t *php_http_message_object_new_ex(zend_class_entry *ce, php_http_message_t *msg)
839 {
840 php_http_message_object_t *o;
841
842 o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce));
843 zend_object_std_init(&o->zo, ce);
844 object_properties_init(&o->zo, ce);
845
846 if (msg) {
847 o->message = msg;
848 if (msg->parent) {
849 o->parent = php_http_message_object_new_ex(ce, msg->parent);
850 }
851 o->body = php_http_message_body_object_new_ex(php_http_get_message_body_class_entry(), php_http_message_body_init(&msg->body, NULL));
852 }
853
854 o->zo.handlers = &php_http_message_object_handlers;
855
856 return o;
857 }
858
859 zend_object *php_http_message_object_clone(zend_object *this_ptr)
860 {
861 php_http_message_object_t *new_obj;
862 php_http_message_object_t *old_obj = PHP_HTTP_OBJ(this_ptr, NULL);
863
864 new_obj = php_http_message_object_new_ex(old_obj->zo.ce, php_http_message_copy(old_obj->message, NULL));
865 zend_objects_clone_members(&new_obj->zo, &old_obj->zo);
866
867 return &new_obj->zo;
868 }
869
870 void php_http_message_object_free(zend_object *object)
871 {
872 php_http_message_object_t *o = PHP_HTTP_OBJ(object, NULL);
873
874 PTR_FREE(o->gc);
875
876 if (!Z_ISUNDEF(o->iterator)) {
877 zval_ptr_dtor(&o->iterator);
878 ZVAL_UNDEF(&o->iterator);
879 }
880 if (o->message) {
881 /* do NOT free recursivly */
882 php_http_message_dtor(o->message);
883 efree(o->message);
884 o->message = NULL;
885 }
886 if (o->parent) {
887 zend_object_release(&o->parent->zo);
888 o->parent = NULL;
889 }
890 if (o->body) {
891 zend_object_release(&o->body->zo);
892 o->body = NULL;
893 }
894 zend_object_std_dtor(object);
895 }
896
897 static zval *php_http_message_object_get_prop_ptr(zend_object *object, zend_string *member, int type, void **cache_slot)
898 {
899 return NULL;
900 }
901
902 static zval *php_http_message_object_read_prop(zend_object *object, zend_string *member, int type, void **cache_slot, zval *tmp)
903 {
904 zval *return_value;
905 php_http_message_object_prophandler_t *handler = php_http_message_object_get_prophandler(member);
906
907 return_value = zend_get_std_object_handlers()->read_property(object, member, type, cache_slot, tmp);
908
909 if (handler && handler->read) {
910 php_http_message_object_t *obj = PHP_HTTP_OBJ(object, NULL);
911
912 handler->read(obj, return_value);
913 }
914 return return_value;
915 }
916
917 static zval *php_http_message_object_write_prop(zend_object *object, zend_string *member, zval *value, void **cache_slot)
918 {
919 php_http_message_object_t *obj = PHP_HTTP_OBJ(object, NULL);
920 php_http_message_object_prophandler_t *handler;
921
922 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
923
924 if ((handler = php_http_message_object_get_prophandler(member))) {
925 handler->write(obj, value);
926 } else {
927 zend_get_std_object_handlers()->write_property(object, member, value, cache_slot);
928 }
929 return value;
930 }
931
932 static HashTable *php_http_message_object_get_debug_info(zend_object *object, int *is_temp)
933 {
934 php_http_message_object_t *obj = PHP_HTTP_OBJ(object, NULL);
935 HashTable *props = zend_get_std_object_handlers()->get_properties(object);
936 char *ver_str, *url_str = NULL;
937 size_t ver_len, url_len = 0;
938 zval tmp;
939
940 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
941 if (is_temp) {
942 *is_temp = 0;
943 }
944
945 #define UPDATE_PROP(name_str, action_with_tmp) \
946 do { \
947 zend_property_info *pi; \
948 if ((pi = zend_hash_str_find_ptr(&obj->zo.ce->properties_info, name_str, lenof(name_str)))) { \
949 action_with_tmp; \
950 zend_hash_update_ind(props, pi->name, &tmp); \
951 } \
952 } while(0)
953
954 UPDATE_PROP("type", ZVAL_LONG(&tmp, obj->message->type));
955
956 ver_len = spprintf(&ver_str, 0, "%u.%u", obj->message->http.version.major, obj->message->http.version.minor);
957 UPDATE_PROP("httpVersion", ZVAL_STR(&tmp, php_http_cs2zs(ver_str, ver_len)));
958
959 switch (obj->message->type) {
960 case PHP_HTTP_REQUEST:
961 UPDATE_PROP("responseCode", ZVAL_LONG(&tmp, 0));
962 UPDATE_PROP("responseStatus", ZVAL_EMPTY_STRING(&tmp));
963 UPDATE_PROP("requestMethod", ZVAL_STRING(&tmp, STR_PTR(obj->message->http.info.request.method)));
964 if (obj->message->http.info.request.url) {
965 php_http_url_to_string(obj->message->http.info.request.url, &url_str, &url_len, 0);
966 UPDATE_PROP("requestUrl", ZVAL_STR(&tmp, php_http_cs2zs(url_str, url_len)));
967 } else {
968 UPDATE_PROP("requestUrl", ZVAL_EMPTY_STRING(&tmp));
969 }
970
971 break;
972
973 case PHP_HTTP_RESPONSE:
974 UPDATE_PROP("responseCode", ZVAL_LONG(&tmp, obj->message->http.info.response.code));
975 UPDATE_PROP("responseStatus", ZVAL_STRING(&tmp, STR_PTR(obj->message->http.info.response.status)));
976 UPDATE_PROP("requestMethod", ZVAL_EMPTY_STRING(&tmp));
977 UPDATE_PROP("requestUrl", ZVAL_EMPTY_STRING(&tmp));
978 break;
979
980 case PHP_HTTP_NONE:
981 default:
982 UPDATE_PROP("responseCode", ZVAL_LONG(&tmp, 0));
983 UPDATE_PROP("responseStatus", ZVAL_EMPTY_STRING(&tmp));
984 UPDATE_PROP("requestMethod", ZVAL_EMPTY_STRING(&tmp));
985 UPDATE_PROP("requestUrl", ZVAL_EMPTY_STRING(&tmp));
986 break;
987 }
988
989 UPDATE_PROP("headers",
990 array_init(&tmp);
991 array_copy(&obj->message->hdrs, Z_ARRVAL(tmp));
992 );
993
994 UPDATE_PROP("body",
995 if (obj->body) {
996 ZVAL_OBJECT(&tmp, &obj->body->zo, 1);
997 } else {
998 ZVAL_NULL(&tmp);
999 }
1000 );
1001
1002 UPDATE_PROP("parentMessage",
1003 if (obj->message->parent) {
1004 ZVAL_OBJECT(&tmp, &obj->parent->zo, 1);
1005 } else {
1006 ZVAL_NULL(&tmp);
1007 }
1008 );
1009
1010 return props;
1011 }
1012
1013 static HashTable *php_http_message_object_get_gc(zend_object *object, zval **table, int *n)
1014 {
1015 php_http_message_object_t *obj = PHP_HTTP_OBJ(object, NULL);
1016 HashTable *props = object->handlers->get_properties(object);
1017 uint32_t count = 2 + zend_hash_num_elements(props);
1018 zval *val;
1019
1020 *n = 0;
1021 *table = obj->gc = erealloc(obj->gc, count * sizeof(zval));
1022
1023 if (obj->body) {
1024 ZVAL_OBJ(&obj->gc[(*n)++], &obj->body->zo);
1025 }
1026 if (obj->parent) {
1027 ZVAL_OBJ(&obj->gc[(*n)++], &obj->parent->zo);
1028 }
1029
1030 ZEND_HASH_FOREACH_VAL(props, val)
1031 {
1032 ZVAL_COPY_VALUE(&obj->gc[(*n)++], val);
1033 }
1034 ZEND_HASH_FOREACH_END();
1035
1036 return NULL;
1037 }
1038
1039 static int php_http_message_object_cast(zend_object *object, zval *return_value, int type)
1040 {
1041 php_http_message_object_t *obj = PHP_HTTP_OBJ(object, NULL);
1042 char *string;
1043 size_t length;
1044
1045 switch (type) {
1046 case IS_STRING:
1047 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1048 php_http_message_to_string(obj->message, &string, &length);
1049 if (string) {
1050 RETVAL_STR(php_http_cs2zs(string, length));
1051 } else {
1052 RETVAL_EMPTY_STRING();
1053 }
1054 return SUCCESS;
1055 case _IS_BOOL:
1056 RETVAL_TRUE;
1057 return SUCCESS;
1058 default:
1059 return FAILURE;
1060 }
1061 }
1062
1063 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage___construct, 0, 0, 0)
1064 ZEND_ARG_INFO(0, message)
1065 ZEND_ARG_INFO(0, greedy)
1066 ZEND_END_ARG_INFO();
1067 static PHP_METHOD(HttpMessage, __construct)
1068 {
1069 zend_bool greedy = 1;
1070 zval *zmessage = NULL;
1071 php_http_message_t *msg = NULL;
1072 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1073 zend_error_handling zeh;
1074
1075 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|z!b", &zmessage, &greedy), invalid_arg, return);
1076
1077 zend_replace_error_handling(EH_THROW, php_http_get_exception_bad_message_class_entry(), &zeh);
1078 if (zmessage && Z_TYPE_P(zmessage) == IS_RESOURCE) {
1079 php_stream *s;
1080 php_http_message_parser_t p;
1081 zend_error_handling zeh;
1082
1083 zend_replace_error_handling(EH_THROW, php_http_get_exception_unexpected_val_class_entry(), &zeh);
1084 php_stream_from_zval(s, zmessage);
1085 zend_restore_error_handling(&zeh);
1086
1087 if (s && php_http_message_parser_init(&p)) {
1088 unsigned flags = (greedy ? PHP_HTTP_MESSAGE_PARSER_GREEDY : 0);
1089 php_http_buffer_t buf;
1090
1091 php_http_buffer_init_ex(&buf, 0x1000, PHP_HTTP_BUFFER_INIT_PREALLOC);
1092 if (PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE == php_http_message_parser_parse_stream(&p, &buf, s, flags, &msg)) {
1093 if (!EG(exception)) {
1094 php_http_throw(bad_message, "Could not parse message from stream");
1095 }
1096 }
1097 php_http_buffer_dtor(&buf);
1098 php_http_message_parser_dtor(&p);
1099 }
1100
1101 if (!msg && !EG(exception)) {
1102 php_http_throw(bad_message, "Empty message received from stream");
1103 }
1104 } else if (zmessage) {
1105 zend_string *zs_msg = zval_get_string(zmessage);
1106
1107 msg = php_http_message_parse(NULL, zs_msg->val, zs_msg->len, greedy);
1108
1109 if (!msg && !EG(exception)) {
1110 php_http_throw(bad_message, "Could not parse message: %.*s", (int) MIN(25, zs_msg->len), zs_msg->val);
1111 }
1112 zend_string_release(zs_msg);
1113 }
1114
1115 if (msg) {
1116 php_http_message_dtor(obj->message);
1117 obj->message = msg;
1118 if (obj->message->parent) {
1119 obj->parent = php_http_message_object_new_ex(obj->zo.ce, obj->message->parent);
1120 }
1121 }
1122 zend_restore_error_handling(&zeh);
1123 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1124 }
1125
1126 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getBody, 0, 0, 0)
1127 ZEND_END_ARG_INFO();
1128 static PHP_METHOD(HttpMessage, getBody)
1129 {
1130 php_http_message_object_t *obj;
1131
1132 php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
1133
1134 obj = PHP_HTTP_OBJ(NULL, getThis());
1135 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1136
1137 if (!obj->body) {
1138 php_http_message_object_init_body_object(obj);
1139 }
1140 if (obj->body) {
1141 RETVAL_OBJECT(&obj->body->zo, 1);
1142 }
1143 }
1144
1145 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setBody, 0, 0, 1)
1146 ZEND_ARG_OBJ_INFO(0, body, http\\Message\\Body, 0)
1147 ZEND_END_ARG_INFO();
1148 static PHP_METHOD(HttpMessage, setBody)
1149 {
1150 zval *zbody;
1151
1152 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "O", &zbody, php_http_get_message_body_class_entry())) {
1153 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1154
1155 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1156 php_http_message_object_prophandler_set_body(obj, zbody);
1157 }
1158 RETVAL_ZVAL(getThis(), 1, 0);
1159 }
1160
1161 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_addBody, 0, 0, 1)
1162 ZEND_ARG_OBJ_INFO(0, body, http\\Message\\Body, 0)
1163 ZEND_END_ARG_INFO();
1164 static PHP_METHOD(HttpMessage, addBody)
1165 {
1166 zval *new_body;
1167
1168 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "O", &new_body, php_http_get_message_body_class_entry())) {
1169 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1170 php_http_message_body_object_t *new_obj = PHP_HTTP_OBJ(NULL, new_body);
1171
1172 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1173 php_http_message_body_to_callback(new_obj->body, (php_http_pass_callback_t) php_http_message_body_append, obj->message->body, 0, 0);
1174 }
1175 RETVAL_ZVAL(getThis(), 1, 0);
1176 }
1177
1178 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getHeader, 0, 0, 1)
1179 ZEND_ARG_INFO(0, header)
1180 ZEND_ARG_INFO(0, into_class)
1181 ZEND_END_ARG_INFO();
1182 static PHP_METHOD(HttpMessage, getHeader)
1183 {
1184 char *header_str;
1185 size_t header_len;
1186 zend_class_entry *header_ce = NULL;
1187
1188 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s|C!", &header_str, &header_len, &header_ce)) {
1189 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1190 zval *header;
1191
1192 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1193
1194 if ((header = php_http_message_header(obj->message, header_str, header_len))) {
1195 if (!header_ce) {
1196 RETURN_ZVAL(header, 1, 0);
1197 } else if (instanceof_function(header_ce, php_http_header_get_class_entry())) {
1198 php_http_object_method_t cb;
1199 zval argv[2];
1200
1201 ZVAL_STRINGL(&argv[0], header_str, header_len);
1202 ZVAL_COPY(&argv[1], header);
1203
1204 object_init_ex(return_value, header_ce);
1205 php_http_object_method_init(&cb, return_value, ZEND_STRL("__construct"));
1206 php_http_object_method_call(&cb, return_value, NULL, 2, argv);
1207 php_http_object_method_dtor(&cb);
1208
1209 zval_ptr_dtor(&argv[0]);
1210 zval_ptr_dtor(&argv[1]);
1211
1212 return;
1213 } else {
1214 php_error_docref(NULL, E_WARNING, "Class '%s' is not as descendant of http\\Header", header_ce->name->val);
1215 }
1216 }
1217 }
1218 RETURN_FALSE;
1219 }
1220
1221 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getHeaders, 0, 0, 0)
1222 ZEND_END_ARG_INFO();
1223 static PHP_METHOD(HttpMessage, getHeaders)
1224 {
1225 if (SUCCESS == zend_parse_parameters_none()) {
1226 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1227
1228 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1229
1230 array_init(return_value);
1231 array_copy(&obj->message->hdrs, Z_ARRVAL_P(return_value));
1232 }
1233 }
1234
1235 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setHeader, 0, 0, 1)
1236 ZEND_ARG_INFO(0, header)
1237 ZEND_ARG_INFO(0, value)
1238 ZEND_END_ARG_INFO();
1239 static PHP_METHOD(HttpMessage, setHeader)
1240 {
1241 zval *zvalue = NULL;
1242 char *name_str;
1243 size_t name_len;
1244
1245 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s|z!", &name_str, &name_len, &zvalue)) {
1246 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1247 char *name = php_http_pretty_key(estrndup(name_str, name_len), name_len, 1, 1);
1248
1249 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1250
1251 if (!zvalue) {
1252 zend_symtable_str_del(&obj->message->hdrs, name, name_len);
1253 } else {
1254 Z_TRY_ADDREF_P(zvalue);
1255 zend_symtable_str_update(&obj->message->hdrs, name, name_len, zvalue);
1256 }
1257 efree(name);
1258 }
1259 RETVAL_ZVAL(getThis(), 1, 0);
1260 }
1261
1262 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setHeaders, 0, 0, 1)
1263 ZEND_ARG_ARRAY_INFO(0, headers, 1)
1264 ZEND_END_ARG_INFO();
1265 static PHP_METHOD(HttpMessage, setHeaders)
1266 {
1267 zval *new_headers = NULL;
1268
1269 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "a/!", &new_headers)) {
1270 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1271
1272 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1273
1274 zend_hash_clean(&obj->message->hdrs);
1275 if (new_headers) {
1276 array_join(Z_ARRVAL_P(new_headers), &obj->message->hdrs, 0, ARRAY_JOIN_PRETTIFY|ARRAY_JOIN_STRONLY);
1277 }
1278 }
1279 RETVAL_ZVAL(getThis(), 1, 0);
1280 }
1281
1282 static inline void php_http_message_object_add_header(php_http_message_object_t *obj, const char *name_str, size_t name_len, zval *zvalue)
1283 {
1284 char *name = php_http_pretty_key(estrndup(name_str, name_len), name_len, 1, 1);
1285 zend_string *hstr, *vstr;
1286 zval *header, tmp;
1287
1288 if (Z_TYPE_P(zvalue) == IS_NULL) {
1289 return;
1290 }
1291
1292 vstr = php_http_header_value_to_string(zvalue);
1293
1294 if ((name_len != lenof("Set-Cookie") && strcmp(name, "Set-Cookie"))
1295 && (hstr = php_http_message_header_string(obj->message, name, name_len))) {
1296 char *hdr_str;
1297 size_t hdr_len = spprintf(&hdr_str, 0, "%s, %s", hstr->val, vstr->val);
1298
1299 ZVAL_STR(&tmp, php_http_cs2zs(hdr_str, hdr_len));
1300 zend_symtable_str_update(&obj->message->hdrs, name, name_len, &tmp);
1301 zend_string_release(hstr);
1302 zend_string_release(vstr);
1303 } else if ((header = php_http_message_header(obj->message, name, name_len))) {
1304 convert_to_array(header);
1305 ZVAL_STR(&tmp, vstr);
1306 zend_hash_next_index_insert(Z_ARRVAL_P(header), &tmp);
1307 } else {
1308 ZVAL_STR(&tmp, vstr);
1309 zend_symtable_str_update(&obj->message->hdrs, name, name_len, &tmp);
1310 }
1311 efree(name);
1312 }
1313
1314 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_addHeader, 0, 0, 2)
1315 ZEND_ARG_INFO(0, header)
1316 ZEND_ARG_INFO(0, value)
1317 ZEND_END_ARG_INFO();
1318 static PHP_METHOD(HttpMessage, addHeader)
1319 {
1320 zval *zvalue;
1321 char *name_str;
1322 size_t name_len;
1323
1324 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &name_str, &name_len, &zvalue)) {
1325 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1326
1327 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1328
1329 php_http_message_object_add_header(obj, name_str, name_len, zvalue);
1330 }
1331 RETVAL_ZVAL(getThis(), 1, 0);
1332 }
1333
1334 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_addHeaders, 0, 0, 1)
1335 ZEND_ARG_ARRAY_INFO(0, headers, 0)
1336 ZEND_ARG_INFO(0, append)
1337 ZEND_END_ARG_INFO();
1338 static PHP_METHOD(HttpMessage, addHeaders)
1339 {
1340 zval *new_headers;
1341 zend_bool append = 0;
1342
1343 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "a|b", &new_headers, &append)) {
1344 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1345
1346 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1347
1348 if (append) {
1349 php_http_arrkey_t key = {0};
1350 zval *val;
1351
1352 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(new_headers), key.h, key.key, val)
1353 {
1354 php_http_arrkey_stringify(&key, NULL);
1355 php_http_message_object_add_header(obj, key.key->val, key.key->len, val);
1356 php_http_arrkey_dtor(&key);
1357 }
1358 ZEND_HASH_FOREACH_END();
1359 } else {
1360 array_join(Z_ARRVAL_P(new_headers), &obj->message->hdrs, 0, ARRAY_JOIN_PRETTIFY|ARRAY_JOIN_STRONLY);
1361 }
1362 }
1363 RETVAL_ZVAL(getThis(), 1, 0);
1364 }
1365
1366 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getType, 0, 0, 0)
1367 ZEND_END_ARG_INFO();
1368 static PHP_METHOD(HttpMessage, getType)
1369 {
1370 if (SUCCESS == zend_parse_parameters_none()) {
1371 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1372
1373 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1374
1375 RETURN_LONG(obj->message->type);
1376 }
1377 }
1378
1379 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setType, 0, 0, 1)
1380 ZEND_ARG_INFO(0, type)
1381 ZEND_END_ARG_INFO();
1382 static PHP_METHOD(HttpMessage, setType)
1383 {
1384 zend_long type;
1385
1386 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "l", &type)) {
1387 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1388
1389 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1390
1391 php_http_message_set_type(obj->message, type);
1392 }
1393 RETVAL_ZVAL(getThis(), 1, 0);
1394 }
1395
1396 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getInfo, 0, 0, 0)
1397 ZEND_END_ARG_INFO();
1398 static PHP_METHOD(HttpMessage, getInfo)
1399 {
1400 if (SUCCESS == zend_parse_parameters_none()) {
1401 char *str = NULL;
1402 size_t len = 0;
1403 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1404
1405 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1406 php_http_info_to_string((php_http_info_t *) obj->message, &str, &len, "");
1407
1408 RETVAL_STR(php_http_cs2zs(str, len));
1409 }
1410 }
1411
1412 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setInfo, 0, 0, 1)
1413 ZEND_ARG_INFO(0, http_info)
1414 ZEND_END_ARG_INFO();
1415 static PHP_METHOD(HttpMessage, setInfo)
1416 {
1417 char *str;
1418 size_t len;
1419 php_http_message_object_t *obj;
1420 php_http_info_t inf;
1421
1422 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &len), invalid_arg, return);
1423
1424 obj = PHP_HTTP_OBJ(NULL, getThis());
1425 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1426
1427 if (!php_http_info_parse(&inf, str)) {
1428 php_http_throw(bad_header, "Could not parse message info '%s'", str);
1429 return;
1430 }
1431
1432 php_http_message_set_info(obj->message, &inf);
1433 php_http_info_dtor(&inf);
1434
1435 RETVAL_ZVAL(getThis(), 1, 0);
1436 }
1437
1438 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getHttpVersion, 0, 0, 0)
1439 ZEND_END_ARG_INFO();
1440 static PHP_METHOD(HttpMessage, getHttpVersion)
1441 {
1442 if (SUCCESS == zend_parse_parameters_none()) {
1443 char *str;
1444 size_t len;
1445 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1446
1447 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1448
1449 php_http_version_to_string(&obj->message->http.version, &str, &len, NULL, NULL);
1450 RETURN_STR(php_http_cs2zs(str, len));
1451 }
1452 }
1453
1454 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setHttpVersion, 0, 0, 1)
1455 ZEND_ARG_INFO(0, http_version)
1456 ZEND_END_ARG_INFO();
1457 static PHP_METHOD(HttpMessage, setHttpVersion)
1458 {
1459 char *v_str;
1460 size_t v_len;
1461 php_http_version_t version;
1462 php_http_message_object_t *obj;
1463
1464 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &v_str, &v_len), invalid_arg, return);
1465
1466 obj = PHP_HTTP_OBJ(NULL, getThis());
1467 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1468
1469 php_http_expect(php_http_version_parse(&version, v_str), unexpected_val, return);
1470
1471 obj->message->http.version = version;
1472
1473 RETVAL_ZVAL(getThis(), 1, 0);
1474 }
1475
1476 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getResponseCode, 0, 0, 0)
1477 ZEND_END_ARG_INFO();
1478 static PHP_METHOD(HttpMessage, getResponseCode)
1479 {
1480 if (SUCCESS == zend_parse_parameters_none()) {
1481 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1482
1483 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1484
1485 if (obj->message->type != PHP_HTTP_RESPONSE) {
1486 php_error_docref(NULL, E_WARNING, "http\\Message is not of type response");
1487 RETURN_FALSE;
1488 }
1489
1490 RETURN_LONG(obj->message->http.info.response.code);
1491 }
1492 }
1493
1494 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setResponseCode, 0, 0, 1)
1495 ZEND_ARG_INFO(0, response_code)
1496 ZEND_ARG_INFO(0, strict)
1497 ZEND_END_ARG_INFO();
1498 static PHP_METHOD(HttpMessage, setResponseCode)
1499 {
1500 zend_long code;
1501 zend_bool strict = 1;
1502 php_http_message_object_t *obj;
1503
1504 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "l|b", &code, &strict), invalid_arg, return);
1505
1506 obj = PHP_HTTP_OBJ(NULL, getThis());
1507 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1508
1509 if (obj->message->type != PHP_HTTP_RESPONSE) {
1510 php_http_throw(bad_method_call, "http\\Message is not of type response");
1511 return;
1512 }
1513
1514 if (strict && (code < 100 || code > 599)) {
1515 php_http_throw(invalid_arg, "Invalid response code (100-599): %ld", code);
1516 return;
1517 }
1518
1519 obj->message->http.info.response.code = code;
1520 PTR_SET(obj->message->http.info.response.status, estrdup(php_http_env_get_response_status_for_code(code)));
1521
1522 RETVAL_ZVAL(getThis(), 1, 0);
1523 }
1524
1525 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getResponseStatus, 0, 0, 0)
1526 ZEND_END_ARG_INFO();
1527 static PHP_METHOD(HttpMessage, getResponseStatus)
1528 {
1529 if (SUCCESS == zend_parse_parameters_none()) {
1530 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1531
1532 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1533
1534 if (obj->message->type != PHP_HTTP_RESPONSE) {
1535 php_error_docref(NULL, E_WARNING, "http\\Message is not of type response");
1536 }
1537
1538 if (obj->message->http.info.response.status) {
1539 RETURN_STRING(obj->message->http.info.response.status);
1540 } else {
1541 RETURN_EMPTY_STRING();
1542 }
1543 }
1544 }
1545
1546 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setResponseStatus, 0, 0, 1)
1547 ZEND_ARG_INFO(0, response_status)
1548 ZEND_END_ARG_INFO();
1549 static PHP_METHOD(HttpMessage, setResponseStatus)
1550 {
1551 char *status;
1552 size_t status_len;
1553 php_http_message_object_t *obj;
1554
1555 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &status, &status_len), invalid_arg, return);
1556
1557 obj = PHP_HTTP_OBJ(NULL, getThis());
1558
1559 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1560
1561 if (obj->message->type != PHP_HTTP_RESPONSE) {
1562 php_http_throw(bad_method_call, "http\\Message is not of type response");
1563 }
1564
1565 PTR_SET(obj->message->http.info.response.status, estrndup(status, status_len));
1566 RETVAL_ZVAL(getThis(), 1, 0);
1567 }
1568
1569 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getRequestMethod, 0, 0, 0)
1570 ZEND_END_ARG_INFO();
1571 static PHP_METHOD(HttpMessage, getRequestMethod)
1572 {
1573 if (SUCCESS == zend_parse_parameters_none()) {
1574 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1575
1576 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1577
1578 if (obj->message->type != PHP_HTTP_REQUEST) {
1579 php_error_docref(NULL, E_WARNING, "http\\Message is not of type request");
1580 RETURN_FALSE;
1581 }
1582
1583 if (obj->message->http.info.request.method) {
1584 RETURN_STRING(obj->message->http.info.request.method);
1585 } else {
1586 RETURN_EMPTY_STRING();
1587 }
1588 }
1589 }
1590
1591 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setRequestMethod, 0, 0, 1)
1592 ZEND_ARG_INFO(0, request_method)
1593 ZEND_END_ARG_INFO();
1594 static PHP_METHOD(HttpMessage, setRequestMethod)
1595 {
1596 char *method;
1597 size_t method_len;
1598 php_http_message_object_t *obj;
1599
1600 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &method, &method_len), invalid_arg, return);
1601
1602 obj = PHP_HTTP_OBJ(NULL, getThis());
1603
1604 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1605
1606 if (obj->message->type != PHP_HTTP_REQUEST) {
1607 php_http_throw(bad_method_call, "http\\Message is not of type request");
1608 return;
1609 }
1610
1611 if (method_len < 1) {
1612 php_http_throw(invalid_arg, "Cannot set http\\Message's request method to an empty string");
1613 return;
1614 }
1615
1616 PTR_SET(obj->message->http.info.request.method, estrndup(method, method_len));
1617 RETVAL_ZVAL(getThis(), 1, 0);
1618 }
1619
1620 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getRequestUrl, 0, 0, 0)
1621 ZEND_END_ARG_INFO();
1622 static PHP_METHOD(HttpMessage, getRequestUrl)
1623 {
1624 if (SUCCESS == zend_parse_parameters_none()) {
1625 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1626
1627 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1628
1629 if (obj->message->type != PHP_HTTP_REQUEST) {
1630 php_error_docref(NULL, E_WARNING, "http\\Message is not of type request");
1631 RETURN_FALSE;
1632 }
1633
1634 if (obj->message->http.info.request.url) {
1635 char *url_str;
1636 size_t url_len;
1637
1638 php_http_url_to_string(obj->message->http.info.request.url, &url_str, &url_len, 0);
1639 RETURN_STR(php_http_cs2zs(url_str, url_len));
1640 } else {
1641 RETURN_EMPTY_STRING();
1642 }
1643 }
1644 }
1645
1646 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setRequestUrl, 0, 0, 1)
1647 ZEND_ARG_INFO(0, url)
1648 ZEND_END_ARG_INFO();
1649 static PHP_METHOD(HttpMessage, setRequestUrl)
1650 {
1651 zval *zurl;
1652 php_http_url_t *url;
1653 php_http_message_object_t *obj;
1654 zend_error_handling zeh;
1655
1656 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zurl), invalid_arg, return);
1657
1658 obj = PHP_HTTP_OBJ(NULL, getThis());
1659
1660 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1661
1662 if (obj->message->type != PHP_HTTP_REQUEST) {
1663 php_http_throw(bad_method_call, "http\\Message is not of type request");
1664 return;
1665 }
1666
1667 zend_replace_error_handling(EH_THROW, php_http_get_exception_bad_url_class_entry(), &zeh);
1668 url = php_http_url_from_zval(zurl, PHP_HTTP_URL_STDFLAGS);
1669 zend_restore_error_handling(&zeh);
1670
1671 if (url && php_http_url_is_empty(url)) {
1672 php_http_url_free(&url);
1673 php_http_throw(invalid_arg, "Cannot set http\\Message's request url to an empty string");
1674 } else if (url) {
1675 PTR_SET(obj->message->http.info.request.url, url);
1676 }
1677
1678 RETVAL_ZVAL(getThis(), 1, 0);
1679 }
1680
1681 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getParentMessage, 0, 0, 0)
1682 ZEND_END_ARG_INFO();
1683 static PHP_METHOD(HttpMessage, getParentMessage)
1684 {
1685 php_http_message_object_t *obj;
1686
1687 php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
1688
1689 obj = PHP_HTTP_OBJ(NULL, getThis());
1690 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1691
1692 if (!obj->message->parent) {
1693 php_http_throw(unexpected_val, "http\\Message has no parent message");
1694 return;
1695 }
1696
1697 RETVAL_OBJECT(&obj->parent->zo, 1);
1698 }
1699
1700 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage___toString, 0, 0, 0)
1701 ZEND_END_ARG_INFO();
1702 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_toString, 0, 0, 0)
1703 ZEND_ARG_INFO(0, include_parent)
1704 ZEND_END_ARG_INFO();
1705 static PHP_METHOD(HttpMessage, toString)
1706 {
1707 zend_bool include_parent = 0;
1708
1709 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &include_parent)) {
1710 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1711 char *string;
1712 size_t length;
1713
1714 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1715
1716 if (include_parent) {
1717 php_http_message_serialize(obj->message, &string, &length);
1718 } else {
1719 php_http_message_to_string(obj->message, &string, &length);
1720 }
1721 if (string) {
1722 RETURN_STR(php_http_cs2zs(string, length));
1723 }
1724 }
1725 RETURN_EMPTY_STRING();
1726 }
1727
1728 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_toStream, 0, 0, 1)
1729 ZEND_ARG_INFO(0, stream)
1730 ZEND_END_ARG_INFO();
1731 static PHP_METHOD(HttpMessage, toStream)
1732 {
1733 zval *zstream;
1734
1735 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zstream)) {
1736 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1737 php_stream *s;
1738
1739 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1740
1741 php_stream_from_zval(s, zstream);
1742 php_http_message_to_callback(obj->message, (php_http_pass_callback_t) _php_stream_write, s);
1743 }
1744 }
1745
1746 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_toCallback, 0, 0, 1)
1747 ZEND_ARG_INFO(0, callback)
1748 ZEND_END_ARG_INFO();
1749 static PHP_METHOD(HttpMessage, toCallback)
1750 {
1751 php_http_pass_fcall_arg_t fcd;
1752
1753 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "f", &fcd.fci, &fcd.fcc)) {
1754 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1755
1756 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1757
1758 ZVAL_COPY(&fcd.fcz, getThis());
1759 php_http_message_to_callback(obj->message, php_http_pass_fcall_callback, &fcd);
1760 zend_fcall_info_args_clear(&fcd.fci, 1);
1761 zval_ptr_dtor(&fcd.fcz);
1762
1763 RETURN_ZVAL(&fcd.fcz, 1, 0);
1764 }
1765 }
1766
1767 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(ai_HttpMessage___serialize, 0, 0, IS_ARRAY, 0)
1768 ZEND_END_ARG_INFO();
1769 static PHP_METHOD(HttpMessage, __serialize)
1770 {
1771 zend_ulong num_index;
1772 zend_string *str_index;
1773 zend_property_info *pi;
1774 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1775 HashTable *props = php_http_message_object_get_debug_info(&obj->zo, NULL);
1776
1777 zend_parse_parameters_none();
1778
1779 array_init(return_value);
1780
1781 ZEND_HASH_FOREACH_KEY_PTR(&obj->zo.ce->properties_info, num_index, str_index, pi)
1782 {
1783 zval *val;
1784 if (str_index && (val = zend_hash_find_ind(props, pi->name))) {
1785 Z_TRY_ADDREF_P(val);
1786 zend_hash_update(Z_ARRVAL_P(return_value), str_index, val);
1787 }
1788 }
1789 ZEND_HASH_FOREACH_END();
1790 }
1791
1792 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(ai_HttpMessage___unserialize, 0, 1, IS_VOID, 0)
1793 ZEND_ARG_TYPE_INFO(0, data, IS_ARRAY, 0)
1794 ZEND_END_ARG_INFO();
1795 static PHP_METHOD(HttpMessage, __unserialize)
1796 {
1797 HashTable *arr;
1798 zend_string *key;
1799 zval *val;
1800 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1801
1802 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "h", &arr), invalid_arg, return);
1803
1804 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1805
1806 ZEND_HASH_FOREACH_STR_KEY_VAL(arr, key, val)
1807 {
1808 php_http_message_object_prophandler_t *ph = php_http_message_object_get_prophandler(key);
1809 if (ph) {
1810 ph->write(obj, val);
1811 } else {
1812 zend_update_property_ex(php_http_message_class_entry, &obj->zo, key, val);
1813 }
1814 }
1815 ZEND_HASH_FOREACH_END();
1816 }
1817
1818 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_serialize, 0, 0, 0)
1819 ZEND_END_ARG_INFO();
1820 static PHP_METHOD(HttpMessage, serialize)
1821 {
1822 if (SUCCESS == zend_parse_parameters_none()) {
1823 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1824 char *string;
1825 size_t length;
1826
1827 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1828
1829 php_http_message_serialize(obj->message, &string, &length);
1830 RETURN_STR(php_http_cs2zs(string, length));
1831 }
1832 RETURN_EMPTY_STRING();
1833 }
1834
1835 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_unserialize, 0, 0, 1)
1836 ZEND_ARG_INFO(0, serialized)
1837 ZEND_END_ARG_INFO();
1838 static PHP_METHOD(HttpMessage, unserialize)
1839 {
1840 size_t length;
1841 char *serialized;
1842
1843 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &serialized, &length)) {
1844 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1845 php_http_message_t *msg;
1846
1847 if (obj->message) {
1848 /* do not free recursively */
1849 php_http_message_dtor(obj->message);
1850 efree(obj->message);
1851 }
1852 if ((msg = php_http_message_parse(NULL, serialized, length, 1))) {
1853 obj->message = msg;
1854 } else {
1855 obj->message = php_http_message_init(NULL, 0, NULL);
1856 php_error_docref(NULL, E_ERROR, "Could not unserialize http\\Message");
1857 }
1858 }
1859 }
1860
1861 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_detach, 0, 0, 0)
1862 ZEND_END_ARG_INFO();
1863 static PHP_METHOD(HttpMessage, detach)
1864 {
1865 php_http_message_object_t *obj, *new_obj;
1866 php_http_message_t *msg_cpy;
1867
1868 php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
1869
1870 obj = PHP_HTTP_OBJ(NULL, getThis());
1871 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1872
1873 msg_cpy = php_http_message_copy_ex(obj->message, NULL, 0);
1874 new_obj = php_http_message_object_new_ex(obj->zo.ce, msg_cpy);
1875
1876 RETVAL_OBJ(&new_obj->zo);
1877 }
1878
1879 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_prepend, 0, 0, 1)
1880 ZEND_ARG_OBJ_INFO(0, message, http\\Message, 0)
1881 ZEND_ARG_INFO(0, top)
1882 ZEND_END_ARG_INFO();
1883 static PHP_METHOD(HttpMessage, prepend)
1884 {
1885 zval *prepend;
1886 zend_bool top = 1;
1887 php_http_message_t *msg[2];
1888 php_http_message_object_t *obj, *prepend_obj;
1889
1890 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "O|b", &prepend, php_http_message_class_entry, &top), invalid_arg, return);
1891
1892 obj = PHP_HTTP_OBJ(NULL, getThis());
1893 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1894 prepend_obj = PHP_HTTP_OBJ(NULL, prepend);
1895 PHP_HTTP_MESSAGE_OBJECT_INIT(prepend_obj);
1896
1897 /* safety check */
1898 for (msg[0] = obj->message; msg[0]; msg[0] = msg[0]->parent) {
1899 for (msg[1] = prepend_obj->message; msg[1]; msg[1] = msg[1]->parent) {
1900 if (msg[0] == msg[1]) {
1901 php_http_throw(unexpected_val, "Cannot prepend a message located within the same message chain");
1902 return;
1903 }
1904 }
1905 }
1906
1907 php_http_message_object_prepend(getThis(), prepend, top);
1908 RETURN_ZVAL(getThis(), 1, 0);
1909 }
1910
1911 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_reverse, 0, 0, 0)
1912 ZEND_END_ARG_INFO();
1913 static PHP_METHOD(HttpMessage, reverse)
1914 {
1915 php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
1916
1917 php_http_message_object_reverse(getThis(), return_value);
1918 }
1919
1920 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_isMultipart, 0, 0, 0)
1921 ZEND_ARG_INFO(1, boundary)
1922 ZEND_END_ARG_INFO();
1923 static PHP_METHOD(HttpMessage, isMultipart)
1924 {
1925 zval *zboundary = NULL;
1926
1927 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|z!", &zboundary)) {
1928 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1929 char *boundary = NULL;
1930
1931 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1932
1933 if (php_http_message_is_multipart(obj->message, zboundary ? &boundary : NULL)) {
1934 RETVAL_TRUE;
1935 } else {
1936 RETVAL_FALSE;
1937 }
1938
1939 if (zboundary && boundary) {
1940 ZVAL_DEREF(zboundary);
1941 zval_dtor(zboundary);
1942 ZVAL_STR(zboundary, php_http_cs2zs(boundary, strlen(boundary)));
1943 }
1944 }
1945 }
1946
1947 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_splitMultipartBody, 0, 0, 0)
1948 ZEND_END_ARG_INFO();
1949 static PHP_METHOD(HttpMessage, splitMultipartBody)
1950 {
1951 php_http_message_object_t *obj;
1952 php_http_message_t *msg;
1953 char *boundary = NULL;
1954
1955 php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
1956
1957 obj = PHP_HTTP_OBJ(NULL, getThis());
1958 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1959
1960 if (!php_http_message_is_multipart(obj->message, &boundary)) {
1961 php_http_throw(bad_method_call, "http\\Message is not a multipart message");
1962 return;
1963 }
1964
1965 php_http_expect(msg = php_http_message_body_split(obj->message->body, boundary), bad_message, return);
1966
1967 PTR_FREE(boundary);
1968
1969 RETURN_OBJ(&php_http_message_object_new_ex(obj->zo.ce, msg)->zo);
1970 }
1971
1972 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(ai_HttpMessage_count, 0, 0, IS_LONG, 0)
1973 ZEND_END_ARG_INFO();
1974 static PHP_METHOD(HttpMessage, count)
1975 {
1976 zend_long count_mode = -1;
1977
1978 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &count_mode)) {
1979 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1980
1981 PHP_HTTP_MESSAGE_OBJECT_INIT(obj);
1982
1983 RETURN_LONG(php_http_message_count(obj->message));
1984 }
1985 }
1986
1987 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(ai_HttpMessage_rewind, 0, 0, IS_VOID, 0)
1988 ZEND_END_ARG_INFO();
1989 static PHP_METHOD(HttpMessage, rewind)
1990 {
1991 if (SUCCESS == zend_parse_parameters_none()) {
1992 zval *zobj = getThis();
1993 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
1994
1995 if (!Z_ISUNDEF(obj->iterator)) {
1996 zval_ptr_dtor(&obj->iterator);
1997 }
1998 ZVAL_COPY(&obj->iterator, zobj);
1999 }
2000 }
2001
2002 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(ai_HttpMessage_valid, 0, 0, _IS_BOOL, 0)
2003 ZEND_END_ARG_INFO();
2004 static PHP_METHOD(HttpMessage, valid)
2005 {
2006 if (SUCCESS == zend_parse_parameters_none()) {
2007 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
2008
2009 RETURN_BOOL(!Z_ISUNDEF(obj->iterator));
2010 }
2011 }
2012
2013 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(ai_HttpMessage_next, 0, 0, IS_VOID, 0)
2014 ZEND_END_ARG_INFO();
2015 static PHP_METHOD(HttpMessage, next)
2016 {
2017 if (SUCCESS == zend_parse_parameters_none()) {
2018 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
2019
2020 if (!Z_ISUNDEF(obj->iterator)) {
2021 php_http_message_object_t *itr = PHP_HTTP_OBJ(NULL, &obj->iterator);
2022
2023 if (itr->parent) {
2024 zval tmp;
2025
2026 ZVAL_OBJECT(&tmp, &itr->parent->zo, 1);
2027 zval_ptr_dtor(&obj->iterator);
2028 obj->iterator = tmp;
2029 } else {
2030 zval_ptr_dtor(&obj->iterator);
2031 ZVAL_UNDEF(&obj->iterator);
2032 }
2033 }
2034 }
2035 }
2036
2037 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(ai_HttpMessage_key, 0, 0, IS_LONG, 0)
2038 ZEND_END_ARG_INFO();
2039 static PHP_METHOD(HttpMessage, key)
2040 {
2041 if (SUCCESS == zend_parse_parameters_none()) {
2042 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
2043
2044 RETURN_LONG(Z_ISUNDEF(obj->iterator) ? 0 : Z_OBJ_HANDLE(obj->iterator));
2045 }
2046 }
2047
2048 ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(ai_HttpMessage_current, 0, 0, http\\Message, 0)
2049 ZEND_END_ARG_INFO();
2050 static PHP_METHOD(HttpMessage, current)
2051 {
2052 if (SUCCESS == zend_parse_parameters_none()) {
2053 php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis());
2054
2055 if (!Z_ISUNDEF(obj->iterator)) {
2056 RETURN_ZVAL(&obj->iterator, 1, 0);
2057 }
2058 }
2059 }
2060
2061 static zend_function_entry php_http_message_methods[] = {
2062 PHP_ME(HttpMessage, __construct, ai_HttpMessage___construct, ZEND_ACC_PUBLIC)
2063 PHP_ME(HttpMessage, getBody, ai_HttpMessage_getBody, ZEND_ACC_PUBLIC)
2064 PHP_ME(HttpMessage, setBody, ai_HttpMessage_setBody, ZEND_ACC_PUBLIC)
2065 PHP_ME(HttpMessage, addBody, ai_HttpMessage_addBody, ZEND_ACC_PUBLIC)
2066 PHP_ME(HttpMessage, getHeader, ai_HttpMessage_getHeader, ZEND_ACC_PUBLIC)
2067 PHP_ME(HttpMessage, setHeader, ai_HttpMessage_setHeader, ZEND_ACC_PUBLIC)
2068 PHP_ME(HttpMessage, addHeader, ai_HttpMessage_addHeader, ZEND_ACC_PUBLIC)
2069 PHP_ME(HttpMessage, getHeaders, ai_HttpMessage_getHeaders, ZEND_ACC_PUBLIC)
2070 PHP_ME(HttpMessage, setHeaders, ai_HttpMessage_setHeaders, ZEND_ACC_PUBLIC)
2071 PHP_ME(HttpMessage, addHeaders, ai_HttpMessage_addHeaders, ZEND_ACC_PUBLIC)
2072 PHP_ME(HttpMessage, getType, ai_HttpMessage_getType, ZEND_ACC_PUBLIC)
2073 PHP_ME(HttpMessage, setType, ai_HttpMessage_setType, ZEND_ACC_PUBLIC)
2074 PHP_ME(HttpMessage, getInfo, ai_HttpMessage_getInfo, ZEND_ACC_PUBLIC)
2075 PHP_ME(HttpMessage, setInfo, ai_HttpMessage_setInfo, ZEND_ACC_PUBLIC)
2076 PHP_ME(HttpMessage, getResponseCode, ai_HttpMessage_getResponseCode, ZEND_ACC_PUBLIC)
2077 PHP_ME(HttpMessage, setResponseCode, ai_HttpMessage_setResponseCode, ZEND_ACC_PUBLIC)
2078 PHP_ME(HttpMessage, getResponseStatus, ai_HttpMessage_getResponseStatus, ZEND_ACC_PUBLIC)
2079 PHP_ME(HttpMessage, setResponseStatus, ai_HttpMessage_setResponseStatus, ZEND_ACC_PUBLIC)
2080 PHP_ME(HttpMessage, getRequestMethod, ai_HttpMessage_getRequestMethod, ZEND_ACC_PUBLIC)
2081 PHP_ME(HttpMessage, setRequestMethod, ai_HttpMessage_setRequestMethod, ZEND_ACC_PUBLIC)
2082 PHP_ME(HttpMessage, getRequestUrl, ai_HttpMessage_getRequestUrl, ZEND_ACC_PUBLIC)
2083 PHP_ME(HttpMessage, setRequestUrl, ai_HttpMessage_setRequestUrl, ZEND_ACC_PUBLIC)
2084 PHP_ME(HttpMessage, getHttpVersion, ai_HttpMessage_getHttpVersion, ZEND_ACC_PUBLIC)
2085 PHP_ME(HttpMessage, setHttpVersion, ai_HttpMessage_setHttpVersion, ZEND_ACC_PUBLIC)
2086 PHP_ME(HttpMessage, getParentMessage, ai_HttpMessage_getParentMessage, ZEND_ACC_PUBLIC)
2087 PHP_ME(HttpMessage, toString, ai_HttpMessage_toString, ZEND_ACC_PUBLIC)
2088 PHP_ME(HttpMessage, toCallback, ai_HttpMessage_toCallback, ZEND_ACC_PUBLIC)
2089 PHP_ME(HttpMessage, toStream, ai_HttpMessage_toStream, ZEND_ACC_PUBLIC)
2090
2091 /* implements Countable */
2092 PHP_ME(HttpMessage, count, ai_HttpMessage_count, ZEND_ACC_PUBLIC)
2093
2094 /* implements Serializable */
2095 PHP_ME(HttpMessage, serialize, ai_HttpMessage_serialize, ZEND_ACC_PUBLIC)
2096 PHP_ME(HttpMessage, unserialize, ai_HttpMessage_unserialize, ZEND_ACC_PUBLIC)
2097 PHP_ME(HttpMessage, __serialize, ai_HttpMessage___serialize, ZEND_ACC_PUBLIC)
2098 PHP_ME(HttpMessage, __unserialize, ai_HttpMessage___unserialize, ZEND_ACC_PUBLIC)
2099
2100 /* implements Iterator */
2101 PHP_ME(HttpMessage, rewind, ai_HttpMessage_rewind, ZEND_ACC_PUBLIC)
2102 PHP_ME(HttpMessage, valid, ai_HttpMessage_valid, ZEND_ACC_PUBLIC)
2103 PHP_ME(HttpMessage, current, ai_HttpMessage_current, ZEND_ACC_PUBLIC)
2104 PHP_ME(HttpMessage, key, ai_HttpMessage_key, ZEND_ACC_PUBLIC)
2105 PHP_ME(HttpMessage, next, ai_HttpMessage_next, ZEND_ACC_PUBLIC)
2106
2107 ZEND_MALIAS(HttpMessage, __toString, toString, ai_HttpMessage___toString, ZEND_ACC_PUBLIC)
2108
2109 PHP_ME(HttpMessage, detach, ai_HttpMessage_detach, ZEND_ACC_PUBLIC)
2110 PHP_ME(HttpMessage, prepend, ai_HttpMessage_prepend, ZEND_ACC_PUBLIC)
2111 PHP_ME(HttpMessage, reverse, ai_HttpMessage_reverse, ZEND_ACC_PUBLIC)
2112
2113 PHP_ME(HttpMessage, isMultipart, ai_HttpMessage_isMultipart, ZEND_ACC_PUBLIC)
2114 PHP_ME(HttpMessage, splitMultipartBody, ai_HttpMessage_splitMultipartBody, ZEND_ACC_PUBLIC)
2115
2116 EMPTY_FUNCTION_ENTRY
2117 };
2118
2119 PHP_MINIT_FUNCTION(http_message)
2120 {
2121 zend_class_entry ce = {0};
2122
2123 INIT_NS_CLASS_ENTRY(ce, "http", "Message", php_http_message_methods);
2124 php_http_message_class_entry = zend_register_internal_class(&ce);
2125 php_http_message_class_entry->create_object = php_http_message_object_new;
2126 memcpy(&php_http_message_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
2127 php_http_message_object_handlers.offset = XtOffsetOf(php_http_message_object_t, zo);
2128 php_http_message_object_handlers.clone_obj = php_http_message_object_clone;
2129 php_http_message_object_handlers.free_obj = php_http_message_object_free;
2130 php_http_message_object_handlers.read_property = php_http_message_object_read_prop;
2131 php_http_message_object_handlers.write_property = php_http_message_object_write_prop;
2132 php_http_message_object_handlers.get_debug_info = php_http_message_object_get_debug_info;
2133 php_http_message_object_handlers.get_property_ptr_ptr = php_http_message_object_get_prop_ptr;
2134 php_http_message_object_handlers.get_gc = php_http_message_object_get_gc;
2135 php_http_message_object_handlers.cast_object = php_http_message_object_cast;
2136
2137 zend_class_implements(php_http_message_class_entry, 3, zend_ce_countable, zend_ce_serializable, zend_ce_iterator);
2138
2139 zend_hash_init(&php_http_message_object_prophandlers, 9, NULL, php_http_message_object_prophandler_hash_dtor, 1);
2140 zend_declare_property_long(php_http_message_class_entry, ZEND_STRL("type"), PHP_HTTP_NONE, ZEND_ACC_PROTECTED);
2141 php_http_message_object_add_prophandler(ZEND_STRL("type"), php_http_message_object_prophandler_get_type, php_http_message_object_prophandler_set_type);
2142 zend_declare_property_null(php_http_message_class_entry, ZEND_STRL("body"), ZEND_ACC_PROTECTED);
2143 php_http_message_object_add_prophandler(ZEND_STRL("body"), php_http_message_object_prophandler_get_body, php_http_message_object_prophandler_set_body);
2144 zend_declare_property_string(php_http_message_class_entry, ZEND_STRL("requestMethod"), "", ZEND_ACC_PROTECTED);
2145 php_http_message_object_add_prophandler(ZEND_STRL("requestMethod"), php_http_message_object_prophandler_get_request_method, php_http_message_object_prophandler_set_request_method);
2146 zend_declare_property_string(php_http_message_class_entry, ZEND_STRL("requestUrl"), "", ZEND_ACC_PROTECTED);
2147 php_http_message_object_add_prophandler(ZEND_STRL("requestUrl"), php_http_message_object_prophandler_get_request_url, php_http_message_object_prophandler_set_request_url);
2148 zend_declare_property_string(php_http_message_class_entry, ZEND_STRL("responseStatus"), "", ZEND_ACC_PROTECTED);
2149 php_http_message_object_add_prophandler(ZEND_STRL("responseStatus"), php_http_message_object_prophandler_get_response_status, php_http_message_object_prophandler_set_response_status);
2150 zend_declare_property_long(php_http_message_class_entry, ZEND_STRL("responseCode"), 0, ZEND_ACC_PROTECTED);
2151 php_http_message_object_add_prophandler(ZEND_STRL("responseCode"), php_http_message_object_prophandler_get_response_code, php_http_message_object_prophandler_set_response_code);
2152 zend_declare_property_null(php_http_message_class_entry, ZEND_STRL("httpVersion"), ZEND_ACC_PROTECTED);
2153 php_http_message_object_add_prophandler(ZEND_STRL("httpVersion"), php_http_message_object_prophandler_get_http_version, php_http_message_object_prophandler_set_http_version);
2154 zend_declare_property_null(php_http_message_class_entry, ZEND_STRL("headers"), ZEND_ACC_PROTECTED);
2155 php_http_message_object_add_prophandler(ZEND_STRL("headers"), php_http_message_object_prophandler_get_headers, php_http_message_object_prophandler_set_headers);
2156 zend_declare_property_null(php_http_message_class_entry, ZEND_STRL("parentMessage"), ZEND_ACC_PROTECTED);
2157 php_http_message_object_add_prophandler(ZEND_STRL("parentMessage"), php_http_message_object_prophandler_get_parent_message, php_http_message_object_prophandler_set_parent_message);
2158
2159 zend_declare_class_constant_long(php_http_message_class_entry, ZEND_STRL("TYPE_NONE"), PHP_HTTP_NONE);
2160 zend_declare_class_constant_long(php_http_message_class_entry, ZEND_STRL("TYPE_REQUEST"), PHP_HTTP_REQUEST);
2161 zend_declare_class_constant_long(php_http_message_class_entry, ZEND_STRL("TYPE_RESPONSE"), PHP_HTTP_RESPONSE);
2162
2163 return SUCCESS;
2164 }
2165
2166 PHP_MSHUTDOWN_FUNCTION(http_message)
2167 {
2168 zend_hash_destroy(&php_http_message_object_prophandlers);
2169
2170 return SUCCESS;
2171 }
2172
2173 /*
2174 * Local variables:
2175 * tab-width: 4
2176 * c-basic-offset: 4
2177 * End:
2178 * vim600: noet sw=4 ts=4 fdm=marker
2179 * vim<600: noet sw=4 ts=4
2180 */