2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.0 of the PHP license, that |
6 | is bundled with this package in the file LICENSE, and is available |
7 | through the world-wide-web at http://www.php.net/license/3_0.txt. |
8 | If you did not receive a copy of the PHP license and are unable to |
9 | obtain it through the world-wide-web, please send a note to |
10 | license@php.net so we can mail you a copy immediately. |
11 +----------------------------------------------------------------------+
12 | Copyright (c) 2004-2005 Michael Wallner <mike@php.net> |
13 +----------------------------------------------------------------------+
24 #include "php_http_std_defs.h"
25 #include "php_http_api.h"
26 #include "php_http_message_api.h"
27 #include "php_http_headers_api.h"
28 #include "php_http_send_api.h"
29 #include "php_http_request_api.h"
30 #include "php_http_url_api.h"
32 #include "phpstr/phpstr.h"
34 ZEND_EXTERN_MODULE_GLOBALS(http
);
36 #define http_message_info_callback _http_message_info_callback
37 static void _http_message_info_callback(http_message
**message
, HashTable
**headers
, http_info
*info TSRMLS_DC
)
39 http_message
*old
= *message
;
42 if (old
->type
|| zend_hash_num_elements(&old
->hdrs
) || PHPSTR_LEN(old
)) {
43 (*message
) = http_message_new();
44 (*message
)->parent
= old
;
45 (*headers
) = &((*message
)->hdrs
);
48 (*message
)->http
.version
= info
->http
.version
;
53 (*message
)->type
= HTTP_MSG_REQUEST
;
54 HTTP_INFO(*message
).request
.URI
= estrdup(HTTP_INFO(info
).request
.URI
);
55 HTTP_INFO(*message
).request
.method
= estrdup(HTTP_INFO(info
).request
.method
);
58 case IS_HTTP_RESPONSE
:
59 (*message
)->type
= HTTP_MSG_RESPONSE
;
60 HTTP_INFO(*message
).response
.code
= HTTP_INFO(info
).response
.code
;
61 HTTP_INFO(*message
).response
.status
= estrdup(HTTP_INFO(info
).response
.status
);
66 #define http_message_init_type _http_message_init_type
67 static inline void _http_message_init_type(http_message
*message
, http_message_type type
)
69 message
->http
.version
= .0;
71 switch (message
->type
= type
)
73 case HTTP_MSG_RESPONSE
:
74 message
->http
.info
.response
.code
= 0;
75 message
->http
.info
.response
.status
= NULL
;
78 case HTTP_MSG_REQUEST
:
79 message
->http
.info
.request
.method
= NULL
;
80 message
->http
.info
.request
.URI
= NULL
;
89 PHP_HTTP_API http_message
*_http_message_init_ex(http_message
*message
, http_message_type type
)
92 message
= ecalloc(1, sizeof(http_message
));
95 http_message_init_type(message
, type
);
96 message
->parent
= NULL
;
97 phpstr_init(&message
->body
);
98 zend_hash_init(&message
->hdrs
, 0, NULL
, ZVAL_PTR_DTOR
, 0);
104 PHP_HTTP_API
void _http_message_set_type(http_message
*message
, http_message_type type
)
106 /* just act if different */
107 if (type
!= message
->type
) {
109 /* free request info */
110 switch (message
->type
)
112 case HTTP_MSG_REQUEST
:
113 STR_FREE(message
->http
.info
.request
.method
);
114 STR_FREE(message
->http
.info
.request
.URI
);
117 case HTTP_MSG_RESPONSE
:
118 STR_FREE(message
->http
.info
.response
.status
);
126 http_message_init_type(message
, type
);
130 PHP_HTTP_API http_message
*_http_message_parse_ex(http_message
*msg
, const char *message
, size_t message_length TSRMLS_DC
)
132 const char *body
= NULL
;
133 zend_bool free_msg
= msg
? 0 : 1;
135 if ((!message
) || (message_length
< HTTP_MSG_MIN_SIZE
)) {
139 msg
= http_message_init(msg
);
141 if (SUCCESS
!= http_parse_headers_cb(message
, &msg
->hdrs
, 1, (http_info_callback
) http_message_info_callback
, (void **) &msg
)) {
143 http_message_free(&msg
);
148 /* header parsing stops at (CR)LF (CR)LF */
149 if (body
= http_locate_body(message
)) {
151 const char *continue_at
= NULL
;
153 /* message has chunked transfer encoding */
154 if (c
= http_message_header(msg
, "Transfer-Encoding")) {
155 if (!strcasecmp("chunked", Z_STRVAL_P(c
))) {
159 /* decode and replace Transfer-Encoding with Content-Length header */
160 if (continue_at
= http_chunked_decode(body
, message
+ message_length
- body
, &decoded
, &decoded_len
)) {
161 phpstr_from_string_ex(PHPSTR(msg
), decoded
, decoded_len
);
167 spprintf(&tmp
, 0, "%lu", (ulong
) decoded_len
);
169 ZVAL_STRING(len
, tmp
, 0);
171 zend_hash_del(&msg
->hdrs
, "Transfer-Encoding", sizeof("Transfer-Encoding"));
172 zend_hash_add(&msg
->hdrs
, "Content-Length", sizeof("Content-Length"), (void *) &len
, sizeof(zval
*), NULL
);
178 /* message has content-length header */
179 if (c
= http_message_header(msg
, "Content-Length")) {
180 long len
= atol(Z_STRVAL_P(c
));
181 phpstr_from_string_ex(PHPSTR(msg
), body
, len
);
182 continue_at
= body
+ len
;
185 /* message has content-range header */
186 if (c
= http_message_header(msg
, "Content-Range")) {
187 ulong start
= 0, end
= 0;
189 sscanf(Z_STRVAL_P(c
), "bytes=%lu-%lu", &start
, &end
);
191 phpstr_from_string_ex(PHPSTR(msg
), body
, (size_t) (end
- start
));
192 continue_at
= body
+ (end
- start
);
196 /* no headers that indicate content length */
197 if (HTTP_MSG_TYPE(RESPONSE
, msg
)) {
198 phpstr_from_string_ex(PHPSTR(msg
), body
, message
+ message_length
- body
);
203 /* check for following messages */
205 while (isspace(*continue_at
)) ++continue_at
;
206 if (continue_at
< (message
+ message_length
)) {
207 http_message
*next
= NULL
, *most
= NULL
;
209 /* set current message to parent of most parent following messages and return deepest */
210 if (most
= next
= http_message_parse(continue_at
, message
+ message_length
- continue_at
)) {
211 while (most
->parent
) most
= most
->parent
;
222 PHP_HTTP_API
void _http_message_tostring(http_message
*msg
, char **string
, size_t *length
)
229 phpstr_init_ex(&str
, 4096, 0);
233 case HTTP_MSG_REQUEST
:
234 phpstr_appendf(&str
, "%s %s HTTP/%1.1f" HTTP_CRLF
,
235 msg
->http
.info
.request
.method
,
236 msg
->http
.info
.request
.URI
,
240 case HTTP_MSG_RESPONSE
:
241 phpstr_appendf(&str
, "HTTP/%1.1f %d%s%s" HTTP_CRLF
,
243 msg
->http
.info
.response
.code
,
244 *msg
->http
.info
.response
.status
? " ":"",
245 msg
->http
.info
.response
.status
);
253 FOREACH_HASH_KEYVAL(&msg
->hdrs
, key
, idx
, header
) {
255 zval
**single_header
;
257 switch (Z_TYPE_PP(header
))
260 phpstr_appendf(&str
, "%s: %s" HTTP_CRLF
, key
, Z_STRVAL_PP(header
));
264 FOREACH_VAL(*header
, single_header
) {
265 phpstr_appendf(&str
, "%s: %s" HTTP_CRLF
, key
, Z_STRVAL_PP(single_header
));
274 if (PHPSTR_LEN(msg
)) {
275 phpstr_appends(&str
, HTTP_CRLF
);
276 phpstr_append(&str
, PHPSTR_VAL(msg
), PHPSTR_LEN(msg
));
277 phpstr_appends(&str
, HTTP_CRLF
);
280 data
= phpstr_data(&str
, string
, length
);
288 PHP_HTTP_API
void _http_message_serialize(http_message
*message
, char **string
, size_t *length
)
297 http_message_tostring(message
, &buf
, &len
);
298 phpstr_prepend(&str
, buf
, len
);
300 } while (message
= message
->parent
);
302 buf
= phpstr_data(&str
, string
, length
);
310 PHP_HTTP_API
void _http_message_tostruct_recursive(http_message
*msg
, zval
*obj TSRMLS_DC
)
315 INIT_ZARR(strct
, HASH_OF(obj
));
317 add_assoc_long(&strct
, "type", msg
->type
);
318 add_assoc_double(&strct
, "httpVersion", msg
->http
.version
);
321 case HTTP_MSG_RESPONSE
:
322 add_assoc_long(&strct
, "responseCode", msg
->http
.info
.response
.code
);
323 add_assoc_string(&strct
, "responseStatus", msg
->http
.info
.response
.status
, 1);
326 case HTTP_MSG_REQUEST
:
327 add_assoc_string(&strct
, "requestMethod", msg
->http
.info
.request
.method
, 1);
328 add_assoc_string(&strct
, "requestUri", msg
->http
.info
.request
.URI
, 1);
332 MAKE_STD_ZVAL(headers
);
334 zend_hash_copy(Z_ARRVAL_P(headers
), &msg
->hdrs
, (copy_ctor_func_t
) zval_add_ref
, NULL
, sizeof(zval
*));
335 add_assoc_zval(&strct
, "headers", headers
);
337 add_assoc_stringl(&strct
, "body", PHPSTR_VAL(msg
), PHPSTR_LEN(msg
), 1);
342 MAKE_STD_ZVAL(parent
);
343 if (Z_TYPE_P(obj
) == IS_ARRAY
) {
348 add_assoc_zval(&strct
, "parentMessage", parent
);
349 http_message_tostruct_recursive(msg
->parent
, parent
);
350 zval_ptr_dtor(&parent
);
352 add_assoc_null(&strct
, "parentMessage");
356 PHP_HTTP_API STATUS
_http_message_send(http_message
*message TSRMLS_DC
)
360 switch (message
->type
)
362 case HTTP_MSG_RESPONSE
:
368 FOREACH_HASH_KEYVAL(&message
->hdrs
, key
, idx
, val
) {
370 if (Z_TYPE_PP(val
) == IS_ARRAY
) {
374 FOREACH_VAL(*val
, data
) {
375 http_send_header_ex(key
, strlen(key
), Z_STRVAL_PP(data
), Z_STRLEN_PP(data
), first
, NULL
);
379 http_send_header_ex(key
, strlen(key
), Z_STRVAL_PP(val
), Z_STRLEN_PP(val
), 1, NULL
);
384 rs
= SUCCESS
== http_send_status(message
->http
.info
.response
.code
) &&
385 SUCCESS
== http_send_data(PHPSTR_VAL(message
), PHPSTR_LEN(message
)) ?
390 case HTTP_MSG_REQUEST
:
392 #ifdef HTTP_HAVE_CURL
394 zval
**zhost
, options
, headers
;
396 INIT_PZVAL(&options
);
397 INIT_PZVAL(&headers
);
398 array_init(&options
);
399 array_init(&headers
);
400 zend_hash_copy(Z_ARRVAL(headers
), &message
->hdrs
, (copy_ctor_func_t
) zval_add_ref
, NULL
, sizeof(zval
*));
401 add_assoc_zval(&options
, "headers", &headers
);
403 /* check host header */
404 if (SUCCESS
== zend_hash_find(&message
->hdrs
, "Host", sizeof("Host"), (void **) &zhost
)) {
405 char *colon
= NULL
, *host
= NULL
;
410 if (colon
= strchr(Z_STRVAL_PP(zhost
), ':')) {
411 port
= atoi(colon
+ 1);
412 host
= estrndup(Z_STRVAL_PP(zhost
), host_len
= (Z_STRVAL_PP(zhost
) - colon
- 1));
414 host
= estrndup(Z_STRVAL_PP(zhost
), host_len
= Z_STRLEN_PP(zhost
));
416 uri
= http_absolute_uri_ex(
417 message
->http
.info
.request
.URI
, strlen(message
->http
.info
.request
.URI
),
418 NULL
, 0, host
, host_len
, port
);
421 uri
= http_absolute_uri(message
->http
.info
.request
.URI
);
424 if (!strcasecmp("POST", message
->http
.info
.request
.method
)) {
425 http_request_body body
= {HTTP_REQUEST_BODY_CSTRING
, PHPSTR_VAL(message
), PHPSTR_LEN(message
)};
426 rs
= http_post(uri
, &body
, Z_ARRVAL(options
), NULL
, NULL
);
428 if (!strcasecmp("GET", message
->http
.info
.request
.method
)) {
429 rs
= http_get(uri
, Z_ARRVAL(options
), NULL
, NULL
);
431 if (!strcasecmp("HEAD", message
->http
.info
.request
.method
)) {
432 rs
= http_head(uri
, Z_ARRVAL(options
), NULL
, NULL
);
434 http_error_ex(HE_WARNING
, HTTP_E_REQUEST_METHOD
,
435 "Cannot send HttpMessage. Request method %s not supported",
436 message
->http
.info
.request
.method
);
441 http_error(HE_WARNING
, HTTP_E_RUNTIME
, "HTTP requests not supported - ext/http was not linked against libcurl.");
448 http_error(HE_WARNING
, HTTP_E_MESSAGE_TYPE
, "HttpMessage is neither of type HTTP_MSG_REQUEST nor HTTP_MSG_RESPONSE");
455 PHP_HTTP_API http_message
*_http_message_dup(http_message
*msg TSRMLS_DC
)
461 char *serialized_data
;
462 size_t serialized_length
;
464 http_message_serialize(msg
, &serialized_data
, &serialized_length
);
465 new = http_message_parse(serialized_data
, serialized_length
);
466 efree(serialized_data
);
470 PHP_HTTP_API
void _http_message_dtor(http_message
*message
)
473 zend_hash_destroy(&message
->hdrs
);
474 phpstr_dtor(PHPSTR(message
));
476 switch (message
->type
)
478 case HTTP_MSG_REQUEST
:
479 STR_SET(message
->http
.info
.request
.method
, NULL
);
480 STR_SET(message
->http
.info
.request
.URI
, NULL
);
483 case HTTP_MSG_RESPONSE
:
484 STR_SET(message
->http
.info
.response
.status
, NULL
);
493 PHP_HTTP_API
void _http_message_free(http_message
**message
)
496 if ((*message
)->parent
) {
497 http_message_free(&(*message
)->parent
);
499 http_message_dtor(*message
);
510 * vim600: noet sw=4 ts=4 fdm=marker
511 * vim<600: noet sw=4 ts=4