87689da915717e5b8118d7f1ce3df5fb0e1ef684
[m6w6/ext-http] / http_message_api.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-2006, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 /* $Id$ */
14
15 #define HTTP_WANT_SAPI
16 #define HTTP_WANT_CURL
17 #define HTTP_WANT_ZLIB
18 #include "php_http.h"
19
20 #include "php_http_api.h"
21 #include "php_http_encoding_api.h"
22 #include "php_http_headers_api.h"
23 #include "php_http_message_api.h"
24 #include "php_http_request_api.h"
25 #include "php_http_send_api.h"
26 #include "php_http_url_api.h"
27
28 #define http_message_info_callback _http_message_info_callback
29 static void _http_message_info_callback(http_message **message, HashTable **headers, http_info *info TSRMLS_DC)
30 {
31 http_message *old = *message;
32
33 /* advance message */
34 if (old->type || zend_hash_num_elements(&old->hdrs) || PHPSTR_LEN(old)) {
35 (*message) = http_message_new();
36 (*message)->parent = old;
37 (*headers) = &((*message)->hdrs);
38 }
39
40 http_message_set_info(*message, info);
41 }
42
43 #define http_message_init_type _http_message_init_type
44 static inline void _http_message_init_type(http_message *message, http_message_type type)
45 {
46 message->http.version = .0;
47
48 switch (message->type = type) {
49 case HTTP_MSG_RESPONSE:
50 message->http.info.response.code = 0;
51 message->http.info.response.status = NULL;
52 break;
53
54 case HTTP_MSG_REQUEST:
55 message->http.info.request.method = NULL;
56 message->http.info.request.url = NULL;
57 break;
58
59 case HTTP_MSG_NONE:
60 default:
61 break;
62 }
63 }
64
65 PHP_HTTP_API http_message *_http_message_init_ex(http_message *message, http_message_type type ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
66 {
67 if (!message) {
68 message = ecalloc_rel(1, sizeof(http_message));
69 }
70
71 http_message_init_type(message, type);
72 message->parent = NULL;
73 phpstr_init(&message->body);
74 zend_hash_init(&message->hdrs, 0, NULL, ZVAL_PTR_DTOR, 0);
75
76 return message;
77 }
78
79 PHP_HTTP_API http_message *_http_message_init_env(http_message *message, http_message_type type TSRMLS_DC ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
80 {
81 int free_msg;
82 http_info inf;
83 zval *sval, tval;
84 char *body_str;
85 size_t body_len;
86
87 if ((free_msg = !message)) {
88 message = http_message_init_rel(NULL, HTTP_MSG_NONE);
89 }
90
91 memset(&inf, 0, sizeof(http_info));
92 switch (inf.type = type) {
93 case HTTP_MSG_REQUEST:
94 if ((sval = http_get_server_var("SERVER_PROTOCOL", 1)) && !strncmp(Z_STRVAL_P(sval), ZEND_STRL("HTTP/"))) {
95 inf.http.version = atof(Z_STRVAL_P(sval) + lenof("HTTP/"));
96 } else {
97 inf.http.version = 1.1;
98 }
99 if ((sval = http_get_server_var("REQUEST_METHOD", 1))) {
100 inf.http.info.request.method = estrdup(Z_STRVAL_P(sval));
101 } else {
102 inf.http.info.request.method = ecalloc(1, 1);
103 }
104 if ((sval = http_get_server_var("REQUEST_URI", 1))) {
105 inf.http.info.request.url = estrdup(Z_STRVAL_P(sval));
106 } else {
107 inf.http.info.request.url = ecalloc(1, 1);
108 }
109
110 http_message_set_info(message, &inf);
111 http_get_request_headers(&message->hdrs);
112 if (SUCCESS == http_get_request_body_ex(&body_str, &body_len, 0)) {
113 phpstr_from_string_ex(&message->body, body_str, body_len);
114 }
115 break;
116
117 case HTTP_MSG_RESPONSE:
118 if (!SG(sapi_headers).http_status_line || SUCCESS != http_info_parse_ex(SG(sapi_headers).http_status_line, &inf, 0)) {
119 inf.http.version = 1.1;
120 inf.http.info.response.code = 200;
121 inf.http.info.response.status = estrdup("Ok");
122 }
123
124 http_message_set_info(message, &inf);
125 http_get_response_headers(&message->hdrs);
126 if (SUCCESS == php_ob_get_buffer(&tval TSRMLS_CC)) {
127 message->body.data = Z_STRVAL(tval);
128 message->body.used = Z_STRLEN(tval);
129 message->body.free = 1; /* "\0" */
130 }
131 break;
132
133 default:
134 if (free_msg) {
135 http_message_free(&message);
136 } else {
137 message = NULL;
138 }
139 break;
140 }
141 http_info_dtor(&inf);
142
143 return message;
144 }
145
146 PHP_HTTP_API void _http_message_set_type(http_message *message, http_message_type type)
147 {
148 /* just act if different */
149 if (type != message->type) {
150
151 /* free request info */
152 switch (message->type) {
153 case HTTP_MSG_REQUEST:
154 STR_FREE(message->http.info.request.method);
155 STR_FREE(message->http.info.request.url);
156 break;
157
158 case HTTP_MSG_RESPONSE:
159 STR_FREE(message->http.info.response.status);
160 break;
161
162 default:
163 break;
164 }
165
166 /* init */
167 http_message_init_type(message, type);
168 }
169 }
170
171 PHP_HTTP_API void _http_message_set_info(http_message *message, http_info *info)
172 {
173 message->http.version = info->http.version;
174 http_message_set_type(message, info->type);
175 switch (message->type) {
176 case IS_HTTP_REQUEST:
177 HTTP_INFO(message).request.url = estrdup(HTTP_INFO(info).request.url);
178 STR_SET(HTTP_INFO(message).request.method, estrdup(HTTP_INFO(info).request.method));
179 break;
180
181 case IS_HTTP_RESPONSE:
182 HTTP_INFO(message).response.code = HTTP_INFO(info).response.code;
183 STR_SET(HTTP_INFO(message).response.status, estrdup(HTTP_INFO(info).response.status));
184 break;
185
186 default:
187 break;
188 }
189 }
190
191 #define http_message_body_parse(m, ms, ml, c) _http_message_body_parse((m), (ms), (ml), (c) TSRMLS_CC)
192 static inline void _http_message_body_parse(http_message *msg, const char *message, size_t message_length, const char **continue_at TSRMLS_DC)
193 {
194 zval *c;
195 size_t remaining;
196 const char *body;
197
198 *continue_at = NULL;
199 if ((body = http_locate_body(message))) {
200 remaining = message + message_length - body;
201
202 if ((c = http_message_header(msg, "Transfer-Encoding"))) {
203 if (strstr(Z_STRVAL_P(c), "chunked")) {
204 /* message has chunked transfer encoding */
205 char *decoded;
206 size_t decoded_len;
207
208 /* decode and replace Transfer-Encoding with Content-Length header */
209 if ((*continue_at = http_encoding_dechunk(body, message + message_length - body, &decoded, &decoded_len))) {
210 zval *len;
211 char *tmp;
212 int tmp_len;
213
214 tmp_len = (int) spprintf(&tmp, 0, "%zu", decoded_len);
215 MAKE_STD_ZVAL(len);
216 ZVAL_STRINGL(len, tmp, tmp_len, 0);
217
218 ZVAL_ADDREF(c);
219 zend_hash_update(&msg->hdrs, "X-Original-Transfer-Encoding", sizeof("X-Original-Transfer-Encoding"), (void *) &c, sizeof(zval *), NULL);
220 zend_hash_del(&msg->hdrs, "Transfer-Encoding", sizeof("Transfer-Encoding"));
221 zend_hash_del(&msg->hdrs, "Content-Length", sizeof("Content-Length"));
222 zend_hash_update(&msg->hdrs, "Content-Length", sizeof("Content-Length"), (void *) &len, sizeof(zval *), NULL);
223
224 phpstr_from_string_ex(PHPSTR(msg), decoded, decoded_len);
225 efree(decoded);
226 }
227 }
228 zval_ptr_dtor(&c);
229 }
230
231 if (!*continue_at && (c = http_message_header(msg, "Content-Length"))) {
232 /* message has content-length header */
233 ulong len = strtoul(Z_STRVAL_P(c), NULL, 10);
234 if (len > remaining) {
235 http_error_ex(HE_NOTICE, HTTP_E_MALFORMED_HEADERS, "The Content-Length header pretends a larger body than actually received (expected %lu bytes; got %lu bytes)", len, remaining);
236 len = remaining;
237 }
238 phpstr_from_string_ex(PHPSTR(msg), body, len);
239 *continue_at = body + len;
240 zval_ptr_dtor(&c);
241 }
242
243 if (!*continue_at && (c = http_message_header(msg, "Content-Range"))) {
244 /* message has content-range header */
245 ulong total = 0, start = 0, end = 0, len = 0;
246
247 if (!strncasecmp(Z_STRVAL_P(c), "bytes", lenof("bytes")) &&
248 ( Z_STRVAL_P(c)[lenof("bytes")] == ':' ||
249 Z_STRVAL_P(c)[lenof("bytes")] == ' ' ||
250 Z_STRVAL_P(c)[lenof("bytes")] == '=')) {
251 char *total_at = NULL, *end_at = NULL;
252 char *start_at = Z_STRVAL_P(c) + sizeof("bytes");
253
254 start = strtoul(start_at, &end_at, 10);
255 if (end_at) {
256 end = strtoul(end_at + 1, &total_at, 10);
257 if (total_at && strncmp(total_at + 1, "*", 1)) {
258 total = strtoul(total_at + 1, NULL, 10);
259 }
260 if ((len = (end + 1 - start)) > remaining) {
261 http_error_ex(HE_NOTICE, HTTP_E_MALFORMED_HEADERS, "The Content-Range header pretends a larger body than actually received (expected %lu bytes; got %lu bytes)", len, remaining);
262 len = remaining;
263 }
264 if (end >= start && (!total || end < total)) {
265 phpstr_from_string_ex(PHPSTR(msg), body, len);
266 *continue_at = body + len;
267 }
268 }
269 }
270
271 if (!*continue_at) {
272 http_error_ex(HE_WARNING, HTTP_E_MALFORMED_HEADERS, "Invalid Content-Range header: %s", Z_STRVAL_P(c));
273 }
274 zval_ptr_dtor(&c);
275 }
276
277 if (!*continue_at) {
278 /* no headers that indicate content length */
279 if (HTTP_MSG_TYPE(RESPONSE, msg)) {
280 phpstr_from_string_ex(PHPSTR(msg), body, remaining);
281 } else {
282 *continue_at = body;
283 }
284 }
285
286 #ifdef HTTP_HAVE_ZLIB
287 /* check for compressed data */
288 if ((c = http_message_header(msg, "Vary"))) {
289 zval_ptr_dtor(&c);
290
291 if ((c = http_message_header(msg, "Content-Encoding"))) {
292 char *decoded = NULL;
293 size_t decoded_len = 0;
294
295 if ( !strcasecmp(Z_STRVAL_P(c), "gzip") ||
296 !strcasecmp(Z_STRVAL_P(c), "x-gzip") ||
297 !strcasecmp(Z_STRVAL_P(c), "deflate")) {
298 http_encoding_inflate(PHPSTR_VAL(msg), PHPSTR_LEN(msg), &decoded, &decoded_len);
299 }
300
301 if (decoded) {
302 zval *len, **original_len;
303 char *tmp;
304 int tmp_len;
305
306 tmp_len = (int) spprintf(&tmp, 0, "%zu", decoded_len);
307 MAKE_STD_ZVAL(len);
308 ZVAL_STRINGL(len, tmp, tmp_len, 0);
309
310 ZVAL_ADDREF(c);
311 zend_hash_update(&msg->hdrs, "X-Original-Content-Encoding", sizeof("X-Original-Content-Encoding"), (void *) &c, sizeof(zval *), NULL);
312 zend_hash_del(&msg->hdrs, "Content-Encoding", sizeof("Content-Encoding"));
313 if (SUCCESS == zend_hash_find(&msg->hdrs, "Content-Length", sizeof("Content-Length"), (void *) &original_len)) {
314 ZVAL_ADDREF(*original_len);
315 zend_hash_update(&msg->hdrs, "X-Original-Content-Length", sizeof("X-Original-Content-Length"), (void *) original_len, sizeof(zval *), NULL);
316 zend_hash_update(&msg->hdrs, "Content-Length", sizeof("Content-Length"), (void *) &len, sizeof(zval *), NULL);
317 } else {
318 zend_hash_update(&msg->hdrs, "Content-Length", sizeof("Content-Length"), (void *) &len, sizeof(zval *), NULL);
319 }
320
321 phpstr_dtor(PHPSTR(msg));
322 PHPSTR(msg)->data = decoded;
323 PHPSTR(msg)->used = decoded_len;
324 PHPSTR(msg)->free = 1;
325 }
326
327 zval_ptr_dtor(&c);
328 }
329 }
330 #endif /* HTTP_HAVE_ZLIB */
331 }
332 }
333
334 PHP_HTTP_API http_message *_http_message_parse_ex(http_message *msg, const char *message, size_t message_length ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC)
335 {
336 const char *continue_at;
337 zend_bool free_msg = msg ? 0 : 1;
338
339 if ((!message) || (message_length < HTTP_MSG_MIN_SIZE)) {
340 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Empty or too short HTTP message: '%s'", message);
341 return NULL;
342 }
343
344 msg = http_message_init_rel(msg, 0);
345
346 if (SUCCESS != http_parse_headers_cb(message, &msg->hdrs, 1, (http_info_callback) http_message_info_callback, (void *) &msg)) {
347 if (free_msg) {
348 http_message_free(&msg);
349 }
350 http_error(HE_WARNING, HTTP_E_MALFORMED_HEADERS, "Failed to parse message headers");
351 return NULL;
352 }
353
354 http_message_body_parse(msg, message, message_length, &continue_at);
355
356 /* check for following messages */
357 if (continue_at && (continue_at < (message + message_length))) {
358 while (HTTP_IS_CTYPE(space, *continue_at)) ++continue_at;
359 if (continue_at < (message + message_length)) {
360 http_message *next = NULL, *most = NULL;
361
362 /* set current message to parent of most parent following messages and return deepest */
363 if ((most = next = http_message_parse_rel(NULL, continue_at, message + message_length - continue_at))) {
364 while (most->parent) most = most->parent;
365 most->parent = msg;
366 msg = next;
367 }
368 }
369 }
370
371 return msg;
372 }
373
374 PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_t *length)
375 {
376 phpstr str;
377 HashKey key = initHashKey(0);
378 zval **header;
379 char *data;
380 HashPosition pos1;
381
382 phpstr_init_ex(&str, 4096, 0);
383
384 switch (msg->type) {
385 case HTTP_MSG_REQUEST:
386 phpstr_appendf(&str, "%s %s HTTP/%1.1f" HTTP_CRLF,
387 msg->http.info.request.method?msg->http.info.request.method:"UNKNOWN",
388 msg->http.info.request.url?msg->http.info.request.url:"/",
389 msg->http.version);
390 break;
391
392 case HTTP_MSG_RESPONSE:
393 phpstr_appendf(&str, "HTTP/%1.1f %d%s%s" HTTP_CRLF,
394 msg->http.version,
395 msg->http.info.response.code,
396 msg->http.info.response.status&&*msg->http.info.response.status ? " ":"",
397 STR_PTR(msg->http.info.response.status));
398 break;
399
400 case HTTP_MSG_NONE:
401 default:
402 break;
403 }
404
405 FOREACH_HASH_KEYVAL(pos1, &msg->hdrs, key, header) {
406 if (key.type == HASH_KEY_IS_STRING) {
407 HashPosition pos2;
408 zval **single_header;
409
410 switch (Z_TYPE_PP(header)) {
411 case IS_STRING:
412 phpstr_appendf(&str, "%s: %s" HTTP_CRLF, key.str, Z_STRVAL_PP(header));
413 break;
414
415 case IS_ARRAY:
416 FOREACH_VAL(pos2, *header, single_header) {
417 phpstr_appendf(&str, "%s: %s" HTTP_CRLF, key.str, Z_STRVAL_PP(single_header));
418 }
419 break;
420 }
421 }
422 }
423
424 if (PHPSTR_LEN(msg)) {
425 phpstr_appends(&str, HTTP_CRLF);
426 phpstr_append(&str, PHPSTR_VAL(msg), PHPSTR_LEN(msg));
427 phpstr_appends(&str, HTTP_CRLF);
428 }
429
430 data = phpstr_data(&str, string, length);
431 if (!string) {
432 efree(data);
433 }
434
435 phpstr_dtor(&str);
436 }
437
438 PHP_HTTP_API void _http_message_serialize(http_message *message, char **string, size_t *length)
439 {
440 char *buf;
441 size_t len;
442 phpstr str;
443
444 phpstr_init(&str);
445
446 do {
447 http_message_tostring(message, &buf, &len);
448 phpstr_prepend(&str, buf, len);
449 efree(buf);
450 } while ((message = message->parent));
451
452 buf = phpstr_data(&str, string, length);
453 if (!string) {
454 efree(buf);
455 }
456
457 phpstr_dtor(&str);
458 }
459
460 PHP_HTTP_API http_message *_http_message_reverse(http_message *msg)
461 {
462 int i, c;
463
464 http_message_count(c, msg);
465
466 if (c > 1) {
467 http_message *tmp = msg, **arr = ecalloc(c, sizeof(http_message *));
468
469 for (i = 0; i < c; ++i) {
470 arr[i] = tmp;
471 tmp = tmp->parent;
472 }
473 arr[0]->parent = NULL;
474 for (i = 0; i < c-1; ++i) {
475 arr[i+1]->parent = arr[i];
476 }
477
478 msg = arr[c-1];
479 efree(arr);
480 }
481
482 return msg;
483 }
484
485 PHP_HTTP_API http_message *_http_message_interconnect(http_message *m1, http_message *m2)
486 {
487 if (m1 && m2) {
488 int i = 0, c1, c2;
489 http_message *t1 = m1, *t2 = m2, *p1, *p2;
490
491 http_message_count(c1, m1);
492 http_message_count(c2, m2);
493
494 while (i++ < (c1 - c2)) {
495 t1 = t1->parent;
496 }
497 while (i++ <= c1) {
498 p1 = t1->parent;
499 p2 = t2->parent;
500 t1->parent = t2;
501 t2->parent = p1;
502 t1 = p1;
503 t2 = p2;
504 }
505 } else if (!m1 && m2) {
506 m1 = m2;
507 }
508 return m1;
509 }
510
511 PHP_HTTP_API void _http_message_tostruct_recursive(http_message *msg, zval *obj TSRMLS_DC)
512 {
513 zval strct;
514 zval *headers;
515
516 INIT_ZARR(strct, HASH_OF(obj));
517
518 add_assoc_long(&strct, "type", msg->type);
519 add_assoc_double(&strct, "httpVersion", msg->http.version);
520 switch (msg->type)
521 {
522 case HTTP_MSG_RESPONSE:
523 add_assoc_long(&strct, "responseCode", msg->http.info.response.code);
524 add_assoc_string(&strct, "responseStatus", msg->http.info.response.status, 1);
525 break;
526
527 case HTTP_MSG_REQUEST:
528 add_assoc_string(&strct, "requestMethod", msg->http.info.request.method, 1);
529 add_assoc_string(&strct, "requestUrl", msg->http.info.request.url, 1);
530 break;
531
532 case HTTP_MSG_NONE:
533 /* avoid compiler warning */
534 break;
535 }
536
537 MAKE_STD_ZVAL(headers);
538 array_init(headers);
539 zend_hash_copy(Z_ARRVAL_P(headers), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
540 add_assoc_zval(&strct, "headers", headers);
541
542 add_assoc_stringl(&strct, "body", PHPSTR_VAL(msg), PHPSTR_LEN(msg), 1);
543
544 if (msg->parent) {
545 zval *parent;
546
547 MAKE_STD_ZVAL(parent);
548 if (Z_TYPE_P(obj) == IS_ARRAY) {
549 array_init(parent);
550 } else {
551 object_init(parent);
552 }
553 add_assoc_zval(&strct, "parentMessage", parent);
554 http_message_tostruct_recursive(msg->parent, parent);
555 } else {
556 add_assoc_null(&strct, "parentMessage");
557 }
558 }
559
560 PHP_HTTP_API STATUS _http_message_send(http_message *message TSRMLS_DC)
561 {
562 STATUS rs = FAILURE;
563
564 switch (message->type) {
565 case HTTP_MSG_RESPONSE:
566 {
567 HashKey key = initHashKey(0);
568 zval **val;
569 HashPosition pos;
570
571 FOREACH_HASH_KEYVAL(pos, &message->hdrs, key, val) {
572 if (key.type == HASH_KEY_IS_STRING) {
573 http_send_header_zval_ex(key.str, key.len-1, val, 1);
574 }
575 }
576 rs = SUCCESS == http_send_status(message->http.info.response.code) &&
577 SUCCESS == http_send_data(PHPSTR_VAL(message), PHPSTR_LEN(message)) ?
578 SUCCESS : FAILURE;
579 break;
580 }
581
582 case HTTP_MSG_REQUEST:
583 {
584 #ifdef HTTP_HAVE_CURL
585 char *uri = NULL;
586 http_request request;
587 zval **zhost, options, headers;
588
589 INIT_PZVAL(&options);
590 INIT_PZVAL(&headers);
591 array_init(&options);
592 array_init(&headers);
593 zend_hash_copy(Z_ARRVAL(headers), &message->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
594 add_assoc_zval(&options, "headers", &headers);
595
596 /* check host header */
597 if (SUCCESS == zend_hash_find(&message->hdrs, "Host", sizeof("Host"), (void *) &zhost)) {
598 char *colon = NULL;
599 php_url parts, *url = php_url_parse(message->http.info.request.url);
600
601 memset(&parts, 0, sizeof(php_url));
602
603 /* check for port */
604 if ((colon = strchr(Z_STRVAL_PP(zhost), ':'))) {
605 parts.port = atoi(colon + 1);
606 parts.host = estrndup(Z_STRVAL_PP(zhost), (Z_STRVAL_PP(zhost) - colon - 1));
607 } else {
608 parts.host = estrndup(Z_STRVAL_PP(zhost), Z_STRLEN_PP(zhost));
609 }
610
611 http_build_url(HTTP_URL_REPLACE, url, &parts, NULL, &uri, NULL);
612 php_url_free(url);
613 efree(parts.host);
614 } else {
615 uri = http_absolute_url(message->http.info.request.url);
616 }
617
618 if ((request.meth = http_request_method_exists(1, 0, message->http.info.request.method))) {
619 http_request_body body;
620
621 http_request_init_ex(&request, NULL, request.meth, uri);
622 request.body = http_request_body_init_ex(&body, HTTP_REQUEST_BODY_CSTRING, PHPSTR_VAL(message), PHPSTR_LEN(message), 0);
623 if (SUCCESS == (rs = http_request_prepare(&request, NULL))) {
624 http_request_exec(&request);
625 }
626 http_request_dtor(&request);
627 } else {
628 http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD,
629 "Cannot send HttpMessage. Request method %s not supported",
630 message->http.info.request.method);
631 }
632 efree(uri);
633 #else
634 http_error(HE_WARNING, HTTP_E_RUNTIME, "HTTP requests not supported - ext/http was not linked against libcurl.");
635 #endif
636 break;
637 }
638
639 case HTTP_MSG_NONE:
640 default:
641 http_error(HE_WARNING, HTTP_E_MESSAGE_TYPE, "HttpMessage is neither of type HTTP_MSG_REQUEST nor HTTP_MSG_RESPONSE");
642 break;
643 }
644
645 return rs;
646 }
647
648 PHP_HTTP_API http_message *_http_message_dup(http_message *orig TSRMLS_DC)
649 {
650 http_message *temp, *copy = NULL;
651 http_info info;
652
653 if (orig) {
654 info.type = orig->type;
655 info.http = orig->http;
656
657 copy = temp = http_message_new();
658 http_message_set_info(temp, &info);
659 zend_hash_copy(&temp->hdrs, &orig->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
660 phpstr_append(&temp->body, orig->body.data, orig->body.used);
661
662 while (orig->parent) {
663 info.type = orig->parent->type;
664 info.http = orig->parent->http;
665
666 temp->parent = http_message_new();
667 http_message_set_info(temp->parent, &info);
668 zend_hash_copy(&temp->parent->hdrs, &orig->parent->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
669 phpstr_append(&temp->parent->body, orig->parent->body.data, orig->parent->body.used);
670
671 temp = temp->parent;
672 orig = orig->parent;
673 }
674 }
675
676 return copy;
677 }
678
679 PHP_HTTP_API void _http_message_dtor(http_message *message)
680 {
681 if (message) {
682 zend_hash_destroy(&message->hdrs);
683 phpstr_dtor(PHPSTR(message));
684
685 switch (message->type) {
686 case HTTP_MSG_REQUEST:
687 STR_SET(message->http.info.request.method, NULL);
688 STR_SET(message->http.info.request.url, NULL);
689 break;
690
691 case HTTP_MSG_RESPONSE:
692 STR_SET(message->http.info.response.status, NULL);
693 break;
694
695 default:
696 break;
697 }
698 }
699 }
700
701 PHP_HTTP_API void _http_message_free(http_message **message)
702 {
703 if (*message) {
704 if ((*message)->parent) {
705 http_message_free(&(*message)->parent);
706 }
707 http_message_dtor(*message);
708 efree(*message);
709 *message = NULL;
710 }
711 }
712
713 /*
714 * Local variables:
715 * tab-width: 4
716 * c-basic-offset: 4
717 * End:
718 * vim600: noet sw=4 ts=4 fdm=marker
719 * vim<600: noet sw=4 ts=4
720 */
721