2 +--------------------------------------------------------------------+
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2004-2010, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
17 #include <main/SAPI.h>
18 #include <ext/date/php_date.h>
19 #include <ext/standard/php_string.h>
21 PHP_RINIT_FUNCTION(http_env
)
23 PHP_HTTP_G
->env
.response
.last_modified
= 0;
24 PHP_HTTP_G
->env
.response
.throttle_chunk
= 0;
25 PHP_HTTP_G
->env
.response
.throttle_delay
= 0;
26 PHP_HTTP_G
->env
.request
.time
= sapi_get_request_time(TSRMLS_C
);
31 PHP_RSHUTDOWN_FUNCTION(http_env
)
33 if (PHP_HTTP_G
->env
.request
.headers
) {
34 zend_hash_destroy(PHP_HTTP_G
->env
.request
.headers
);
35 FREE_HASHTABLE(PHP_HTTP_G
->env
.request
.headers
);
36 PHP_HTTP_G
->env
.request
.headers
= NULL
;
38 if (PHP_HTTP_G
->env
.request
.body
) {
39 php_http_message_body_free(&PHP_HTTP_G
->env
.request
.body
);
41 if (PHP_HTTP_G
->env
.response
.body
) {
42 php_http_message_body_free(&PHP_HTTP_G
->env
.response
.body
);
44 STR_SET(PHP_HTTP_G
->env
.response
.content_type
, NULL
);
45 STR_SET(PHP_HTTP_G
->env
.response
.etag
, NULL
);
47 if (PHP_HTTP_G
->env
.server_var
) {
48 zval_ptr_dtor(&PHP_HTTP_G
->env
.server_var
);
49 PHP_HTTP_G
->env
.server_var
= NULL
;
55 PHP_HTTP_API
void php_http_env_get_request_headers(HashTable
*headers TSRMLS_DC
)
57 php_http_array_hashkey_t key
= php_http_array_hashkey_init(0);
61 if (!PHP_HTTP_G
->env
.request
.headers
) {
62 ALLOC_HASHTABLE(PHP_HTTP_G
->env
.request
.headers
);
63 zend_hash_init(PHP_HTTP_G
->env
.request
.headers
, 0, NULL
, ZVAL_PTR_DTOR
, 0);
65 zend_is_auto_global("_SERVER", lenof("_SERVER") TSRMLS_CC
);
67 if (SUCCESS
== zend_hash_find(&EG(symbol_table
), "_SERVER", sizeof("_SERVER"), (void *) &hsv
) && Z_TYPE_PP(hsv
) == IS_ARRAY
) {
68 FOREACH_KEY(pos
, *hsv
, key
) {
69 if (key
.type
== HASH_KEY_IS_STRING
&& key
.len
> 6 && !strncmp(key
.str
, "HTTP_", 5)) {
71 key
.str
= php_http_pretty_key(estrndup(key
.str
+ 5, key
.len
- 1), key
.len
- 1, 1, 1);
73 zend_hash_get_current_data_ex(Z_ARRVAL_PP(hsv
), (void *) &header
, &pos
);
75 zend_hash_add(PHP_HTTP_G
->env
.request
.headers
, key
.str
, key
.len
, (void *) header
, sizeof(zval
*), NULL
);
84 zend_hash_copy(headers
, PHP_HTTP_G
->env
.request
.headers
, (copy_ctor_func_t
) zval_add_ref
, NULL
, sizeof(zval
*));
88 PHP_HTTP_API
char *php_http_env_get_request_header(const char *name_str
, size_t name_len TSRMLS_DC
)
91 char *val
= NULL
, *key
= php_http_pretty_key(estrndup(name_str
, name_len
), name_len
, 1, 1);
93 php_http_env_get_request_headers(NULL TSRMLS_CC
);
95 if (SUCCESS
== zend_hash_find(PHP_HTTP_G
->env
.request
.headers
, key
, name_len
+ 1, (void *) &zvalue
)) {
96 zval
*zcopy
= php_http_ztyp(IS_STRING
, *zvalue
);
98 val
= estrndup(Z_STRVAL_P(zcopy
), Z_STRLEN_P(zcopy
));
99 zval_ptr_dtor(&zcopy
);
107 PHP_HTTP_API
int php_http_env_got_request_header(const char *name_str
, size_t name_len TSRMLS_DC
)
109 char *key
= php_http_pretty_key(estrndup(name_str
, name_len
), name_len
, 1, 1);
112 php_http_env_get_request_headers(NULL TSRMLS_CC
);
113 got
= zend_hash_exists(PHP_HTTP_G
->env
.request
.headers
, key
, name_len
+ 1);
119 PHP_HTTP_API zval
*php_http_env_get_server_var(const char *key
, size_t key_len
, zend_bool check TSRMLS_DC
)
124 /* if available, this is a lot faster than accessing $_SERVER */
125 if (sapi_module
.getenv
) {
126 if ((!(env
= sapi_module
.getenv((char *) key
, key_len TSRMLS_CC
))) || (check
&& !*env
)) {
129 if (PHP_HTTP_G
->env
.server_var
) {
130 zval_ptr_dtor(&PHP_HTTP_G
->env
.server_var
);
132 MAKE_STD_ZVAL(PHP_HTTP_G
->env
.server_var
);
133 ZVAL_STRING(PHP_HTTP_G
->env
.server_var
, env
, 1);
134 return PHP_HTTP_G
->env
.server_var
;
137 zend_is_auto_global(ZEND_STRL("_SERVER") TSRMLS_CC
);
139 if ((SUCCESS
!= zend_hash_find(&EG(symbol_table
), ZEND_STRS("_SERVER"), (void *) &hsv
)) || (Z_TYPE_PP(hsv
) != IS_ARRAY
)) {
142 if ((SUCCESS
!= zend_hash_find(Z_ARRVAL_PP(hsv
), key
, key_len
+ 1, (void *) &var
))) {
145 if (check
&& !((Z_TYPE_PP(var
) == IS_STRING
) && Z_STRVAL_PP(var
) && Z_STRLEN_PP(var
))) {
151 PHP_HTTP_API php_http_message_body_t
*php_http_env_get_request_body(TSRMLS_D
)
153 if (!PHP_HTTP_G
->env
.request
.body
) {
154 php_stream
*s
= NULL
;
156 if (SG(request_info
).post_data
|| SG(request_info
).raw_post_data
) {
157 if ((s
= php_stream_temp_new())) {
158 /* php://input does not support seek() */
159 if (SG(request_info
).raw_post_data
) {
160 php_stream_write(s
, SG(request_info
).raw_post_data
, SG(request_info
).raw_post_data_length
);
162 php_stream_write(s
, SG(request_info
).post_data
, SG(request_info
).post_data_length
);
164 php_stream_rewind(s
);
166 } else if (sapi_module
.read_post
&& !SG(read_post_bytes
)) {
167 if ((s
= php_stream_temp_new())) {
168 char *buf
= emalloc(4096);
171 while (0 < (len
= sapi_module
.read_post(buf
, 4096 TSRMLS_CC
))) {
172 SG(read_post_bytes
) += len
;
173 php_stream_write(s
, buf
, len
);
181 php_stream_rewind(s
);
184 PHP_HTTP_G
->env
.request
.body
= php_http_message_body_init(NULL
, s TSRMLS_CC
);
187 return PHP_HTTP_G
->env
.request
.body
;
190 PHP_HTTP_API php_http_range_status_t
php_http_env_get_request_ranges(HashTable
*ranges
, size_t length TSRMLS_DC
)
194 long begin
= -1, end
= -1, *ptr
;
196 if (!(range
= php_http_env_get_request_header(ZEND_STRL("Range") TSRMLS_CC
))) {
197 return PHP_HTTP_RANGE_NO
;
199 if (strncmp(range
, "bytes=", lenof("bytes="))) {
201 return PHP_HTTP_RANGE_NO
;
204 rp
= range
+ lenof("bytes=");
208 switch (c
= *(rp
++)) {
210 /* allow 000... - shall we? */
216 case '1': case '2': case '3':
217 case '4': case '5': case '6':
218 case '7': case '8': case '9':
220 * If the value of the pointer is already set (non-negative)
221 * then multiply its value by ten and add the current value,
222 * else initialise the pointers value with the current value
224 * This let us recognize empty fields when validating the
225 * ranges, i.e. a "-10" for begin and "12345" for the end
226 * was the following range request: "Range: bytes=0-12345";
227 * While a "-1" for begin and "12345" for the end would
228 * have been: "Range: bytes=-12345".
249 /* validate ranges */
257 return PHP_HTTP_RANGE_NO
;
265 if (length
<= (size_t) end
) {
276 if (end
== -1 || end
== -10) {
278 return PHP_HTTP_RANGE_ERR
;
280 begin
= length
- end
;
286 if (length
<= (size_t) begin
) {
288 return PHP_HTTP_RANGE_ERR
;
294 return PHP_HTTP_RANGE_ERR
;
303 if (length
<= (size_t) end
) {
305 } else if (end
< begin
) {
307 return PHP_HTTP_RANGE_ERR
;
315 MAKE_STD_ZVAL(zentry
);
317 add_index_long(zentry
, 0, begin
);
318 add_index_long(zentry
, 1, end
);
319 zend_hash_next_index_insert(ranges
, &zentry
, sizeof(zval
*), NULL
);
329 return PHP_HTTP_RANGE_NO
;
334 return PHP_HTTP_RANGE_OK
;
337 static void grab_headers(void *data
, void *arg TSRMLS_DC
)
339 php_http_buffer_appendl(PHP_HTTP_BUFFER(arg
), ((sapi_header_struct
*)data
)->header
);
340 php_http_buffer_appends(PHP_HTTP_BUFFER(arg
), PHP_HTTP_CRLF
);
343 PHP_HTTP_API STATUS
php_http_env_get_response_headers(HashTable
*headers_ht TSRMLS_DC
)
346 php_http_buffer_t headers
;
348 php_http_buffer_init(&headers
);
349 zend_llist_apply_with_argument(&SG(sapi_headers
).headers
, grab_headers
, &headers TSRMLS_CC
);
350 php_http_buffer_fix(&headers
);
352 status
= php_http_headers_parse(PHP_HTTP_BUFFER_VAL(&headers
), PHP_HTTP_BUFFER_LEN(&headers
), headers_ht
, NULL
, NULL TSRMLS_CC
);
353 php_http_buffer_dtor(&headers
);
358 PHP_HTTP_API
char *php_http_env_get_response_header(const char *name_str
, size_t name_len TSRMLS_DC
)
363 zend_hash_init(&headers
, 0, NULL
, NULL
, 0);
364 if (SUCCESS
== php_http_env_get_response_headers(&headers TSRMLS_CC
)) {
366 char *key
= php_http_pretty_key(estrndup(name_str
, name_len
), name_len
, 1, 1);
368 if (SUCCESS
== zend_hash_find(&headers
, key
, name_len
+ 1, (void *) &zvalue
)) {
369 zval
*zcopy
= php_http_ztyp(IS_STRING
, *zvalue
);
371 val
= estrndup(Z_STRVAL_P(zcopy
), Z_STRLEN_P(zcopy
));
372 zval_ptr_dtor(&zcopy
);
377 zend_hash_destroy(&headers
);
382 PHP_HTTP_API
long php_http_env_get_response_code(TSRMLS_D
)
384 long code
= SG(sapi_headers
).http_response_code
;
385 return code
? code
: 200;
388 PHP_HTTP_API STATUS
php_http_env_set_response_code(long http_code TSRMLS_DC
)
390 return sapi_header_op(SAPI_HEADER_SET_STATUS
, (void *) http_code TSRMLS_CC
);
393 PHP_HTTP_API STATUS
php_http_env_set_response_status_line(long code
, php_http_version_t
*v TSRMLS_DC
)
395 sapi_header_line h
= {0};
398 h
.line_len
= spprintf(&h
.line
, 0, "HTTP/%u.%u %ld %s", v
->major
, v
->minor
, code
, php_http_env_get_response_status_for_code(code
));
399 ret
= sapi_header_op(SAPI_HEADER_REPLACE
, (void *) &h TSRMLS_CC
);
405 PHP_HTTP_API STATUS
php_http_env_set_response_protocol_version(php_http_version_t
*v TSRMLS_DC
)
407 return php_http_env_set_response_status_line(php_http_env_get_response_code(TSRMLS_C
), v TSRMLS_CC
);
410 PHP_HTTP_API STATUS
php_http_env_set_response_header(long http_code
, const char *header_str
, size_t header_len
, zend_bool replace TSRMLS_DC
)
412 sapi_header_line h
= {estrndup(header_str
, header_len
), header_len
, http_code
};
413 STATUS ret
= sapi_header_op(replace
? SAPI_HEADER_REPLACE
: SAPI_HEADER_ADD
, (void *) &h TSRMLS_CC
);
418 PHP_HTTP_API STATUS
php_http_env_set_response_header_format(long http_code
, zend_bool replace TSRMLS_DC
, const char *fmt
, ...)
421 STATUS ret
= FAILURE
;
422 sapi_header_line h
= {NULL
, 0, http_code
};
425 h
.line_len
= vspprintf(&h
.line
, 0, fmt
, args
);
430 ret
= sapi_header_op(replace
? SAPI_HEADER_REPLACE
: SAPI_HEADER_ADD
, (void *) &h TSRMLS_CC
);
437 PHP_HTTP_API STATUS
php_http_env_set_response_header_value(long http_code
, const char *name_str
, size_t name_len
, zval
*value
, zend_bool replace TSRMLS_DC
)
440 sapi_header_line h
= {(char *) name_str
, name_len
, http_code
};
442 return sapi_header_op(SAPI_HEADER_DELETE
, (void *) &h TSRMLS_CC
);
445 if(Z_TYPE_P(value
) == IS_ARRAY
|| Z_TYPE_P(value
) == IS_OBJECT
) {
450 FOREACH_HASH_VAL(pos
, HASH_OF(value
), data_ptr
) {
451 if (SUCCESS
!= php_http_env_set_response_header_value(http_code
, name_str
, name_len
, *data_ptr
, first TSRMLS_CC
)) {
459 zval
*data
= php_http_ztyp(IS_STRING
, value
);
461 if (!Z_STRLEN_P(data
)) {
462 zval_ptr_dtor(&data
);
463 return php_http_env_set_response_header_value(http_code
, name_str
, name_len
, NULL
, replace TSRMLS_CC
);
468 if (name_len
> INT_MAX
) {
471 h
.response_code
= http_code
;
472 h
.line_len
= spprintf(&h
.line
, 0, "%.*s: %.*s", (int) name_len
, name_str
, Z_STRLEN_P(data
), Z_STRVAL_P(data
));
474 ret
= sapi_header_op(replace
? SAPI_HEADER_REPLACE
: SAPI_HEADER_ADD
, (void *) &h TSRMLS_CC
);
476 zval_ptr_dtor(&data
);
484 static void set_container_value(zval
*container
, const char *name_str
, size_t name_len
, int type
, const void *value_ptr
, size_t value_len TSRMLS_DC
)
486 if (Z_TYPE_P(container
) == IS_OBJECT
) {
487 /* stupid non-const api */
488 char *name
= estrndup(name_str
, name_len
);
491 zend_update_property_double(Z_OBJCE_P(container
), container
, name
, name_len
, *(double *)value_ptr TSRMLS_CC
);
494 zend_update_property_long(Z_OBJCE_P(container
), container
, name
, name_len
, *(long *)value_ptr TSRMLS_CC
);
497 zend_update_property_stringl(Z_OBJCE_P(container
), container
, name
, name_len
, value_ptr
, value_len TSRMLS_CC
);
502 convert_to_array(container
);
505 add_assoc_double_ex(container
, name_str
, name_len
+ 1, *(double *)value_ptr
);
508 add_assoc_long_ex(container
, name_str
, name_len
+ 1, *(long *)value_ptr
);
511 char *value
= estrndup(value_ptr
, value_len
);
512 add_assoc_stringl_ex(container
, name_str
, name_len
+ 1, value
, value_len
, 0);
519 PHP_HTTP_API
void php_http_env_set_response_throttle_rate(zval
*container
, size_t chunk_size
, double delay TSRMLS_CC
)
521 long chunk_size_long
= (long) chunk_size
;
523 set_container_value(container
, ZEND_STRL("throttleDelay"), IS_DOUBLE
, &delay
, 0 TSRMLS_CC
);
524 set_container_value(container
, ZEND_STRL("throttleChunk"), IS_LONG
, &chunk_size_long
, 0 TSRMLS_CC
);
525 if (Z_TYPE_P(container
) == IS_OBJECT
) {
526 zend_update_property_double(Z_OBJCE_P(container
), container
, ZEND_STRL("throttleDelay"), delay TSRMLS_CC
);
527 zend_update_property_long(Z_OBJCE_P(container
), container
, ZEND_STRL("throttleChunk"), chunk_size TSRMLS_CC
);
529 convert_to_array(container
);
530 add_assoc_double_ex(container
, ZEND_STRS("throttleDelay"), delay
);
531 add_assoc_long_ex(container
, ZEND_STRS("throttleChunk"), chunk_size
);
535 PHP_HTTP_API STATUS
php_http_env_set_response_last_modified(zval
*container
, time_t t
, char **sent_header TSRMLS_DC
)
538 char *lm_header_str
, *date
;
539 size_t lm_header_len
;
542 if (!(date
= php_format_date(ZEND_STRL(PHP_HTTP_DATE_FORMAT
), t
, 0 TSRMLS_CC
))) {
546 lm_header_len
= spprintf(&lm_header_str
, 0, "Last-Modified: %s", date
);
549 lm_header_str
= "Last-Modified:";
550 lm_header_len
= lenof("Last-Modified:");
553 if (SUCCESS
== (ret
= php_http_env_set_response_header(0, lm_header_str
, lm_header_len
, 1 TSRMLS_CC
))) {
554 set_container_value(container
, ZEND_STRL("lastModified"), IS_LONG
, &t
, 0 TSRMLS_CC
);
558 *sent_header
= lm_header_str
;
560 STR_FREE(lm_header_str
);
566 PHP_HTTP_API STATUS
php_http_env_set_response_etag(zval
*container
, const char *etag_str
, size_t etag_len
, char **sent_header TSRMLS_DC
)
569 char *etag
= NULL
, *etag_header_str
;
570 size_t etag_header_len
;
573 etag_header_len
= spprintf(&etag_header_str
, 0, "ETag: \"%s\"", etag_str
);
575 etag_header_str
= "ETag:";
576 etag_header_len
= lenof("ETag:");
579 if (SUCCESS
== (ret
= php_http_env_set_response_header(0, etag_header_str
, etag_header_len
, 1 TSRMLS_CC
))) {
580 set_container_value(container
, ZEND_STRL(etag
), IS_STRING
, etag_str
, etag_len TSRMLS_CC
);
584 *sent_header
= etag_header_str
;
585 } else if (etag_len
) {
586 STR_FREE(etag_header_str
);
592 PHP_HTTP_API STATUS
php_http_env_set_response_content_type(zval
*container
, const char *ct_str
, size_t ct_len
, char **sent_header TSRMLS_DC
)
596 size_t ct_header_len
;
599 PHP_HTTP_CHECK_CONTENT_TYPE(ct_str
, return FAILURE
);
600 ct_header_len
= spprintf(&ct_header_str
, 0, "Content-Type: %s", ct_str
);
602 ct_header_str
= "Content-Type:";
603 ct_header_len
= lenof("Content-Type:");
606 if (SUCCESS
== (ret
= php_http_env_set_response_header(0, ct_header_str
, ct_header_len
, 1 TSRMLS_CC
))) {
607 set_container_value(container
, ZEND_STRL("contentType"), IS_STRING
, ct_str
, ct_len TSRMLS_CC
);
611 *sent_header
= ct_header_str
;
613 STR_FREE(ct_header_str
);
619 PHP_HTTP_API STATUS
php_http_env_set_response_content_disposition(zval
*container
, php_http_content_disposition_t d
, const char *f_str
, size_t f_len
, char **sent_header TSRMLS_DC
)
622 char *tmp
, *cd_header_str
, *new_f_str
;
624 size_t cd_header_len
;
627 case PHP_HTTP_CONTENT_DISPOSITION_NONE
:
629 case PHP_HTTP_CONTENT_DISPOSITION_INLINE
:
632 case PHP_HTTP_CONTENT_DISPOSITION_ATTACHMENT
:
636 php_http_error(HE_WARNING
, PHP_HTTP_E_INVALID_PARAM
, "Unknown content disposition (%d)", (int) d
);
641 new_f_str
= php_addslashes(estrndup(f_str
, f_len
), f_len
, &new_f_len
, 0 TSRMLS_CC
);
642 cd_header_len
= spprintf(&cd_header_str
, 0, "Content-Disposition: %s; filename=\"%.*s\"", tmp
, new_f_len
, new_f_str
);
645 cd_header_len
= spprintf(&cd_header_str
, 0, "Content-Disposition: %s", tmp
);
647 cd_header_str
= "Content-Disposition:";
648 cd_header_len
= lenof("Content-Disposition:");
651 ret
= php_http_env_set_response_header(0, cd_header_str
, cd_header_len
, 1 TSRMLS_CC
);
654 *sent_header
= cd_header_str
;
655 } else if (f_len
|| d
){
656 STR_FREE(cd_header_str
);
662 PHP_HTTP_API STATUS
php_http_env_set_response_cache_control(zval
*container
, const char *cc_str
, size_t cc_len
, char **sent_header TSRMLS_DC
)
666 size_t cc_header_len
;
669 cc_header_len
= spprintf(&cc_header_str
, 0, "Cache-Control: %s", cc_str
);
671 cc_header_str
= "Content-Disposition:";
672 cc_header_len
= lenof("Content-Disposition:");
675 ret
= php_http_env_set_response_header(0, cc_header_str
, cc_header_len
, 1 TSRMLS_CC
);
678 *sent_header
= cc_header_str
;
680 STR_FREE(cc_header_str
);
686 static zval
*get_container_value(zval
*container
, const char *name_str
, size_t name_len TSRMLS_CC
)
690 if (Z_TYPE_P(container
) == IS_OBJECT
) {
691 char *name
= estrndup(name_str
, name_len
);
692 val
= zend_read_property(Z_OBJCE_P(container
), container
, name
, name_len
, 0 TSRMLS_CC
);
695 if (SUCCESS
== zend_hash_find(Z_ARRVAL_P(container
), name_str
, name_len
+ 1, (void *) &valptr
)) {
707 PHP_HTTP_API php_http_cache_status_t
php_http_env_is_response_cached_by_etag(zval
*container
, const char *header_str
, size_t header_len TSRMLS_DC
)
709 int ret
, free_etag
= 0;
711 zval
*zetag
, *zbody
= NULL
;
713 if ( !(header
= php_http_env_get_request_header(header_str
, header_len TSRMLS_CC
))
714 || !(zbody
= get_container_value(container
, ZEND_STRL("body") TSRMLS_CC
))
715 || !(Z_TYPE_P(zbody
) == IS_OBJECT
)
716 || !instanceof_function(Z_OBJCE_P(zbody
), php_http_message_body_class_entry TSRMLS_CC
)
720 zval_ptr_dtor(&zbody
);
722 return PHP_HTTP_CACHE_NO
;
725 if ((zetag
= get_container_value(container
, ZEND_STRL("etag") TSRMLS_CC
))) {
726 zval
*zetag_copy
= php_http_ztyp(IS_STRING
, zetag
);
727 zval_ptr_dtor(&zetag
);
731 if (zetag
&& Z_STRLEN_P(zetag
)) {
732 etag
= Z_STRVAL_P(zetag
);
734 etag
= php_http_message_body_etag(((php_http_message_body_object_t
*) zend_object_store_get_object(zbody TSRMLS_CC
))->body
);
735 php_http_env_set_response_etag(container
, etag
, strlen(etag
), NULL TSRMLS_CC
);
740 zval_ptr_dtor(&zetag
);
743 ret
= php_http_match(header
, etag
, PHP_HTTP_MATCH_WORD
);
750 return ret
? PHP_HTTP_CACHE_HIT
: PHP_HTTP_CACHE_MISS
;
753 PHP_HTTP_API php_http_cache_status_t
php_http_env_is_response_cached_by_last_modified(zval
*container
, const char *header_str
, size_t header_len TSRMLS_DC
)
757 zval
*zbody
= NULL
, *zlm
;
759 if ( !(header
= php_http_env_get_request_header(header_str
, header_len TSRMLS_CC
))
760 || !(zbody
= get_container_value(container
, ZEND_STRL("body") TSRMLS_CC
))
761 || !(Z_TYPE_P(zbody
) == IS_OBJECT
)
762 || !instanceof_function(Z_OBJCE_P(zbody
), php_http_message_body_class_entry TSRMLS_CC
)
766 zval_ptr_dtor(&zbody
);
768 return PHP_HTTP_CACHE_NO
;
771 if ((zlm
= get_container_value(container
, ZEND_STRL("lastModified") TSRMLS_CC
))) {
772 zval
*zlm_copy
= php_http_ztyp(IS_LONG
, zlm
);
777 if (zlm
&& Z_LVAL_P(zlm
) > 0) {
780 lm
= php_http_message_body_mtime(((php_http_message_body_object_t
*) zend_object_store_get_object(zbody TSRMLS_CC
))->body
);
781 php_http_env_set_response_last_modified(container
, lm
, NULL TSRMLS_CC
);
788 ums
= php_parse_date(header
, NULL TSRMLS_CC
);
791 if (ums
> 0 && ums
<= lm
) {
792 return PHP_HTTP_CACHE_HIT
;
794 return PHP_HTTP_CACHE_MISS
;
798 PHP_HTTP_API
void php_http_env_set_response_body(zval
*container
, php_http_message_body_t
*body
)
800 TSRMLS_FETCH_FROM_CTX(body
->ts
);
801 zend_object_value ov
= php_http_message_body_object_new_ex(php_http_message_body_class_entry
, php_http_message_body_copy(body
, NULL
, 0), NULL TSRMLS_CC
);
803 set_container_value(container
, ZEND_STRL("body"), IS_OBJECT
, &ov
, 0 TSRMLS_CC
);
807 php_http_buffer_t
*buf
;
811 static size_t output(void *context
, const char *buf
, size_t len TSRMLS_DC
)
813 struct output_ctx
*ctx
= context
;
817 size_t chunk_size
= PHP_HTTP_SENDBUF_SIZE
;
819 if ((zcs
= get_container_value(ctx
->container
, ZEND_STRL("throttleChunk") TSRMLS_CC
))) {
820 zval
*zcs_copy
= php_http_ztyp(IS_LONG
, zcs
);
823 chunk_size
= Z_LVAL_P(zcs_copy
);
824 zval_ptr_dtor(&zcs_copy
);
826 php_http_buffer_chunked_output(&ctx
->buf
, buf
, len
, buf
? chunk_size
: 0, output
, NULL TSRMLS_CC
);
833 /* we really only need to flush when throttling is enabled,
834 because we push the data as fast as possible anyway if not */
835 if ((ztd
= get_container_value(ctx
->container
, ZEND_STRL("throttleDelay") TSRMLS_CC
))) {
837 zval
*ztd_copy
= php_http_ztyp(IS_DOUBLE
, ztd
);
840 delay
= Z_DVAL_P(ztd_copy
);
841 zval_ptr_dtor(&ztd_copy
);
843 if (delay
>= PHP_HTTP_DIFFSEC
) {
844 if (php_output_get_level(TSRMLS_C
)) {
845 php_output_flush_all(TSRMLS_C
);
847 if (!(php_output_get_status(TSRMLS_C
) & PHP_OUTPUT_IMPLICITFLUSH
)) {
848 sapi_flush(TSRMLS_C
);
850 php_http_sleep(delay
);
857 PHP_HTTP_API STATUS
php_http_env_send_response(zval
*container TSRMLS_DC
)
859 struct output_ctx ctx
= {NULL
, container
};
860 zval
*zbody
, *zheader
, *zrcode
, *zversion
;
862 php_http_range_status_t range_status
;
863 php_http_message_body_t
*body
;
866 if ( !(zbody
= get_container_value(container
, ZEND_STRL("body") TSRMLS_CC
))
867 || !(Z_TYPE_P(zbody
) == IS_OBJECT
)
868 || !instanceof_function(Z_OBJCE_P(zbody
), php_http_message_body_class_entry TSRMLS_CC
)
871 zval_ptr_dtor(&zbody
);
876 if ((zrcode
= get_container_value(container
, ZEND_STRL("responseCode") TSRMLS_CC
))) {
877 zval
*zrcode_copy
= php_http_ztyp(IS_LONG
, zrcode
);
879 zval_ptr_dtor(&zrcode
);
880 if (Z_LVAL_P(zrcode_copy
) > 0) {
881 php_http_env_set_response_code(Z_LVAL_P(zrcode_copy
) TSRMLS_CC
);
883 zval_ptr_dtor(&zrcode_copy
);
886 if ((zversion
= get_container_value(container
, ZEND_STRL("httpVersion") TSRMLS_CC
))) {
887 php_http_version_t v
;
888 zval
*zversion_copy
= php_http_ztyp(IS_STRING
, zversion
);
890 zval_ptr_dtor(&zversion
);
891 if (Z_STRLEN_P(zversion_copy
) && php_http_version_parse(&v
, Z_STRVAL_P(zversion_copy
) TSRMLS_CC
)) {
892 php_http_env_set_response_protocol_version(&v TSRMLS_CC
);
893 php_http_version_dtor(&v
);
895 zval_ptr_dtor(&zversion_copy
);
898 if ((zheader
= get_container_value(container
, ZEND_STRL("headers") TSRMLS_CC
))) {
899 if (Z_TYPE_P(zheader
) == IS_ARRAY
) {
902 php_http_array_hashkey_t key
= php_http_array_hashkey_init(0);
904 FOREACH_KEYVAL(pos
, zheader
, key
, val
) {
905 if (key
.type
== HASH_KEY_IS_STRING
) {
906 php_http_env_set_response_header_value(0, key
.str
, key
.len
- 1, *val
, 1 TSRMLS_CC
);
910 zval_ptr_dtor(&zheader
);
913 body
= ((php_http_message_body_object_t
*) zend_object_store_get_object(zbody TSRMLS_CC
))->body
;
914 body_size
= php_http_message_body_size(body
);
915 php_http_env_set_response_header(0, ZEND_STRL("Accept-Ranges: bytes"), 1 TSRMLS_CC
);
916 zend_hash_init(&ranges
, 0, NULL
, ZVAL_PTR_DTOR
, 0);
917 range_status
= php_http_env_get_request_ranges(&ranges
, body_size TSRMLS_CC
);
919 switch (range_status
) {
920 case PHP_HTTP_RANGE_ERR
:
921 zend_hash_destroy(&ranges
);
922 if (!php_http_env_got_request_header(ZEND_STRL("If-Range") TSRMLS_CC
)) {
924 size_t cr_header_len
;
926 cr_header_len
= spprintf(&cr_header_str
, 0, "Content-Range: bytes */%zu", body_size
);
927 php_http_env_set_response_header(416, cr_header_str
, cr_header_len
, 1 TSRMLS_CC
);
928 efree(cr_header_str
);
930 zval_ptr_dtor(&zbody
);
936 case PHP_HTTP_RANGE_NO
:
937 /* send full entity */
938 zend_hash_destroy(&ranges
);
941 case PHP_HTTP_RANGE_OK
:
942 /* send content-range response */
943 if (PHP_HTTP_CACHE_MISS
== php_http_env_is_response_cached_by_etag(container
, ZEND_STRL("If-Range") TSRMLS_CC
)
944 || PHP_HTTP_CACHE_MISS
== php_http_env_is_response_cached_by_last_modified(container
, ZEND_STRL("If-Range") TSRMLS_CC
)
946 /* send full entity */
947 zend_hash_destroy(&ranges
);
950 if (PHP_HTTP_CACHE_MISS
== php_http_env_is_response_cached_by_etag(container
, ZEND_STRL("If-Match") TSRMLS_CC
)
951 || PHP_HTTP_CACHE_MISS
== php_http_env_is_response_cached_by_last_modified(container
, ZEND_STRL("If-Unmodified-Since") TSRMLS_CC
)
952 || PHP_HTTP_CACHE_MISS
== php_http_env_is_response_cached_by_last_modified(container
, ZEND_STRL("Unless-Modified-Since") TSRMLS_CC
)
954 zend_hash_destroy(&ranges
);
955 php_http_env_set_response_code(412 TSRMLS_CC
);
957 zval_ptr_dtor(&zbody
);
961 if (zend_hash_num_elements(&ranges
) == 1) {
963 zval
**range
, **begin
, **end
;
965 if (SUCCESS
!= zend_hash_index_find(&ranges
, 0, (void *) &range
)
966 || SUCCESS
!= zend_hash_index_find(Z_ARRVAL_PP(range
), 0, (void *) &begin
)
967 || SUCCESS
!= zend_hash_index_find(Z_ARRVAL_PP(range
), 1, (void *) &end
)
969 /* this should never happen */
970 zend_hash_destroy(&ranges
);
971 php_http_env_set_response_code(500 TSRMLS_CC
);
973 zval_ptr_dtor(&zbody
);
978 size_t cr_header_len
;
980 cr_header_len
= spprintf(&cr_header_str
, 0, "Content-Range: bytes %ld-%ld/%zu", Z_LVAL_PP(begin
), Z_LVAL_PP(end
), body_size
);
981 php_http_env_set_response_header(206, cr_header_str
, cr_header_len
, 1 TSRMLS_CC
);
982 efree(cr_header_str
);
985 php_http_message_body_to_callback(body
, output
, &ctx
, Z_LVAL_PP(begin
), Z_LVAL_PP(end
) - Z_LVAL_PP(begin
) + 1);
986 output(&ctx
, NULL
, 0 TSRMLS_CC
);
987 zend_hash_destroy(&ranges
);
989 zval_ptr_dtor(&zbody
);
994 /* send multipart/byte-ranges message */
997 php_http_buffer_t preface
;
999 char *content_type
= "application/octet-stream";
1002 if ((zct
= get_container_value(container
, ZEND_STRL("contentType") TSRMLS_CC
))) {
1003 zval
*zct_copy
= php_http_ztyp(IS_STRING
, zct
);
1005 zval_ptr_dtor(&zct
);
1006 if (Z_STRLEN_P(zct_copy
)) {
1007 content_type
= estrndup(Z_STRVAL_P(zct_copy
), Z_STRLEN_P(zct_copy
));
1011 zval_ptr_dtor(&zct
);
1014 php_http_boundary(boundary
, sizeof(boundary
));
1015 php_http_env_set_response_header_format(206, 1 TSRMLS_CC
, "Content-Type: multipart/byteranges; boundary=%s", boundary
);
1017 php_http_buffer_init(&preface
);
1018 FOREACH_HASH_VAL(pos
, &ranges
, chunk
) {
1019 zval
**begin
, **end
;
1021 if (IS_ARRAY
== Z_TYPE_PP(chunk
)
1022 && SUCCESS
== zend_hash_index_find(Z_ARRVAL_PP(chunk
), 0, (void *) &begin
)
1023 && IS_LONG
== Z_TYPE_PP(begin
)
1024 && SUCCESS
== zend_hash_index_find(Z_ARRVAL_PP(chunk
), 1, (void *) &end
)
1025 && IS_LONG
== Z_TYPE_PP(end
)
1027 php_http_buffer_appendf(&preface
,
1029 "--%s" PHP_HTTP_CRLF
1030 "Content-Type: %s" PHP_HTTP_CRLF
1031 "Content-Range: bytes %ld-%ld/%zu" PHP_HTTP_CRLF
,
1039 php_http_buffer_fix(&preface
);
1040 output(&ctx
, PHP_HTTP_BUFFER_VAL(&preface
), PHP_HTTP_BUFFER_LEN(&preface
) TSRMLS_CC
);
1041 php_http_buffer_reset(&preface
);
1043 php_http_message_body_to_callback(body
, output
, &ctx
, Z_LVAL_PP(begin
), Z_LVAL_PP(end
) - Z_LVAL_PP(begin
) + 1);
1046 php_http_buffer_appendf(&preface
, PHP_HTTP_CRLF
"--%s--", boundary
);
1047 php_http_buffer_fix(&preface
);
1048 output(&ctx
, PHP_HTTP_BUFFER_VAL(&preface
), PHP_HTTP_BUFFER_LEN(&preface
) TSRMLS_CC
);
1049 php_http_buffer_dtor(&preface
);
1050 output(&ctx
, NULL
, 0 TSRMLS_CC
);
1051 zend_hash_destroy(&ranges
);
1053 zval_ptr_dtor(&zbody
);
1060 switch (php_http_env_is_response_cached_by_etag(container
, ZEND_STRL("If-None-Match"))) {
1061 case PHP_HTTP_CACHE_MISS
:
1064 case PHP_HTTP_CACHE_NO
:
1065 if (PHP_HTTP_CACHE_HIT
!= php_http_env_is_response_cached_by_last_modified(container
, ZEND_STRL("If-Modified-Since"))) {
1069 case PHP_HTTP_CACHE_HIT
:
1070 php_http_env_set_response_code(304 TSRMLS_CC
);
1072 zval_ptr_dtor(&zbody
);
1077 php_http_message_body_to_callback(body
, output
, &ctx
, 0, 0);
1078 output(&ctx
, NULL
, 0 TSRMLS_CC
);
1081 zval_ptr_dtor(&zbody
);
1086 static PHP_HTTP_STRLIST(php_http_env_response_status
) =
1087 PHP_HTTP_STRLIST_ITEM("Continue")
1088 PHP_HTTP_STRLIST_ITEM("Switching Protocols")
1089 PHP_HTTP_STRLIST_NEXT
1090 PHP_HTTP_STRLIST_ITEM("OK")
1091 PHP_HTTP_STRLIST_ITEM("Created")
1092 PHP_HTTP_STRLIST_ITEM("Accepted")
1093 PHP_HTTP_STRLIST_ITEM("Non-Authoritative Information")
1094 PHP_HTTP_STRLIST_ITEM("No Content")
1095 PHP_HTTP_STRLIST_ITEM("Reset Content")
1096 PHP_HTTP_STRLIST_ITEM("Partial Content")
1097 PHP_HTTP_STRLIST_NEXT
1098 PHP_HTTP_STRLIST_ITEM("Multiple Choices")
1099 PHP_HTTP_STRLIST_ITEM("Moved Permanently")
1100 PHP_HTTP_STRLIST_ITEM("Found")
1101 PHP_HTTP_STRLIST_ITEM("See Other")
1102 PHP_HTTP_STRLIST_ITEM("Not Modified")
1103 PHP_HTTP_STRLIST_ITEM("Use Proxy")
1104 PHP_HTTP_STRLIST_ITEM("(Unused)")
1105 PHP_HTTP_STRLIST_ITEM("Temporary Redirect")
1106 PHP_HTTP_STRLIST_NEXT
1107 PHP_HTTP_STRLIST_ITEM("Bad Request")
1108 PHP_HTTP_STRLIST_ITEM("Unauthorized")
1109 PHP_HTTP_STRLIST_ITEM("Payment Required")
1110 PHP_HTTP_STRLIST_ITEM("Forbidden")
1111 PHP_HTTP_STRLIST_ITEM("Not Found")
1112 PHP_HTTP_STRLIST_ITEM("Method Not Allowed")
1113 PHP_HTTP_STRLIST_ITEM("Not Acceptable")
1114 PHP_HTTP_STRLIST_ITEM("Proxy Authentication Required")
1115 PHP_HTTP_STRLIST_ITEM("Request Timeout")
1116 PHP_HTTP_STRLIST_ITEM("Conflict")
1117 PHP_HTTP_STRLIST_ITEM("Gone")
1118 PHP_HTTP_STRLIST_ITEM("Length Required")
1119 PHP_HTTP_STRLIST_ITEM("Precondition Failed")
1120 PHP_HTTP_STRLIST_ITEM("Request Entity Too Large")
1121 PHP_HTTP_STRLIST_ITEM("Request URI Too Long")
1122 PHP_HTTP_STRLIST_ITEM("Unsupported Media Type")
1123 PHP_HTTP_STRLIST_ITEM("Requested Range Not Satisfiable")
1124 PHP_HTTP_STRLIST_ITEM("Expectation Failed")
1125 PHP_HTTP_STRLIST_NEXT
1126 PHP_HTTP_STRLIST_ITEM("Internal Server Error")
1127 PHP_HTTP_STRLIST_ITEM("Not Implemented")
1128 PHP_HTTP_STRLIST_ITEM("Bad Gateway")
1129 PHP_HTTP_STRLIST_ITEM("Service Unavailable")
1130 PHP_HTTP_STRLIST_ITEM("Gateway Timeout")
1131 PHP_HTTP_STRLIST_ITEM("HTTP Version Not Supported")
1132 PHP_HTTP_STRLIST_STOP
1135 PHP_HTTP_API
const char *php_http_env_get_response_status_for_code(unsigned code
)
1137 return php_http_strlist_find(php_http_env_response_status
, 100, code
);
1140 zend_class_entry
*php_http_env_class_entry
;
1142 #define PHP_HTTP_BEGIN_ARGS(method, req_args) PHP_HTTP_BEGIN_ARGS_EX(HttpEnv, method, 0, req_args)
1143 #define PHP_HTTP_EMPTY_ARGS(method) PHP_HTTP_EMPTY_ARGS_EX(HttpEnv, method, 0)
1144 #define PHP_HTTP_ENV_ME(method) PHP_ME(HttpEnv, method, PHP_HTTP_ARGS(HttpEnv, method), ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
1146 PHP_HTTP_BEGIN_ARGS(getRequestHeader
, 0)
1147 PHP_HTTP_ARG_VAL(header_name
, 0)
1150 PHP_HTTP_BEGIN_ARGS(getRequestBody
, 0)
1151 PHP_HTTP_ARG_VAL(body_class_name
, 0)
1154 PHP_HTTP_BEGIN_ARGS(getResponseStatusForCode
, 1)
1155 PHP_HTTP_ARG_VAL(code
, 0)
1158 PHP_HTTP_BEGIN_ARGS(getResponseHeader
, 0)
1159 PHP_HTTP_ARG_VAL(header_name
, 0)
1162 PHP_HTTP_EMPTY_ARGS(getResponseCode
);
1164 PHP_HTTP_BEGIN_ARGS(setResponseHeader
, 1)
1165 PHP_HTTP_ARG_VAL(header_name
, 0)
1166 PHP_HTTP_ARG_VAL(header_value
, 0)
1167 PHP_HTTP_ARG_VAL(response_code
, 0)
1168 PHP_HTTP_ARG_VAL(replace_header
, 0)
1171 PHP_HTTP_BEGIN_ARGS(setResponseCode
, 1)
1172 PHP_HTTP_ARG_VAL(code
, 0)
1175 PHP_HTTP_BEGIN_ARGS(negotiateLanguage
, 0)
1176 PHP_HTTP_ARG_VAL(supported
, 0)
1177 PHP_HTTP_ARG_VAL(result_array
, 1)
1180 PHP_HTTP_BEGIN_ARGS(negotiateContentType
, 0)
1181 PHP_HTTP_ARG_VAL(supported
, 0)
1182 PHP_HTTP_ARG_VAL(result_array
, 1)
1185 PHP_HTTP_BEGIN_ARGS(negotiateCharset
, 0)
1186 PHP_HTTP_ARG_VAL(supported
, 0)
1187 PHP_HTTP_ARG_VAL(result_array
, 1)
1190 PHP_HTTP_BEGIN_ARGS(negotiate
, 0)
1191 PHP_HTTP_ARG_VAL(value
, 0)
1192 PHP_HTTP_ARG_VAL(supported
, 0)
1193 PHP_HTTP_ARG_VAL(result_array
, 1)
1196 PHP_HTTP_EMPTY_ARGS(persistentHandlesStat
);
1198 PHP_HTTP_BEGIN_ARGS(persistentHandlesClean
, 0)
1199 PHP_HTTP_ARG_VAL(name
, 0)
1200 PHP_HTTP_ARG_VAL(ident
, 0)
1203 PHP_HTTP_BEGIN_ARGS(persistentHandlesIdent
, 0)
1204 PHP_HTTP_ARG_VAL(name
, 0)
1207 zend_function_entry php_http_env_method_entry
[] = {
1208 PHP_HTTP_ENV_ME(getRequestHeader
)
1209 PHP_HTTP_ENV_ME(getRequestBody
)
1211 PHP_HTTP_ENV_ME(getResponseStatusForCode
)
1213 PHP_HTTP_ENV_ME(getResponseHeader
)
1214 PHP_HTTP_ENV_ME(getResponseCode
)
1215 PHP_HTTP_ENV_ME(setResponseHeader
)
1216 PHP_HTTP_ENV_ME(setResponseCode
)
1218 PHP_HTTP_ENV_ME(negotiateLanguage
)
1219 PHP_HTTP_ENV_ME(negotiateContentType
)
1220 PHP_HTTP_ENV_ME(negotiateCharset
)
1221 PHP_HTTP_ENV_ME(negotiate
)
1223 PHP_HTTP_ENV_ME(persistentHandlesStat
)
1224 PHP_HTTP_ENV_ME(persistentHandlesClean
)
1225 PHP_HTTP_ENV_ME(persistentHandlesIdent
)
1227 EMPTY_FUNCTION_ENTRY
1230 PHP_METHOD(HttpEnv
, getRequestHeader
)
1232 char *header_name_str
;
1233 int header_name_len
;
1235 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "|s!", &header_name_str
, &header_name_len
)) {
1236 if (header_name_str
&& header_name_len
) {
1237 char *header_value
= php_http_env_get_request_header(header_name_str
, header_name_len TSRMLS_CC
);
1240 RETURN_STRING(header_value
, 0);
1244 array_init(return_value
);
1245 php_http_env_get_request_headers(Z_ARRVAL_P(return_value
) TSRMLS_CC
);
1252 PHP_METHOD(HttpEnv
, getRequestBody
)
1254 with_error_handling(EH_THROW
, php_http_exception_class_entry
) {
1255 zend_class_entry
*class_entry
= php_http_message_body_class_entry
;
1257 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "|C", &class_entry
)) {
1258 zend_object_value ov
;
1259 php_http_message_body_t
*body
= php_http_env_get_request_body(TSRMLS_C
);
1261 if (SUCCESS
== php_http_new(&ov
, class_entry
, (php_http_new_t
) php_http_message_body_object_new_ex
, php_http_message_body_class_entry
, body
, NULL TSRMLS_CC
)) {
1262 RETURN_OBJVAL(ov
, 0);
1265 } end_error_handling();
1268 PHP_METHOD(HttpEnv
, getResponseStatusForCode
)
1272 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "l", &code
)) {
1273 RETURN_STRING(php_http_env_get_response_status_for_code(code
), 1);
1278 PHP_METHOD(HttpEnv
, getResponseHeader
)
1280 char *header_name_str
;
1281 int header_name_len
;
1283 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "|s!", &header_name_str
, &header_name_len
)) {
1284 if (header_name_str
&& header_name_len
) {
1285 char *header_value
= php_http_env_get_response_header(header_name_str
, header_name_len TSRMLS_CC
);
1288 RETURN_STRING(header_value
, 0);
1292 array_init(return_value
);
1293 php_http_env_get_response_headers(Z_ARRVAL_P(return_value
) TSRMLS_CC
);
1300 PHP_METHOD(HttpEnv
, getResponseCode
)
1302 if (SUCCESS
== zend_parse_parameters_none()) {
1303 RETURN_LONG(php_http_env_get_response_code(TSRMLS_C
));
1308 PHP_METHOD(HttpEnv
, setResponseHeader
)
1310 char *header_name_str
;
1311 int header_name_len
;
1314 zend_bool replace_header
= 1;
1316 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "s|z!lb", &header_name_str
, &header_name_len
, &header_value
, &code
, &replace_header
)) {
1317 RETURN_SUCCESS(php_http_env_set_response_header_value(code
, header_name_str
, header_name_len
, header_value
, replace_header TSRMLS_CC
));
1322 PHP_METHOD(HttpEnv
, setResponseCode
)
1326 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "l", &code
)) {
1327 RETURN_SUCCESS(php_http_env_set_response_code(code TSRMLS_CC
));
1333 #define PHP_HTTP_DO_NEGOTIATE_DEFAULT(supported) \
1337 zend_hash_internal_pointer_reset((supported)); \
1338 if (SUCCESS == zend_hash_get_current_data((supported), (void *) &value)) { \
1339 RETVAL_ZVAL(*value, 1, 0); \
1345 #define PHP_HTTP_DO_NEGOTIATE_HANDLE_DEFAULT(supported, rs_array) \
1346 PHP_HTTP_DO_NEGOTIATE_DEFAULT(supported); \
1351 FOREACH_HASH_VAL(pos, supported, value_ptr) { \
1352 zval *value = php_http_ztyp(IS_STRING, *value_ptr); \
1353 add_assoc_double(rs_array, Z_STRVAL_P(value), 1.0); \
1354 zval_ptr_dtor(&value); \
1358 #define PHP_HTTP_DO_NEGOTIATE_HANDLE_RESULT(result, supported, rs_array) \
1364 if (zend_hash_num_elements(result) && HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(result, &key, &key_len, &idx, 1, NULL)) { \
1365 RETVAL_STRINGL(key, key_len-1, 0); \
1367 PHP_HTTP_DO_NEGOTIATE_DEFAULT(supported); \
1371 zend_hash_copy(Z_ARRVAL_P(rs_array), result, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); \
1374 zend_hash_destroy(result); \
1375 FREE_HASHTABLE(result); \
1378 #define PHP_HTTP_DO_NEGOTIATE(type, supported, rs_array) \
1380 HashTable *result; \
1381 if ((result = php_http_negotiate_ ##type(supported))) { \
1382 PHP_HTTP_DO_NEGOTIATE_HANDLE_RESULT(result, supported, rs_array); \
1384 PHP_HTTP_DO_NEGOTIATE_HANDLE_DEFAULT(supported, rs_array); \
1388 PHP_METHOD(HttpEnv
, negotiateLanguage
)
1390 HashTable
*supported
;
1391 zval
*rs_array
= NULL
;
1393 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "H|z", &supported
, &rs_array
)) {
1395 zval_dtor(rs_array
);
1396 array_init(rs_array
);
1399 PHP_HTTP_DO_NEGOTIATE(language
, supported
, rs_array
);
1404 PHP_METHOD(HttpEnv
, negotiateCharset
)
1406 HashTable
*supported
;
1407 zval
*rs_array
= NULL
;
1409 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "H|z", &supported
, &rs_array
)) {
1411 zval_dtor(rs_array
);
1412 array_init(rs_array
);
1414 PHP_HTTP_DO_NEGOTIATE(charset
, supported
, rs_array
);
1419 PHP_METHOD(HttpEnv
, negotiateContentType
)
1421 HashTable
*supported
;
1422 zval
*rs_array
= NULL
;
1424 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "H|z", &supported
, &rs_array
)) {
1426 zval_dtor(rs_array
);
1427 array_init(rs_array
);
1429 PHP_HTTP_DO_NEGOTIATE(content_type
, supported
, rs_array
);
1434 PHP_METHOD(HttpEnv
, negotiate
)
1436 HashTable
*supported
;
1437 zval
*rs_array
= NULL
;
1441 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "sH|z", &value_str
, &value_len
, &supported
, &rs_array
)) {
1445 zval_dtor(rs_array
);
1446 array_init(rs_array
);
1449 if ((rs
= php_http_negotiate(value_str
, supported
, php_http_negotiate_default_func
))) {
1450 PHP_HTTP_DO_NEGOTIATE_HANDLE_RESULT(rs
, supported
, rs_array
);
1452 PHP_HTTP_DO_NEGOTIATE_HANDLE_DEFAULT(supported
, rs_array
);
1458 PHP_METHOD(HttpEnv
, persistentHandlesStat
)
1460 if (SUCCESS
== zend_parse_parameters_none()) {
1461 object_init(return_value
);
1462 if (php_http_persistent_handle_statall(HASH_OF(return_value
))) {
1465 zval_dtor(return_value
);
1470 PHP_METHOD(HttpEnv
, persistentHandlesClean
)
1472 char *name_str
= NULL
, *ident_str
= NULL
;
1473 int name_len
= 0, ident_len
= 0;
1475 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "|ss", &name_str
, &name_len
, &ident_str
, &ident_len
)) {
1476 php_http_persistent_handle_cleanup(name_str
, name_len
, ident_str
, ident_len TSRMLS_CC
);
1480 PHP_METHOD(HttpEnv
, persistentHandlesIdent
)
1482 char *ident_str
= NULL
;
1485 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "|s", &ident_str
, &ident_len
)) {
1486 RETVAL_STRING(zend_ini_string(ZEND_STRS("http.persistent.handles.ident"), 0), 1);
1487 if (ident_str
&& ident_len
) {
1488 zend_alter_ini_entry(ZEND_STRS("http.persistent.handles.ident"), ident_str
, ident_len
, ZEND_INI_USER
, PHP_INI_STAGE_RUNTIME
);
1495 zend_class_entry
*php_http_env_request_class_entry
;
1497 #undef PHP_HTTP_BEGIN_ARGS
1498 #undef PHP_HTTP_EMPTY_ARGS
1499 #define PHP_HTTP_BEGIN_ARGS(method, req_args) PHP_HTTP_BEGIN_ARGS_EX(HttpEnvRequest, method, 0, req_args)
1500 #define PHP_HTTP_EMPTY_ARGS(method) PHP_HTTP_EMPTY_ARGS_EX(HttpEnvRequest, method, 0)
1501 #define PHP_HTTP_ENV_REQUEST_ME(method, visibility) PHP_ME(HttpEnvRequest, method, PHP_HTTP_ARGS(HttpEnvRequest, method), visibility)
1503 PHP_HTTP_EMPTY_ARGS(__construct
);
1505 zend_function_entry php_http_env_request_method_entry
[] = {
1506 PHP_HTTP_ENV_REQUEST_ME(__construct
, ZEND_ACC_PUBLIC
|ZEND_ACC_CTOR
)
1508 EMPTY_FUNCTION_ENTRY
1511 PHP_METHOD(HttpEnvRequest
, __construct
)
1513 with_error_handling(EH_THROW
, php_http_exception_class_entry
) {
1514 if (SUCCESS
== zend_parse_parameters_none()) {
1515 php_http_message_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
1517 with_error_handling(EH_THROW
, php_http_exception_class_entry
) {
1518 obj
->message
= php_http_message_init_env(obj
->message
, PHP_HTTP_REQUEST TSRMLS_CC
);
1519 } end_error_handling();
1521 } end_error_handling();
1525 zend_class_entry
*php_http_env_response_class_entry
;
1527 #undef PHP_HTTP_BEGIN_ARGS
1528 #undef PHP_HTTP_EMPTY_ARGS
1529 #define PHP_HTTP_BEGIN_ARGS(method, req_args) PHP_HTTP_BEGIN_ARGS_EX(HttpEnvResponse, method, 0, req_args)
1530 #define PHP_HTTP_EMPTY_ARGS(method) PHP_HTTP_EMPTY_ARGS_EX(HttpEnvResponse, method, 0)
1531 #define PHP_HTTP_ENV_RESPONSE_ME(method, visibility) PHP_ME(HttpEnvResponse, method, PHP_HTTP_ARGS(HttpEnvResponse, method), visibility)
1533 PHP_HTTP_EMPTY_ARGS(__construct
);
1535 PHP_HTTP_BEGIN_ARGS(setContentType
, 1)
1536 PHP_HTTP_ARG_VAL(content_type
, 0)
1539 PHP_HTTP_BEGIN_ARGS(setContentDisposition
, 1)
1540 PHP_HTTP_ARG_VAL(content_disposition
, 0)
1541 PHP_HTTP_ARG_VAL(filename
, 0)
1544 PHP_HTTP_BEGIN_ARGS(setCacheControl
, 1)
1545 PHP_HTTP_ARG_VAL(cache_control
, 0)
1548 PHP_HTTP_BEGIN_ARGS(setLastModified
, 1)
1549 PHP_HTTP_ARG_VAL(last_modified
, 0)
1552 PHP_HTTP_BEGIN_ARGS(isCachedByLastModified
, 0)
1553 PHP_HTTP_ARG_VAL(header_name
, 0)
1556 PHP_HTTP_BEGIN_ARGS(setEtag
, 1)
1557 PHP_HTTP_ARG_VAL(etag
, 0)
1560 PHP_HTTP_BEGIN_ARGS(isCachedByEtag
, 0)
1561 PHP_HTTP_ARG_VAL(header_name
, 0)
1564 PHP_HTTP_BEGIN_ARGS(setThrottleRate
, 1)
1565 PHP_HTTP_ARG_VAL(chunk_size
, 0)
1566 PHP_HTTP_ARG_VAL(delay
, 0)
1569 PHP_HTTP_EMPTY_ARGS(send
);
1572 zend_function_entry php_http_env_response_method_entry
[] = {
1573 PHP_HTTP_ENV_RESPONSE_ME(__construct
, ZEND_ACC_PUBLIC
|ZEND_ACC_CTOR
)
1574 PHP_HTTP_ENV_RESPONSE_ME(setContentType
, ZEND_ACC_PUBLIC
)
1575 PHP_HTTP_ENV_RESPONSE_ME(setContentDisposition
, ZEND_ACC_PUBLIC
)
1576 PHP_HTTP_ENV_RESPONSE_ME(setCacheControl
, ZEND_ACC_PUBLIC
)
1577 PHP_HTTP_ENV_RESPONSE_ME(setLastModified
, ZEND_ACC_PUBLIC
)
1578 PHP_HTTP_ENV_RESPONSE_ME(isCachedByLastModified
, ZEND_ACC_PUBLIC
)
1579 PHP_HTTP_ENV_RESPONSE_ME(setEtag
, ZEND_ACC_PUBLIC
)
1580 PHP_HTTP_ENV_RESPONSE_ME(isCachedByEtag
, ZEND_ACC_PUBLIC
)
1581 PHP_HTTP_ENV_RESPONSE_ME(setThrottleRate
, ZEND_ACC_PUBLIC
)
1583 PHP_HTTP_ENV_RESPONSE_ME(send
, ZEND_ACC_PUBLIC
)
1585 EMPTY_FUNCTION_ENTRY
1589 PHP_METHOD(HttpEnvResponse
, __construct
)
1591 with_error_handling(EH_THROW
, php_http_exception_class_entry
) {
1592 if (SUCCESS
== zend_parse_parameters_none()) {
1593 php_http_message_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
1595 with_error_handling(EH_THROW
, php_http_exception_class_entry
) {
1596 obj
->message
= php_http_message_init_env(obj
->message
, PHP_HTTP_RESPONSE TSRMLS_CC
);
1597 } end_error_handling();
1599 } end_error_handling();
1603 PHP_METHOD(HttpEnvResponse
, setContentType
)
1608 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "s", &ct_str
, &ct_len
)) {
1609 RETURN_SUCCESS(php_http_env_set_response_content_type(getThis(), ct_str
, ct_len
, NULL TSRMLS_CC
));
1614 PHP_METHOD(HttpEnvResponse
, setContentDisposition
)
1617 char *file_str
= NULL
;
1620 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "l|s!", &cd
, &file_str
, &file_len
)) {
1621 RETURN_SUCCESS(php_http_env_set_response_content_disposition(getThis(), cd
, file_str
, file_len
, NULL TSRMLS_CC
));
1626 PHP_METHOD(HttpEnvResponse
, setCacheControl
)
1631 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "s", &cc_str
, &cc_len
)) {
1632 RETURN_SUCCESS(php_http_env_set_response_cache_control(getThis(), cc_str
, cc_len
, NULL TSRMLS_CC
));
1637 PHP_METHOD(HttpEnvResponse
, setLastModified
)
1641 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "l", &last_modified
)) {
1642 RETURN_SUCCESS(php_http_env_set_response_last_modified(getThis(), last_modified
, NULL TSRMLS_CC
));
1647 PHP_METHOD(HttpEnvResponse
, isCachedByLastModified
)
1649 char *header_name_str
= NULL
;
1650 int header_name_len
= 0;
1652 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "|s!", &header_name_str
, &header_name_len
)) {
1653 if (!header_name_str
|| !header_name_len
) {
1654 header_name_str
= "If-Modified-Since";
1655 header_name_len
= lenof("If-Modified-Since");
1657 RETURN_LONG(php_http_env_is_response_cached_by_last_modified(getThis(), header_name_str
, header_name_len TSRMLS_CC
));
1662 PHP_METHOD(HttpEnvResponse
, setEtag
)
1667 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "s!", &etag_str
, &etag_len
)) {
1668 RETURN_SUCCESS(php_http_env_set_response_etag(getThis(), etag_str
, etag_len
, NULL TSRMLS_CC
));
1673 PHP_METHOD(HttpEnvResponse
, isCachedByEtag
)
1675 char *header_name_str
= NULL
;
1676 int header_name_len
= 0;
1678 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "s", &header_name_str
, &header_name_len
)) {
1679 if (!header_name_str
|| !header_name_len
) {
1680 header_name_str
= "If-None-Match";
1681 header_name_len
= lenof("If-None-Match");
1683 RETURN_LONG(php_http_env_is_response_cached_by_etag(getThis(), header_name_str
, header_name_len TSRMLS_CC
));
1688 PHP_METHOD(HttpEnvResponse
, setThrottleRate
)
1693 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "l|d", &chunk_size
, &delay
)) {
1694 php_http_env_set_response_throttle_rate(getThis(), chunk_size
, delay TSRMLS_CC
);
1700 PHP_METHOD(HttpEnvResponse
, send
)
1702 if (SUCCESS
== zend_parse_parameters_none()) {
1703 RETURN_SUCCESS(php_http_env_send_response(getThis() TSRMLS_CC
));
1709 PHP_MINIT_FUNCTION(http_env
)
1711 PHP_HTTP_REGISTER_CLASS(http
, Env
, http_env
, NULL
, 0);
1712 PHP_HTTP_REGISTER_CLASS(http
\\env
, Request
, http_env_request
, php_http_message_class_entry
, 0);
1713 PHP_HTTP_REGISTER_CLASS(http
\\env
, Response
, http_env_response
, php_http_message_class_entry
, 0);
1715 zend_declare_class_constant_long(php_http_env_response_class_entry
, ZEND_STRL("CONTENT_DISPOSITION_INLINE"), PHP_HTTP_CONTENT_DISPOSITION_INLINE TSRMLS_CC
);
1716 zend_declare_class_constant_long(php_http_env_response_class_entry
, ZEND_STRL("CONTENT_DISPOSITION_ATTACHMENT"), PHP_HTTP_CONTENT_DISPOSITION_ATTACHMENT TSRMLS_CC
);
1718 zend_declare_class_constant_long(php_http_env_response_class_entry
, ZEND_STRL("CACHE_NO"), PHP_HTTP_CACHE_NO TSRMLS_CC
);
1719 zend_declare_class_constant_long(php_http_env_response_class_entry
, ZEND_STRL("CACHE_HIT"), PHP_HTTP_CACHE_HIT TSRMLS_CC
);
1720 zend_declare_class_constant_long(php_http_env_response_class_entry
, ZEND_STRL("CACHE_MISS"), PHP_HTTP_CACHE_MISS TSRMLS_CC
);
1722 zend_declare_property_null(php_http_env_response_class_entry
, ZEND_STRL("contentType"), ZEND_ACC_PROTECTED TSRMLS_CC
);
1723 zend_declare_property_null(php_http_env_response_class_entry
, ZEND_STRL("etag"), ZEND_ACC_PROTECTED TSRMLS_CC
);
1724 zend_declare_property_null(php_http_env_response_class_entry
, ZEND_STRL("lastModified"), ZEND_ACC_PROTECTED TSRMLS_CC
);
1725 zend_declare_property_null(php_http_env_response_class_entry
, ZEND_STRL("throttleDelay"), ZEND_ACC_PROTECTED TSRMLS_CC
);
1726 zend_declare_property_null(php_http_env_response_class_entry
, ZEND_STRL("throttleChunk"), ZEND_ACC_PROTECTED TSRMLS_CC
);
1737 * vim600: noet sw=4 ts=4 fdm=marker
1738 * vim<600: noet sw=4 ts=4