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 #define http_message_headers_cb _http_message_headers_cb
35 static void _http_message_headers_cb(const char *http_line
, HashTable
**headers
, void **message TSRMLS_DC
)
39 http_message
*new, *old
= (http_message
*) *message
;
41 if (crlf
= strstr(http_line
, HTTP_CRLF
)) {
42 line_length
= crlf
- http_line
;
44 line_length
= strlen(http_line
);
47 if (old
->type
|| zend_hash_num_elements(&old
->hdrs
) || PHPSTR_LEN(old
)) {
48 new = http_message_new();
52 *headers
= &new->hdrs
;
57 while (isspace(http_line
[line_length
-1])) --line_length
;
60 if (!strncmp(http_line
, "HTTP/1.", lenof("HTTP/1."))) {
61 new->type
= HTTP_MSG_RESPONSE
;
62 new->info
.response
.http_version
= (float) atof(http_line
+ lenof("HTTP/"));
63 new->info
.response
.code
= atoi(http_line
+ lenof("HTTP/1.1 "));
66 if (!strncmp(http_line
+ line_length
- lenof("HTTP/1.1"), "HTTP/1.", lenof("HTTP/1."))) {
67 const char *method_sep_uri
= strchr(http_line
, ' ');
68 new->type
= HTTP_MSG_REQUEST
;
69 new->info
.request
.http_version
= (float) atof(http_line
+ line_length
- lenof("1.1"));
70 new->info
.request
.method
= estrndup(http_line
, method_sep_uri
- http_line
);
71 new->info
.request
.URI
= estrndup(method_sep_uri
+ 1, http_line
+ line_length
- method_sep_uri
- 1 - lenof(" HTTP/1.1"));
75 #define http_message_init_type _http_message_init_type
76 static inline void _http_message_init_type(http_message
*message
, http_message_type type
)
78 switch (message
->type
= type
)
80 case HTTP_MSG_RESPONSE
:
81 message
->info
.response
.http_version
= .0;
82 message
->info
.response
.code
= 0;
85 case HTTP_MSG_REQUEST
:
86 message
->info
.request
.http_version
= .0;
87 message
->info
.request
.method
= NULL
;
88 message
->info
.request
.URI
= NULL
;
97 #define http_message_header(m, h) _http_message_header_ex((m), (h), sizeof(h))
98 #define http_message_header_ex _http_message_header_ex
99 static inline zval
*_http_message_header_ex(http_message
*msg
, char *key_str
, size_t key_len
)
102 if (SUCCESS
== zend_hash_find(&msg
->hdrs
, key_str
, key_len
, (void **) &header
)) {
108 PHP_HTTP_API http_message
*_http_message_init_ex(http_message
*message
, http_message_type type
)
111 message
= ecalloc(1, sizeof(http_message
));
114 http_message_init_type(message
, type
);
115 message
->parent
= NULL
;
116 phpstr_init(&message
->body
);
117 zend_hash_init(&message
->hdrs
, 0, NULL
, ZVAL_PTR_DTOR
, 0);
123 PHP_HTTP_API
void _http_message_set_type(http_message
*message
, http_message_type type
)
125 /* just act if different */
126 if (type
!= message
->type
) {
128 /* free request info */
129 if (message
->type
== HTTP_MSG_REQUEST
) {
130 if (message
->info
.request
.method
) {
131 efree(message
->info
.request
.method
);
133 if (message
->info
.request
.URI
) {
134 efree(message
->info
.request
.URI
);
139 http_message_init_type(message
, type
);
143 PHP_HTTP_API http_message
*_http_message_parse_ex(http_message
*msg
, const char *message
, size_t message_length TSRMLS_DC
)
146 zend_bool free_msg
= msg
? 0 : 1;
148 if (message_length
< HTTP_MSG_MIN_SIZE
) {
156 msg
= http_message_init(msg
);
158 if (SUCCESS
!= http_parse_headers_cb(message
, &msg
->hdrs
, 1, http_message_headers_cb
, (void **) &msg
)) {
160 http_message_free(msg
);
165 /* header parsing stops at CRLF CRLF */
166 if (body
= strstr(message
, HTTP_CRLF HTTP_CRLF
)) {
168 const char *continue_at
= NULL
;
170 body
+= lenof(HTTP_CRLF HTTP_CRLF
);
172 /* message has content-length header */
173 if (c
= http_message_header(msg
, "Content-Length")) {
174 long len
= atol(Z_STRVAL_P(c
));
175 phpstr_from_string_ex(PHPSTR(msg
), body
, len
);
176 continue_at
= body
+ len
;
179 /* message has chunked transfer encoding */
180 if (c
= http_message_header(msg
, "Transfer-Encoding")) {
181 if (!strcasecmp("chunked", Z_STRVAL_P(c
))) {
185 /* decode and replace Transfer-Encoding with Content-Length header */
186 if (continue_at
= http_chunked_decode(body
, message
+ message_length
- body
, &decoded
, &decoded_len
)) {
187 phpstr_from_string_ex(PHPSTR(msg
), decoded
, decoded_len
);
193 spprintf(&tmp
, 0, "%lu", decoded_len
);
195 ZVAL_STRING(len
, tmp
, 0);
197 zend_hash_del(&msg
->hdrs
, "Transfer-Encoding", sizeof("Transfer-Encoding"));
198 zend_hash_add(&msg
->hdrs
, "Content-Length", sizeof("Content-Length"), (void *) &len
, sizeof(zval
*), NULL
);
204 /* message has content-range header */
205 if (c
= http_message_header(msg
, "Content-Range")) {
206 ulong start
= 0, end
= 0;
208 sscanf(Z_STRVAL_P(c
), "bytes=%lu-%lu", &start
, &end
);
210 phpstr_from_string_ex(PHPSTR(msg
), body
, (size_t) (end
- start
));
211 continue_at
= body
+ (end
- start
);
215 /* no headers that indicate content length */
217 phpstr_from_string_ex(PHPSTR(msg
), body
, message
+ message_length
- body
);
220 /* check for following messages */
222 while (isspace(*continue_at
)) ++continue_at
;
223 if (continue_at
< (message
+ message_length
)) {
224 http_message
*next
= NULL
, *most
= NULL
;
226 /* set current message to parent of most parent following messages and return deepest */
227 if (most
= next
= http_message_parse(continue_at
, message
+ message_length
- continue_at
)) {
228 while (most
->parent
) most
= most
->parent
;
239 PHP_HTTP_API
void _http_message_tostring(http_message
*msg
, char **string
, size_t *length
)
246 phpstr_init_ex(&str
, 4096, 0);
250 case HTTP_MSG_REQUEST
:
251 phpstr_appendf(&str
, "%s %s HTTP/%1.1f" HTTP_CRLF
,
252 msg
->info
.request
.method
,
253 msg
->info
.request
.URI
,
254 msg
->info
.request
.http_version
);
257 case HTTP_MSG_RESPONSE
:
258 phpstr_appendf(&str
, "HTTP/%1.1f %d" HTTP_CRLF
,
259 msg
->info
.response
.http_version
,
260 msg
->info
.response
.code
);
268 FOREACH_HASH_KEYVAL(&msg
->hdrs
, key
, idx
, header
) {
270 zval
**single_header
;
272 switch (Z_TYPE_PP(header
))
275 phpstr_appendf(&str
, "%s: %s" HTTP_CRLF
, key
, Z_STRVAL_PP(header
));
279 FOREACH_VAL(*header
, single_header
) {
280 phpstr_appendf(&str
, "%s: %s" HTTP_CRLF
, key
, Z_STRVAL_PP(single_header
));
289 if (PHPSTR_LEN(msg
)) {
290 phpstr_appends(&str
, HTTP_CRLF
);
291 phpstr_append(&str
, PHPSTR_VAL(msg
), PHPSTR_LEN(msg
));
292 phpstr_appends(&str
, HTTP_CRLF
);
295 data
= phpstr_data(&str
, string
, length
);
303 PHP_HTTP_API
void _http_message_serialize(http_message
*message
, char **string
, size_t *length
)
312 http_message_tostring(message
, &buf
, &len
);
313 phpstr_prepend(&str
, buf
, len
);
315 } while (message
= message
->parent
);
317 buf
= phpstr_data(&str
, string
, length
);
325 PHP_HTTP_API STATUS
_http_message_send(http_message
*message TSRMLS_DC
)
329 switch (message
->type
)
331 case HTTP_MSG_RESPONSE
:
337 FOREACH_HASH_KEYVAL(&message
->hdrs
, key
, idx
, val
) {
340 spprintf(&header
, 0, "%s: %s", key
, Z_STRVAL_PP(val
));
341 http_send_header(header
);
346 rs
= SUCCESS
== http_send_status(message
->info
.response
.code
) &&
347 SUCCESS
== http_send_data(PHPSTR_VAL(message
), PHPSTR_LEN(message
)) ?
352 case HTTP_MSG_REQUEST
:
354 #ifdef HTTP_HAVE_CURL
356 zval
**zhost
, options
, headers
;
358 array_init(&options
);
359 array_init(&headers
);
360 zend_hash_copy(Z_ARRVAL(headers
), &message
->hdrs
, (copy_ctor_func_t
) zval_add_ref
, NULL
, sizeof(zval
*));
361 add_assoc_zval(&options
, "headers", &headers
);
363 /* check host header */
364 if (SUCCESS
== zend_hash_find(&message
->hdrs
, "Host", sizeof("Host"), (void **) &zhost
)) {
365 char *colon
= NULL
, *host
= NULL
;
370 if (colon
= strchr(Z_STRVAL_PP(zhost
), ':')) {
371 port
= atoi(colon
+ 1);
372 host
= estrndup(Z_STRVAL_PP(zhost
), host_len
= (Z_STRVAL_PP(zhost
) - colon
- 1));
374 host
= estrndup(Z_STRVAL_PP(zhost
), host_len
= Z_STRLEN_PP(zhost
));
376 uri
= http_absolute_uri_ex(
377 message
->info
.request
.URI
, strlen(message
->info
.request
.URI
),
378 NULL
, 0, host
, host_len
, port
);
381 uri
= http_absolute_uri(message
->info
.request
.URI
);
384 if (!strcasecmp("POST", message
->info
.request
.method
)) {
385 http_request_body body
= {HTTP_REQUEST_BODY_CSTRING
, PHPSTR_VAL(message
), PHPSTR_LEN(message
)};
386 rs
= http_post(uri
, &body
, Z_ARRVAL(options
), NULL
, NULL
);
388 if (!strcasecmp("GET", message
->info
.request
.method
)) {
389 rs
= http_get(uri
, Z_ARRVAL(options
), NULL
, NULL
);
391 if (!strcasecmp("HEAD", message
->info
.request
.method
)) {
392 rs
= http_head(uri
, Z_ARRVAL(options
), NULL
, NULL
);
394 http_error_ex(E_WARNING
, HTTP_E_MSG
,
395 "Cannot send HttpMessage. Request method %s not supported",
396 message
->info
.request
.method
);
401 http_error(E_WARNING
, HTTP_E_MSG
, "HTTP requests not supported - ext/http was not linked against libcurl.");
408 http_error(E_WARNING
, HTTP_E_MSG
, "HttpMessage is neither of type HTTP_MSG_REQUEST nor HTTP_MSG_RESPONSE");
415 PHP_HTTP_API http_message
*_http_message_dup(http_message
*msg TSRMLS_DC
)
421 char *serialized_data
;
422 size_t serialized_length
;
424 http_message_serialize(msg
, &serialized_data
, &serialized_length
);
425 new = http_message_parse(serialized_data
, serialized_length
);
426 efree(serialized_data
);
430 PHP_HTTP_API
void _http_message_dtor(http_message
*message
)
433 zend_hash_destroy(&message
->hdrs
);
434 phpstr_dtor(PHPSTR(message
));
435 if (HTTP_MSG_TYPE(REQUEST
, message
)) {
436 if (message
->info
.request
.method
) {
437 efree(message
->info
.request
.method
);
438 message
->info
.request
.method
= NULL
;
440 if (message
->info
.request
.URI
) {
441 efree(message
->info
.request
.URI
);
442 message
->info
.request
.URI
= NULL
;
448 PHP_HTTP_API
void _http_message_free(http_message
*message
)
451 if (message
->parent
) {
452 http_message_free(message
->parent
);
453 message
->parent
= NULL
;
455 http_message_dtor(message
);
465 * vim600: noet sw=4 ts=4 fdm=marker
466 * vim<600: noet sw=4 ts=4