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-2014, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
13 #include "php_http_api.h"
15 static void set_option(zval
*options
, const char *name_str
, size_t name_len
, int type
, void *value_ptr
, size_t value_len TSRMLS_DC
)
17 if (Z_TYPE_P(options
) == IS_OBJECT
) {
21 zend_update_property_double(Z_OBJCE_P(options
), options
, name_str
, name_len
, *(double *)value_ptr TSRMLS_CC
);
24 zend_update_property_long(Z_OBJCE_P(options
), options
, name_str
, name_len
, *(long *)value_ptr TSRMLS_CC
);
27 zend_update_property_stringl(Z_OBJCE_P(options
), options
, name_str
, name_len
, value_ptr
, value_len TSRMLS_CC
);
31 zend_update_property(Z_OBJCE_P(options
), options
, name_str
, name_len
, value_ptr TSRMLS_CC
);
35 zend_update_property_null(Z_OBJCE_P(options
), options
, name_str
, name_len TSRMLS_CC
);
38 convert_to_array(options
);
42 add_assoc_double_ex(options
, name_str
, name_len
+ 1, *(double *)value_ptr
);
45 add_assoc_long_ex(options
, name_str
, name_len
+ 1, *(long *)value_ptr
);
48 char *value
= estrndup(value_ptr
, value_len
);
49 add_assoc_stringl_ex(options
, name_str
, name_len
+ 1, value
, value_len
, 0);
53 Z_ADDREF_P(value_ptr
);
54 add_assoc_zval_ex(options
, name_str
, name_len
+ 1, value_ptr
);
59 add_assoc_null_ex(options
, name_str
, name_len
+ 1);
63 static zval
*get_option(zval
*options
, const char *name_str
, size_t name_len TSRMLS_DC
)
67 if (Z_TYPE_P(options
) == IS_OBJECT
) {
68 val
= zend_read_property(Z_OBJCE_P(options
), options
, name_str
, name_len
, 0 TSRMLS_CC
);
70 if (SUCCESS
== zend_symtable_find(Z_ARRVAL_P(options
), name_str
, name_len
+ 1, (void *) &valptr
)) {
81 static php_http_message_body_t
*get_body(zval
*options TSRMLS_DC
)
84 php_http_message_body_t
*body
= NULL
;
86 if ((zbody
= get_option(options
, ZEND_STRL("body") TSRMLS_CC
))) {
87 if ((Z_TYPE_P(zbody
) == IS_OBJECT
) && instanceof_function(Z_OBJCE_P(zbody
), php_http_message_body_class_entry TSRMLS_CC
)) {
88 php_http_message_body_object_t
*body_obj
= zend_object_store_get_object(zbody TSRMLS_CC
);
90 body
= body_obj
->body
;
92 zval_ptr_dtor(&zbody
);
97 static php_http_message_t
*get_request(zval
*options TSRMLS_DC
)
100 php_http_message_t
*request
= NULL
;
102 if ((zrequest
= get_option(options
, ZEND_STRL("request") TSRMLS_CC
))) {
103 if (Z_TYPE_P(zrequest
) == IS_OBJECT
&& instanceof_function(Z_OBJCE_P(zrequest
), php_http_message_class_entry TSRMLS_CC
)) {
104 php_http_message_object_t
*request_obj
= zend_object_store_get_object(zrequest TSRMLS_CC
);
106 request
= request_obj
->message
;
108 zval_ptr_dtor(&zrequest
);
113 static void set_cookie(zval
*options
, zval
*zcookie_new TSRMLS_DC
)
117 php_http_array_hashkey_t key
= php_http_array_hashkey_init(0);
118 php_http_cookie_object_t
*obj
= zend_object_store_get_object(zcookie_new TSRMLS_CC
);
120 zcookies_set
= get_option(options
, ZEND_STRL("cookies") TSRMLS_CC
);
121 if (!zcookies_set
|| Z_TYPE_P(zcookies_set
) != IS_ARRAY
) {
123 zval_ptr_dtor(&zcookies_set
);
125 MAKE_STD_ZVAL(zcookies_set
);
126 array_init_size(zcookies_set
, zend_hash_num_elements(&obj
->list
->cookies
));
128 SEPARATE_ZVAL(&zcookies_set
);
131 FOREACH_HASH_KEY(pos
, &obj
->list
->cookies
, key
) {
132 Z_ADDREF_P(zcookie_new
);
133 if (key
.type
== HASH_KEY_IS_STRING
) {
134 add_assoc_zval_ex(zcookies_set
, key
.str
, key
.len
, zcookie_new
);
136 add_index_zval(zcookies_set
, key
.num
, zcookie_new
);
140 set_option(options
, ZEND_STRL("cookies"), IS_ARRAY
, zcookies_set
, 0 TSRMLS_CC
);
141 zval_ptr_dtor(&zcookies_set
);
144 php_http_cache_status_t
php_http_env_is_response_cached_by_etag(zval
*options
, const char *header_str
, size_t header_len
, php_http_message_t
*request TSRMLS_DC
)
146 php_http_cache_status_t ret
= PHP_HTTP_CACHE_NO
;
148 char *header
= NULL
, *etag
;
149 php_http_message_body_t
*body
;
153 if (!(body
= get_body(options TSRMLS_CC
))) {
157 if ((zetag
= get_option(options
, ZEND_STRL("etag") TSRMLS_CC
))) {
158 zval
*zetag_copy
= php_http_ztyp(IS_STRING
, zetag
);
159 zval_ptr_dtor(&zetag
);
163 if (zetag
&& Z_STRLEN_P(zetag
)) {
164 etag
= Z_STRVAL_P(zetag
);
165 } else if ((etag
= php_http_message_body_etag(body
))) {
166 set_option(options
, ZEND_STRL("etag"), IS_STRING
, etag
, strlen(etag
) TSRMLS_CC
);
171 zval_ptr_dtor(&zetag
);
174 if (etag
&& (header
= php_http_env_get_request_header(header_str
, header_len
, NULL
, request TSRMLS_CC
))) {
175 ret
= php_http_match(header
, etag
, PHP_HTTP_MATCH_WORD
) ? PHP_HTTP_CACHE_HIT
: PHP_HTTP_CACHE_MISS
;
186 php_http_cache_status_t
php_http_env_is_response_cached_by_last_modified(zval
*options
, const char *header_str
, size_t header_len
, php_http_message_t
*request TSRMLS_DC
)
188 php_http_cache_status_t ret
= PHP_HTTP_CACHE_NO
;
191 php_http_message_body_t
*body
;
194 if (!(body
= get_body(options TSRMLS_CC
))) {
198 if ((zlm
= get_option(options
, ZEND_STRL("lastModified") TSRMLS_CC
))) {
199 zval
*zlm_copy
= php_http_ztyp(IS_LONG
, zlm
);
204 if (zlm
&& Z_LVAL_P(zlm
) > 0) {
207 lm
= php_http_message_body_mtime(body
);
208 set_option(options
, ZEND_STRL("lastModified"), IS_LONG
, &lm
, 0 TSRMLS_CC
);
215 if ((header
= php_http_env_get_request_header(header_str
, header_len
, NULL
, request TSRMLS_CC
))) {
216 ums
= php_parse_date(header
, NULL
);
218 if (ums
> 0 && ums
>= lm
) {
219 ret
= PHP_HTTP_CACHE_HIT
;
221 ret
= PHP_HTTP_CACHE_MISS
;
229 static zend_bool
php_http_env_response_is_cacheable(php_http_env_response_t
*r
, php_http_message_t
*request
)
231 long status
= r
->ops
->get_status(r
);
232 TSRMLS_FETCH_FROM_CTX(r
->ts
);
234 if (status
&& status
/ 100 != 2) {
238 if (php_http_env_got_request_header(ZEND_STRL("Authorization"), request TSRMLS_CC
)) {
242 if (-1 == php_http_select_str(php_http_env_get_request_method(request TSRMLS_CC
), 2, "HEAD", "GET")) {
249 static size_t output(void *context
, char *buf
, size_t len TSRMLS_DC
)
251 php_http_env_response_t
*r
= context
;
253 if (SUCCESS
!= r
->ops
->write(r
, buf
, len
)) {
257 /* we really only need to flush when throttling is enabled,
258 because we push the data as fast as possible anyway if not */
259 if (r
->throttle
.delay
>= PHP_HTTP_DIFFSEC
) {
261 php_http_sleep(r
->throttle
.delay
);
266 #define php_http_env_response_send_done(r) php_http_env_response_send_data((r), NULL, 0)
267 static ZEND_RESULT_CODE
php_http_env_response_send_data(php_http_env_response_t
*r
, const char *buf
, size_t len
)
269 size_t chunks_sent
, chunk
= r
->throttle
.chunk
? r
->throttle
.chunk
: PHP_HTTP_SENDBUF_SIZE
;
270 TSRMLS_FETCH_FROM_CTX(r
->ts
);
272 if (r
->content
.encoder
) {
273 char *enc_str
= NULL
;
277 if (SUCCESS
!= php_http_encoding_stream_update(r
->content
.encoder
, buf
, len
, &enc_str
, &enc_len
)) {
281 if (SUCCESS
!= php_http_encoding_stream_finish(r
->content
.encoder
, &enc_str
, &enc_len
)) {
289 chunks_sent
= php_http_buffer_chunked_output(&r
->buffer
, enc_str
, enc_len
, buf
? chunk
: 0, output
, r TSRMLS_CC
);
292 chunks_sent
= php_http_buffer_chunked_output(&r
->buffer
, buf
, len
, buf
? chunk
: 0, output
, r TSRMLS_CC
);
295 return chunks_sent
!= (size_t) -1 ? SUCCESS
: FAILURE
;
298 php_http_env_response_t
*php_http_env_response_init(php_http_env_response_t
*r
, zval
*options
, php_http_env_response_ops_t
*ops
, void *init_arg TSRMLS_DC
)
303 r
= emalloc(sizeof(*r
));
305 memset(r
, 0, sizeof(*r
));
310 r
->ops
= php_http_env_response_get_sapi_ops();
313 r
->buffer
= php_http_buffer_init(NULL
);
316 r
->options
= options
;
318 TSRMLS_SET_CTX(r
->ts
);
320 if (r
->ops
->init
&& (SUCCESS
!= r
->ops
->init(r
, init_arg
))) {
322 php_http_env_response_free(&r
);
324 php_http_env_response_dtor(r
);
332 void php_http_env_response_dtor(php_http_env_response_t
*r
)
337 php_http_buffer_free(&r
->buffer
);
338 zval_ptr_dtor(&r
->options
);
339 PTR_FREE(r
->content
.type
);
340 PTR_FREE(r
->content
.encoding
);
341 if (r
->content
.encoder
) {
342 php_http_encoding_stream_free(&r
->content
.encoder
);
346 void php_http_env_response_free(php_http_env_response_t
**r
)
349 php_http_env_response_dtor(*r
);
355 static ZEND_RESULT_CODE
php_http_env_response_send_head(php_http_env_response_t
*r
, php_http_message_t
*request
)
357 ZEND_RESULT_CODE ret
= SUCCESS
;
358 zval
*zoption
, *options
= r
->options
;
359 TSRMLS_FETCH_FROM_CTX(r
->ts
);
365 if ((zoption
= get_option(options
, ZEND_STRL("headers") TSRMLS_CC
))) {
366 if (Z_TYPE_P(zoption
) == IS_ARRAY
) {
367 php_http_header_to_callback(Z_ARRVAL_P(zoption
), 0, (php_http_pass_format_callback_t
) r
->ops
->set_header
, r TSRMLS_CC
);
369 zval_ptr_dtor(&zoption
);
372 if (ret
!= SUCCESS
) {
376 if ((zoption
= get_option(options
, ZEND_STRL("responseCode") TSRMLS_CC
))) {
377 zval
*zoption_copy
= php_http_ztyp(IS_LONG
, zoption
);
379 zval_ptr_dtor(&zoption
);
380 if (Z_LVAL_P(zoption_copy
) > 0) {
381 ret
= r
->ops
->set_status(r
, Z_LVAL_P(zoption_copy
));
383 zval_ptr_dtor(&zoption_copy
);
386 if (ret
!= SUCCESS
) {
390 if ((zoption
= get_option(options
, ZEND_STRL("httpVersion") TSRMLS_CC
))) {
391 php_http_version_t v
;
392 zval
*zoption_copy
= php_http_ztyp(IS_STRING
, zoption
);
394 zval_ptr_dtor(&zoption
);
395 if (Z_STRLEN_P(zoption_copy
) && php_http_version_parse(&v
, Z_STRVAL_P(zoption_copy
) TSRMLS_CC
)) {
396 ret
= r
->ops
->set_protocol_version(r
, &v
);
397 php_http_version_dtor(&v
);
399 zval_ptr_dtor(&zoption_copy
);
402 if (ret
!= SUCCESS
) {
406 if ((zoption
= get_option(options
, ZEND_STRL("cookies") TSRMLS_CC
))) {
407 if (Z_TYPE_P(zoption
) == IS_ARRAY
) {
411 FOREACH_VAL(pos
, zoption
, zcookie
) {
412 if (Z_TYPE_PP(zcookie
) == IS_OBJECT
&& instanceof_function(Z_OBJCE_PP(zcookie
), php_http_cookie_class_entry TSRMLS_CC
)) {
413 php_http_cookie_object_t
*obj
= zend_object_store_get_object(*zcookie TSRMLS_CC
);
417 php_http_cookie_list_to_string(obj
->list
, &str
, &len
);
418 if (SUCCESS
!= (ret
= r
->ops
->add_header(r
, "Set-Cookie: %s", str
))) {
426 zval_ptr_dtor(&zoption
);
429 if (ret
!= SUCCESS
) {
433 if ((zoption
= get_option(options
, ZEND_STRL("contentType") TSRMLS_CC
))) {
434 zval
*zoption_copy
= php_http_ztyp(IS_STRING
, zoption
);
436 zval_ptr_dtor(&zoption
);
437 if (Z_STRLEN_P(zoption_copy
) && strchr(Z_STRVAL_P(zoption_copy
), '/')) {
438 if (SUCCESS
== (ret
= r
->ops
->set_header(r
, "Content-Type: %.*s", Z_STRLEN_P(zoption_copy
), Z_STRVAL_P(zoption_copy
)))) {
439 r
->content
.type
= estrndup(Z_STRVAL_P(zoption_copy
), Z_STRLEN_P(zoption_copy
));
442 zval_ptr_dtor(&zoption_copy
);
445 if (ret
!= SUCCESS
) {
449 if (r
->range
.status
== PHP_HTTP_RANGE_OK
) {
450 if (zend_hash_num_elements(&r
->range
.values
) == 1) {
451 zval
**range
, **begin
, **end
;
453 if ( 1 == php_http_array_list(&r
->range
.values TSRMLS_CC
, 1, &range
)
454 && 2 == php_http_array_list(Z_ARRVAL_PP(range
) TSRMLS_CC
, 2, &begin
, &end
)
456 if (SUCCESS
== (ret
= r
->ops
->set_status(r
, 206))) {
457 ret
= r
->ops
->set_header(r
, "Content-Range: bytes %ld-%ld/%zu", Z_LVAL_PP(begin
), Z_LVAL_PP(end
), r
->content
.length
);
460 /* this should never happen */
461 zend_hash_destroy(&r
->range
.values
);
465 php_http_boundary(r
->range
.boundary
, sizeof(r
->range
.boundary
) TSRMLS_CC
);
466 if (SUCCESS
== (ret
= r
->ops
->set_status(r
, 206))) {
467 ret
= r
->ops
->set_header(r
, "Content-Type: multipart/byteranges; boundary=%s", r
->range
.boundary
);
471 if ((zoption
= get_option(options
, ZEND_STRL("cacheControl") TSRMLS_CC
))) {
472 zval
*zoption_copy
= php_http_ztyp(IS_STRING
, zoption
);
474 zval_ptr_dtor(&zoption
);
475 if (Z_STRLEN_P(zoption_copy
)) {
476 ret
= r
->ops
->set_header(r
, "Cache-Control: %.*s", Z_STRLEN_P(zoption_copy
), Z_STRVAL_P(zoption_copy
));
478 zval_ptr_dtor(&zoption_copy
);
481 if (ret
!= SUCCESS
) {
485 if ((zoption
= get_option(options
, ZEND_STRL("contentDisposition") TSRMLS_CC
))) {
486 zval
*zoption_copy
= php_http_ztyp(IS_ARRAY
, zoption
);
487 php_http_buffer_t buf
;
489 php_http_buffer_init(&buf
);
490 if (php_http_params_to_string(&buf
, Z_ARRVAL_P(zoption_copy
), ZEND_STRL(","), ZEND_STRL(";"), ZEND_STRL("="), PHP_HTTP_PARAMS_DEFAULT TSRMLS_CC
)) {
492 ret
= r
->ops
->set_header(r
, "Content-Disposition: %.*s", buf
.used
, buf
.data
);
496 php_http_buffer_dtor(&buf
);
497 zval_ptr_dtor(&zoption_copy
);
498 zval_ptr_dtor(&zoption
);
501 if (ret
!= SUCCESS
) {
505 if ((zoption
= get_option(options
, ZEND_STRL("contentEncoding") TSRMLS_CC
))) {
506 zval
*zoption_copy
= php_http_ztyp(IS_LONG
, zoption
);
508 HashTable
*result
= NULL
;
510 zval_ptr_dtor(&zoption
);
511 switch (Z_LVAL_P(zoption_copy
)) {
512 case PHP_HTTP_CONTENT_ENCODING_GZIP
:
513 INIT_PZVAL(&zsupported
);
514 array_init(&zsupported
);
515 add_next_index_stringl(&zsupported
, ZEND_STRL("none"), 1);
516 add_next_index_stringl(&zsupported
, ZEND_STRL("gzip"), 1);
517 add_next_index_stringl(&zsupported
, ZEND_STRL("deflate"), 1);
519 if ((result
= php_http_negotiate_encoding(Z_ARRVAL(zsupported
), request TSRMLS_CC
))) {
520 char *key_str
= NULL
;
523 zend_hash_internal_pointer_reset(result
);
524 if (HASH_KEY_IS_STRING
== zend_hash_get_current_key_ex(result
, &key_str
, &key_len
, NULL
, 0, NULL
)) {
525 if (!strcmp(key_str
, "gzip")) {
526 if (!(r
->content
.encoder
= php_http_encoding_stream_init(NULL
, php_http_encoding_stream_get_deflate_ops(), PHP_HTTP_DEFLATE_TYPE_GZIP TSRMLS_CC
))) {
528 } else if (SUCCESS
== (ret
= r
->ops
->set_header(r
, "Content-Encoding: gzip"))) {
529 r
->content
.encoding
= estrndup(key_str
, key_len
- 1);
531 } else if (!strcmp(key_str
, "deflate")) {
532 if (!(r
->content
.encoder
= php_http_encoding_stream_init(NULL
, php_http_encoding_stream_get_deflate_ops(), PHP_HTTP_DEFLATE_TYPE_ZLIB TSRMLS_CC
))) {
534 } else if (SUCCESS
== (ret
= r
->ops
->set_header(r
, "Content-Encoding: deflate"))) {
535 r
->content
.encoding
= estrndup(key_str
, key_len
- 1);
538 ret
= r
->ops
->del_header(r
, ZEND_STRL("Content-Encoding"));
541 if (SUCCESS
== ret
) {
542 ret
= r
->ops
->add_header(r
, "Vary: Accept-Encoding");
546 zend_hash_destroy(result
);
547 FREE_HASHTABLE(result
);
550 zval_dtor(&zsupported
);
553 case PHP_HTTP_CONTENT_ENCODING_NONE
:
555 ret
= r
->ops
->del_header(r
, ZEND_STRL("Content-Encoding"));
558 zval_ptr_dtor(&zoption_copy
);
561 if (SUCCESS
!= ret
) {
565 if (php_http_env_response_is_cacheable(r
, request
)) {
566 switch (php_http_env_is_response_cached_by_etag(options
, ZEND_STRL("If-None-Match"), request TSRMLS_CC
)) {
567 case PHP_HTTP_CACHE_MISS
:
570 case PHP_HTTP_CACHE_NO
:
571 if (PHP_HTTP_CACHE_HIT
!= php_http_env_is_response_cached_by_last_modified(options
, ZEND_STRL("If-Modified-Since"), request TSRMLS_CC
)) {
576 case PHP_HTTP_CACHE_HIT
:
577 ret
= r
->ops
->set_status(r
, 304);
582 if ((zoption
= get_option(options
, ZEND_STRL("etag") TSRMLS_CC
))) {
583 zval
*zoption_copy
= php_http_ztyp(IS_STRING
, zoption
);
585 zval_ptr_dtor(&zoption
);
586 if (*Z_STRVAL_P(zoption_copy
) != '"' && strncmp(Z_STRVAL_P(zoption_copy
), "W/\"", 3)) {
587 ret
= r
->ops
->set_header(r
, "ETag: \"%s\"", Z_STRVAL_P(zoption_copy
));
589 ret
= r
->ops
->set_header(r
, "ETag: %s", Z_STRVAL_P(zoption_copy
));
591 zval_ptr_dtor(&zoption_copy
);
593 if ((zoption
= get_option(options
, ZEND_STRL("lastModified") TSRMLS_CC
))) {
594 zval
*zoption_copy
= php_http_ztyp(IS_LONG
, zoption
);
596 zval_ptr_dtor(&zoption
);
597 if (Z_LVAL_P(zoption_copy
)) {
598 char *date
= php_format_date(ZEND_STRL(PHP_HTTP_DATE_FORMAT
), Z_LVAL_P(zoption_copy
), 0 TSRMLS_CC
);
600 ret
= r
->ops
->set_header(r
, "Last-Modified: %s", date
);
604 zval_ptr_dtor(&zoption_copy
);
612 static ZEND_RESULT_CODE
php_http_env_response_send_body(php_http_env_response_t
*r
)
614 ZEND_RESULT_CODE ret
= SUCCESS
;
616 php_http_message_body_t
*body
;
617 TSRMLS_FETCH_FROM_CTX(r
->ts
);
623 if ((body
= get_body(r
->options TSRMLS_CC
))) {
624 if ((zoption
= get_option(r
->options
, ZEND_STRL("throttleDelay") TSRMLS_CC
))) {
625 if (Z_TYPE_P(zoption
) == IS_DOUBLE
) {
626 r
->throttle
.delay
= Z_DVAL_P(zoption
);
628 zval_ptr_dtor(&zoption
);
630 if ((zoption
= get_option(r
->options
, ZEND_STRL("throttleChunk") TSRMLS_CC
))) {
631 if (Z_TYPE_P(zoption
) == IS_LONG
) {
632 r
->throttle
.chunk
= Z_LVAL_P(zoption
);
634 zval_ptr_dtor(&zoption
);
637 if (r
->range
.status
== PHP_HTTP_RANGE_OK
) {
638 if (zend_hash_num_elements(&r
->range
.values
) == 1) {
640 zval
**range
, **begin
, **end
;
642 if ( 1 == php_http_array_list(&r
->range
.values TSRMLS_CC
, 1, &range
)
643 && 2 == php_http_array_list(Z_ARRVAL_PP(range
) TSRMLS_CC
, 2, &begin
, &end
)
646 ret
= php_http_message_body_to_callback(body
, (php_http_pass_callback_t
) php_http_env_response_send_data
, r
, Z_LVAL_PP(begin
), Z_LVAL_PP(end
) - Z_LVAL_PP(begin
) + 1);
647 if (ret
== SUCCESS
) {
648 ret
= php_http_env_response_send_done(r
);
650 zend_hash_destroy(&r
->range
.values
);
652 /* this should never happen */
653 zend_hash_destroy(&r
->range
.values
);
654 r
->ops
->set_status(r
, 500);
659 /* send multipart/byte-ranges message */
663 FOREACH_HASH_VAL(pos
, &r
->range
.values
, chunk
) {
666 if (2 == php_http_array_list(Z_ARRVAL_PP(chunk
) TSRMLS_CC
, 2, &begin
, &end
)) {
667 php_http_buffer_appendf(r
->buffer
,
670 "Content-Type: %s" PHP_HTTP_CRLF
671 "Content-Range: bytes %ld-%ld/%zu" PHP_HTTP_CRLF PHP_HTTP_CRLF
,
674 r
->content
.type
? r
->content
.type
: "application/octet-stream",
679 ret
= php_http_message_body_to_callback(body
, (php_http_pass_callback_t
) php_http_env_response_send_data
, r
, Z_LVAL_PP(begin
), Z_LVAL_PP(end
) - Z_LVAL_PP(begin
) + 1);
683 if (ret
== SUCCESS
) {
684 php_http_buffer_appendf(r
->buffer
, PHP_HTTP_CRLF
"--%s--", r
->range
.boundary
);
685 ret
= php_http_env_response_send_done(r
);
687 zend_hash_destroy(&r
->range
.values
);
691 ret
= php_http_message_body_to_callback(body
, (php_http_pass_callback_t
) php_http_env_response_send_data
, r
, 0, 0);
692 if (ret
== SUCCESS
) {
693 ret
= php_http_env_response_send_done(r
);
700 ZEND_RESULT_CODE
php_http_env_response_send(php_http_env_response_t
*r
)
702 php_http_message_t
*request
;
703 php_http_message_body_t
*body
;
704 TSRMLS_FETCH_FROM_CTX(r
->ts
);
706 request
= get_request(r
->options TSRMLS_CC
);
708 /* check for ranges */
709 if ((body
= get_body(r
->options TSRMLS_CC
))) {
710 r
->content
.length
= php_http_message_body_size(body
);
712 if (SUCCESS
!= r
->ops
->set_header(r
, "Accept-Ranges: bytes")) {
715 zend_hash_init(&r
->range
.values
, 0, NULL
, ZVAL_PTR_DTOR
, 0);
716 r
->range
.status
= php_http_env_get_request_ranges(&r
->range
.values
, r
->content
.length
, request TSRMLS_CC
);
718 switch (r
->range
.status
) {
719 case PHP_HTTP_RANGE_NO
:
720 zend_hash_destroy(&r
->range
.values
);
723 case PHP_HTTP_RANGE_ERR
:
724 if (php_http_env_got_request_header(ZEND_STRL("If-Range"), request TSRMLS_CC
)) {
725 r
->range
.status
= PHP_HTTP_RANGE_NO
;
726 zend_hash_destroy(&r
->range
.values
);
729 zend_hash_destroy(&r
->range
.values
);
730 if (SUCCESS
!= r
->ops
->set_status(r
, 416)) {
733 if (SUCCESS
!= r
->ops
->set_header(r
, "Content-Range: bytes */%zu", r
->content
.length
)) {
739 case PHP_HTTP_RANGE_OK
:
740 if (PHP_HTTP_CACHE_MISS
== php_http_env_is_response_cached_by_etag(r
->options
, ZEND_STRL("If-Range"), request TSRMLS_CC
)
741 || PHP_HTTP_CACHE_MISS
== php_http_env_is_response_cached_by_last_modified(r
->options
, ZEND_STRL("If-Range"), request TSRMLS_CC
)
743 r
->range
.status
= PHP_HTTP_RANGE_NO
;
744 zend_hash_destroy(&r
->range
.values
);
747 if (PHP_HTTP_CACHE_MISS
== php_http_env_is_response_cached_by_etag(r
->options
, ZEND_STRL("If-Match"), request TSRMLS_CC
)
748 || PHP_HTTP_CACHE_MISS
== php_http_env_is_response_cached_by_last_modified(r
->options
, ZEND_STRL("If-Unmodified-Since"), request TSRMLS_CC
)
749 || PHP_HTTP_CACHE_MISS
== php_http_env_is_response_cached_by_last_modified(r
->options
, ZEND_STRL("Unless-Modified-Since"), request TSRMLS_CC
)
752 zend_hash_destroy(&r
->range
.values
);
753 if (SUCCESS
!= r
->ops
->set_status(r
, 412)) {
764 if (SUCCESS
!= php_http_env_response_send_head(r
, request
)) {
765 php_error_docref(NULL TSRMLS_CC
, E_WARNING
, "Failed to send response headers");
769 if (SUCCESS
!= php_http_env_response_send_body(r
)) {
770 php_error_docref(NULL TSRMLS_CC
, E_WARNING
, "Failed to send response body");
774 if (SUCCESS
!= r
->ops
->finish(r
)) {
775 php_error_docref(NULL TSRMLS_CC
, E_WARNING
, "Failed to finish response");
782 static long php_http_env_response_sapi_get_status(php_http_env_response_t
*r
)
784 TSRMLS_FETCH_FROM_CTX(r
->ts
);
786 return php_http_env_get_response_code(TSRMLS_C
);
788 static ZEND_RESULT_CODE
php_http_env_response_sapi_set_status(php_http_env_response_t
*r
, long http_code
)
790 TSRMLS_FETCH_FROM_CTX(r
->ts
);
792 return php_http_env_set_response_code(http_code TSRMLS_CC
);
794 static ZEND_RESULT_CODE
php_http_env_response_sapi_set_protocol_version(php_http_env_response_t
*r
, php_http_version_t
*v
)
796 TSRMLS_FETCH_FROM_CTX(r
->ts
);
798 return php_http_env_set_response_protocol_version(v TSRMLS_CC
);
800 static ZEND_RESULT_CODE
php_http_env_response_sapi_set_header(php_http_env_response_t
*r
, const char *fmt
, ...)
802 ZEND_RESULT_CODE ret
;
804 TSRMLS_FETCH_FROM_CTX(r
->ts
);
807 ret
= php_http_env_set_response_header_va(0, 1, fmt
, args TSRMLS_CC
);
812 static ZEND_RESULT_CODE
php_http_env_response_sapi_add_header(php_http_env_response_t
*r
, const char *fmt
, ...)
814 ZEND_RESULT_CODE ret
;
816 TSRMLS_FETCH_FROM_CTX(r
->ts
);
819 ret
= php_http_env_set_response_header_va(0, 0, fmt
, args TSRMLS_CC
);
824 static ZEND_RESULT_CODE
php_http_env_response_sapi_del_header(php_http_env_response_t
*r
, const char *header_str
, size_t header_len
)
826 TSRMLS_FETCH_FROM_CTX(r
->ts
);
828 return php_http_env_set_response_header_value(0, header_str
, header_len
, NULL
, 1 TSRMLS_CC
);
830 static ZEND_RESULT_CODE
php_http_env_response_sapi_write(php_http_env_response_t
*r
, const char *data_str
, size_t data_len
)
832 TSRMLS_FETCH_FROM_CTX(r
->ts
);
834 if (0 < PHPWRITE(data_str
, data_len
)) {
839 static ZEND_RESULT_CODE
php_http_env_response_sapi_flush(php_http_env_response_t
*r
)
841 TSRMLS_FETCH_FROM_CTX(r
->ts
);
843 #if PHP_VERSION_ID >= 50400
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
);
851 php_end_ob_buffer(1, 1 TSRMLS_CC
);
852 sapi_flush(TSRMLS_C
);
857 static ZEND_RESULT_CODE
php_http_env_response_sapi_finish(php_http_env_response_t
*r
)
862 static php_http_env_response_ops_t php_http_env_response_sapi_ops
= {
865 php_http_env_response_sapi_get_status
,
866 php_http_env_response_sapi_set_status
,
867 php_http_env_response_sapi_set_protocol_version
,
868 php_http_env_response_sapi_set_header
,
869 php_http_env_response_sapi_add_header
,
870 php_http_env_response_sapi_del_header
,
871 php_http_env_response_sapi_write
,
872 php_http_env_response_sapi_flush
,
873 php_http_env_response_sapi_finish
876 php_http_env_response_ops_t
*php_http_env_response_get_sapi_ops(void)
878 return &php_http_env_response_sapi_ops
;
881 typedef struct php_http_env_response_stream_ctx
{
883 php_http_version_t version
;
887 php_stream_filter
*chunked_filter
;
888 php_http_message_t
*request
;
893 } php_http_env_response_stream_ctx_t
;
895 static ZEND_RESULT_CODE
php_http_env_response_stream_init(php_http_env_response_t
*r
, void *init_arg
)
897 php_http_env_response_stream_ctx_t
*ctx
;
898 size_t buffer_size
= 0x1000;
899 TSRMLS_FETCH_FROM_CTX(r
->ts
);
901 ctx
= ecalloc(1, sizeof(*ctx
));
903 ctx
->stream
= init_arg
;
904 if (!ctx
->stream
|| SUCCESS
!= zend_list_addref(ctx
->stream
->rsrc_id
)) {
908 php_stream_set_option(ctx
->stream
, PHP_STREAM_OPTION_WRITE_BUFFER
, PHP_STREAM_BUFFER_FULL
, &buffer_size
);
909 zend_hash_init(&ctx
->header
, 0, NULL
, ZVAL_PTR_DTOR
, 0);
910 php_http_version_init(&ctx
->version
, 1, 1 TSRMLS_CC
);
911 ctx
->status_code
= 200;
913 ctx
->request
= get_request(r
->options TSRMLS_CC
);
915 /* there are some limitations regarding TE:chunked, see https://tools.ietf.org/html/rfc7230#section-3.3.1 */
916 if (ctx
->request
&& ctx
->request
->http
.version
.major
== 1 && ctx
->request
->http
.version
.minor
== 0) {
917 ctx
->version
.minor
= 0;
924 static void php_http_env_response_stream_dtor(php_http_env_response_t
*r
)
926 php_http_env_response_stream_ctx_t
*ctx
= r
->ctx
;
927 TSRMLS_FETCH_FROM_CTX(r
->ts
);
929 if (ctx
->chunked_filter
) {
930 ctx
->chunked_filter
= php_stream_filter_remove(ctx
->chunked_filter
, 1 TSRMLS_CC
);
932 zend_hash_destroy(&ctx
->header
);
933 zend_list_delete(ctx
->stream
->rsrc_id
);
937 static void php_http_env_response_stream_header(php_http_env_response_stream_ctx_t
*ctx
, HashTable
*header
, php_http_buffer_t
*buf TSRMLS_DC
)
942 FOREACH_HASH_VAL(pos
, header
, val
) {
943 if (Z_TYPE_PP(val
) == IS_ARRAY
) {
944 php_http_env_response_stream_header(ctx
, Z_ARRVAL_PP(val
), buf TSRMLS_CC
);
946 zval
*tmp
= php_http_ztyp(IS_STRING
, *val
);
949 /* disable chunked transfer encoding if we've got an explicit content-length */
950 if (!strncasecmp(Z_STRVAL_P(tmp
), "Content-Length:", lenof("Content-Length:"))) {
954 php_http_buffer_append(buf
, Z_STRVAL_P(tmp
), Z_STRLEN_P(tmp
));
955 php_http_buffer_appends(buf
, PHP_HTTP_CRLF
);
960 static ZEND_RESULT_CODE
php_http_env_response_stream_start(php_http_env_response_stream_ctx_t
*ctx TSRMLS_DC
)
962 php_http_buffer_t header_buf
;
964 if (ctx
->started
|| ctx
->finished
) {
968 php_http_buffer_init(&header_buf
);
969 php_http_buffer_appendf(&header_buf
, "HTTP/%u.%u %ld %s" PHP_HTTP_CRLF
, ctx
->version
.major
, ctx
->version
.minor
, ctx
->status_code
, php_http_env_get_response_status_for_code(ctx
->status_code
));
971 /* there are some limitations regarding TE:chunked, see https://tools.ietf.org/html/rfc7230#section-3.3.1 */
972 if (ctx
->version
.major
== 1 && ctx
->version
.minor
== 0) {
974 } else if (ctx
->status_code
== 204 || ctx
->status_code
/100 == 1) {
976 } else if (ctx
->request
&& ctx
->status_code
/100 == 2 && !strcasecmp(ctx
->request
->http
.info
.request
.method
, "CONNECT")) {
980 php_http_env_response_stream_header(ctx
, &ctx
->header
, &header_buf TSRMLS_CC
);
982 /* enable chunked transfer encoding */
984 php_http_buffer_appends(&header_buf
, "Transfer-Encoding: chunked" PHP_HTTP_CRLF
);
986 php_http_buffer_appends(&header_buf
, PHP_HTTP_CRLF
);
988 if (header_buf
.used
== php_stream_write(ctx
->stream
, header_buf
.data
, header_buf
.used
)) {
991 php_http_buffer_dtor(&header_buf
);
992 php_stream_flush(ctx
->stream
);
995 ctx
->chunked_filter
= php_stream_filter_create("http.chunked_encode", NULL
, 0 TSRMLS_CC
);
996 php_stream_filter_append(&ctx
->stream
->writefilters
, ctx
->chunked_filter
);
999 return ctx
->started
? SUCCESS
: FAILURE
;
1001 static long php_http_env_response_stream_get_status(php_http_env_response_t
*r
)
1003 php_http_env_response_stream_ctx_t
*ctx
= r
->ctx
;
1005 return ctx
->status_code
;
1007 static ZEND_RESULT_CODE
php_http_env_response_stream_set_status(php_http_env_response_t
*r
, long http_code
)
1009 php_http_env_response_stream_ctx_t
*stream_ctx
= r
->ctx
;
1011 if (stream_ctx
->started
|| stream_ctx
->finished
) {
1015 stream_ctx
->status_code
= http_code
;
1019 static ZEND_RESULT_CODE
php_http_env_response_stream_set_protocol_version(php_http_env_response_t
*r
, php_http_version_t
*v
)
1021 php_http_env_response_stream_ctx_t
*stream_ctx
= r
->ctx
;
1023 if (stream_ctx
->started
|| stream_ctx
->finished
) {
1027 memcpy(&stream_ctx
->version
, v
, sizeof(stream_ctx
->version
));
1031 static ZEND_RESULT_CODE
php_http_env_response_stream_set_header_ex(php_http_env_response_t
*r
, zend_bool replace
, const char *fmt
, va_list argv
)
1033 php_http_env_response_stream_ctx_t
*stream_ctx
= r
->ctx
;
1034 char *header_end
, *header_str
= NULL
;
1035 size_t header_len
= 0;
1036 zval
*zheader
, **zheader_ptr
;
1038 if (stream_ctx
->started
|| stream_ctx
->finished
) {
1042 header_len
= vspprintf(&header_str
, 0, fmt
, argv
);
1044 if (!(header_end
= strchr(header_str
, ':'))) {
1051 if (!replace
&& (SUCCESS
== zend_hash_find(&stream_ctx
->header
, header_str
, header_end
- header_str
+ 1, (void *) &zheader_ptr
))) {
1052 convert_to_array(*zheader_ptr
);
1054 return add_next_index_stringl(*zheader_ptr
, header_str
, header_len
, 0);
1056 MAKE_STD_ZVAL(zheader
);
1057 ZVAL_STRINGL(zheader
, header_str
, header_len
, 0);
1059 if (SUCCESS
!= zend_hash_update(&stream_ctx
->header
, header_str
, header_end
- header_str
+ 1, (void *) &zheader
, sizeof(zval
*), NULL
)) {
1060 zval_ptr_dtor(&zheader
);
1068 static ZEND_RESULT_CODE
php_http_env_response_stream_set_header(php_http_env_response_t
*r
, const char *fmt
, ...)
1070 ZEND_RESULT_CODE ret
;
1073 va_start(argv
, fmt
);
1074 ret
= php_http_env_response_stream_set_header_ex(r
, 1, fmt
, argv
);
1079 static ZEND_RESULT_CODE
php_http_env_response_stream_add_header(php_http_env_response_t
*r
, const char *fmt
, ...)
1081 ZEND_RESULT_CODE ret
;
1084 va_start(argv
, fmt
);
1085 ret
= php_http_env_response_stream_set_header_ex(r
, 0, fmt
, argv
);
1090 static ZEND_RESULT_CODE
php_http_env_response_stream_del_header(php_http_env_response_t
*r
, const char *header_str
, size_t header_len
)
1092 php_http_env_response_stream_ctx_t
*stream_ctx
= r
->ctx
;
1094 if (stream_ctx
->started
|| stream_ctx
->finished
) {
1098 zend_hash_del(&stream_ctx
->header
, header_str
, header_len
+ 1);
1101 static ZEND_RESULT_CODE
php_http_env_response_stream_write(php_http_env_response_t
*r
, const char *data_str
, size_t data_len
)
1103 php_http_env_response_stream_ctx_t
*stream_ctx
= r
->ctx
;
1104 TSRMLS_FETCH_FROM_CTX(r
->ts
);
1106 if (stream_ctx
->finished
) {
1109 if (!stream_ctx
->started
) {
1110 if (SUCCESS
!= php_http_env_response_stream_start(stream_ctx TSRMLS_CC
)) {
1115 if (data_len
!= php_stream_write(stream_ctx
->stream
, data_str
, data_len
)) {
1121 static ZEND_RESULT_CODE
php_http_env_response_stream_flush(php_http_env_response_t
*r
)
1123 php_http_env_response_stream_ctx_t
*stream_ctx
= r
->ctx
;
1124 TSRMLS_FETCH_FROM_CTX(r
->ts
);
1126 if (stream_ctx
->finished
) {
1129 if (!stream_ctx
->started
) {
1130 if (SUCCESS
!= php_http_env_response_stream_start(stream_ctx TSRMLS_CC
)) {
1135 return php_stream_flush(stream_ctx
->stream
);
1137 static ZEND_RESULT_CODE
php_http_env_response_stream_finish(php_http_env_response_t
*r
)
1139 php_http_env_response_stream_ctx_t
*ctx
= r
->ctx
;
1140 TSRMLS_FETCH_FROM_CTX(r
->ts
);
1142 if (ctx
->finished
) {
1145 if (!ctx
->started
) {
1146 if (SUCCESS
!= php_http_env_response_stream_start(ctx TSRMLS_CC
)) {
1151 php_stream_flush(ctx
->stream
);
1152 if (ctx
->chunked
&& ctx
->chunked_filter
) {
1153 php_stream_filter_flush(ctx
->chunked_filter
, 1);
1154 ctx
->chunked_filter
= php_stream_filter_remove(ctx
->chunked_filter
, 1 TSRMLS_CC
);
1162 static php_http_env_response_ops_t php_http_env_response_stream_ops
= {
1163 php_http_env_response_stream_init
,
1164 php_http_env_response_stream_dtor
,
1165 php_http_env_response_stream_get_status
,
1166 php_http_env_response_stream_set_status
,
1167 php_http_env_response_stream_set_protocol_version
,
1168 php_http_env_response_stream_set_header
,
1169 php_http_env_response_stream_add_header
,
1170 php_http_env_response_stream_del_header
,
1171 php_http_env_response_stream_write
,
1172 php_http_env_response_stream_flush
,
1173 php_http_env_response_stream_finish
1176 php_http_env_response_ops_t
*php_http_env_response_get_stream_ops(void)
1178 return &php_http_env_response_stream_ops
;
1181 #define PHP_HTTP_ENV_RESPONSE_OBJECT_INIT(obj) \
1183 if (!obj->message) { \
1184 obj->message = php_http_message_init_env(NULL, PHP_HTTP_RESPONSE TSRMLS_CC); \
1188 ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse___construct
, 0, 0, 0)
1189 ZEND_END_ARG_INFO();
1190 static PHP_METHOD(HttpEnvResponse
, __construct
)
1192 php_http_message_object_t
*obj
;
1194 php_http_expect(SUCCESS
== zend_parse_parameters_none(), invalid_arg
, return);
1196 obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
1198 php_http_expect(obj
->message
= php_http_message_init_env(obj
->message
, PHP_HTTP_RESPONSE TSRMLS_CC
), unexpected_val
, return);
1201 ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse___invoke
, 0, 0, 1)
1202 ZEND_ARG_INFO(0, ob_string
)
1203 ZEND_ARG_INFO(0, ob_flags
)
1204 ZEND_END_ARG_INFO();
1205 static PHP_METHOD(HttpEnvResponse
, __invoke
)
1211 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "s|l", &ob_str
, &ob_len
, &ob_flags
)) {
1212 php_http_message_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
1214 PHP_HTTP_ENV_RESPONSE_OBJECT_INIT(obj
);
1217 php_http_message_object_init_body_object(obj
);
1219 php_http_message_body_append(obj
->message
->body
, ob_str
, ob_len
);
1220 #if PHP_VERSION_ID >= 50400
1221 if (ob_flags
& PHP_OUTPUT_HANDLER_CLEAN
) {
1222 php_stream_truncate_set_size(php_http_message_body_stream(obj
->message
->body
), 0);
1226 RETURN_EMPTY_STRING();
1231 ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setEnvRequest
, 0, 0, 1)
1232 ZEND_ARG_OBJ_INFO(0, env_request
, http
\\Message
, 1)
1233 ZEND_END_ARG_INFO();
1234 static PHP_METHOD(HttpEnvResponse
, setEnvRequest
)
1236 zval
*env_req
= NULL
;
1238 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "|O", &env_req
, php_http_message_class_entry
), invalid_arg
, return);
1240 set_option(getThis(), ZEND_STRL("request"), IS_OBJECT
, env_req
, 0 TSRMLS_CC
);
1241 RETVAL_ZVAL(getThis(), 1, 0);
1244 ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setContentType
, 0, 0, 1)
1245 ZEND_ARG_INFO(0, content_type
)
1246 ZEND_END_ARG_INFO();
1247 static PHP_METHOD(HttpEnvResponse
, setContentType
)
1249 char *ct_str
= NULL
;
1252 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "s!", &ct_str
, &ct_len
), invalid_arg
, return);
1254 set_option(getThis(), ZEND_STRL("contentType"), IS_STRING
, ct_str
, ct_len TSRMLS_CC
);
1255 RETVAL_ZVAL(getThis(), 1, 0);
1258 ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setContentDisposition
, 0, 0, 1)
1259 ZEND_ARG_ARRAY_INFO(0, disposition_params
, 1)
1260 ZEND_END_ARG_INFO();
1261 static PHP_METHOD(HttpEnvResponse
, setContentDisposition
)
1265 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "a", &zdisposition
), invalid_arg
, return);
1267 zend_update_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("contentDisposition"), zdisposition TSRMLS_CC
);
1268 RETVAL_ZVAL(getThis(), 1, 0);
1271 ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setContentEncoding
, 0, 0, 1)
1272 ZEND_ARG_INFO(0, content_encoding
)
1273 ZEND_END_ARG_INFO();
1274 static PHP_METHOD(HttpEnvResponse
, setContentEncoding
)
1278 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "l", &ce
), invalid_arg
, return);
1280 set_option(getThis(), ZEND_STRL("contentEncoding"), IS_LONG
, &ce
, 0 TSRMLS_CC
);
1281 RETVAL_ZVAL(getThis(), 1, 0);
1284 ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setCacheControl
, 0, 0, 1)
1285 ZEND_ARG_INFO(0, cache_control
)
1286 ZEND_END_ARG_INFO();
1287 static PHP_METHOD(HttpEnvResponse
, setCacheControl
)
1289 char *cc_str
= NULL
;
1292 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "s!", &cc_str
, &cc_len
), invalid_arg
, return);
1294 set_option(getThis(), ZEND_STRL("cacheControl"), IS_STRING
, cc_str
, cc_len TSRMLS_CC
);
1295 RETVAL_ZVAL(getThis(), 1, 0);
1298 ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setLastModified
, 0, 0, 1)
1299 ZEND_ARG_INFO(0, last_modified
)
1300 ZEND_END_ARG_INFO();
1301 static PHP_METHOD(HttpEnvResponse
, setLastModified
)
1305 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "l", &last_modified
), invalid_arg
, return);
1307 set_option(getThis(), ZEND_STRL("lastModified"), IS_LONG
, &last_modified
, 0 TSRMLS_CC
);
1308 RETVAL_ZVAL(getThis(), 1, 0);
1311 ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_isCachedByLastModified
, 0, 0, 0)
1312 ZEND_ARG_INFO(0, header_name
)
1313 ZEND_END_ARG_INFO();
1314 static PHP_METHOD(HttpEnvResponse
, isCachedByLastModified
)
1316 char *header_name_str
= NULL
;
1317 int header_name_len
= 0;
1319 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "|s!", &header_name_str
, &header_name_len
)) {
1320 if (!header_name_str
|| !header_name_len
) {
1321 header_name_str
= "If-Modified-Since";
1322 header_name_len
= lenof("If-Modified-Since");
1325 RETURN_LONG(php_http_env_is_response_cached_by_last_modified(getThis(), header_name_str
, header_name_len
, get_request(getThis() TSRMLS_CC
) TSRMLS_CC
));
1329 ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setEtag
, 0, 0, 1)
1330 ZEND_ARG_INFO(0, etag
)
1331 ZEND_END_ARG_INFO();
1332 static PHP_METHOD(HttpEnvResponse
, setEtag
)
1334 char *etag_str
= NULL
;
1337 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "s!", &etag_str
, &etag_len
), invalid_arg
, return);
1339 set_option(getThis(), ZEND_STRL("etag"), IS_STRING
, etag_str
, etag_len TSRMLS_CC
);
1340 RETVAL_ZVAL(getThis(), 1, 0);
1343 ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_isCachedByEtag
, 0, 0, 0)
1344 ZEND_ARG_INFO(0, header_name
)
1345 ZEND_END_ARG_INFO();
1346 static PHP_METHOD(HttpEnvResponse
, isCachedByEtag
)
1348 char *header_name_str
= NULL
;
1349 int header_name_len
= 0;
1351 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "|s!", &header_name_str
, &header_name_len
)) {
1352 if (!header_name_str
|| !header_name_len
) {
1353 header_name_str
= "If-None-Match";
1354 header_name_len
= lenof("If-None-Match");
1356 RETURN_LONG(php_http_env_is_response_cached_by_etag(getThis(), header_name_str
, header_name_len
, get_request(getThis() TSRMLS_CC
) TSRMLS_CC
));
1360 ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setThrottleRate
, 0, 0, 1)
1361 ZEND_ARG_INFO(0, chunk_size
)
1362 ZEND_ARG_INFO(0, delay
)
1363 ZEND_END_ARG_INFO();
1364 static PHP_METHOD(HttpEnvResponse
, setThrottleRate
)
1369 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "l|d", &chunk_size
, &delay
), invalid_arg
, return);
1371 set_option(getThis(), ZEND_STRL("throttleDelay"), IS_DOUBLE
, &delay
, 0 TSRMLS_CC
);
1372 set_option(getThis(), ZEND_STRL("throttleChunk"), IS_LONG
, &chunk_size
, 0 TSRMLS_CC
);
1373 RETVAL_ZVAL(getThis(), 1, 0);
1376 ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setCookie
, 0, 0, 1)
1377 ZEND_ARG_INFO(0, cookie
)
1378 ZEND_END_ARG_INFO();
1379 static PHP_METHOD(HttpEnvResponse
, setCookie
)
1382 zend_error_handling zeh
;
1383 php_http_cookie_list_t
*list
= NULL
;
1385 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "z", &zcookie_new
), invalid_arg
, return);
1387 zend_replace_error_handling(EH_THROW
, php_http_exception_unexpected_val_class_entry
, &zeh TSRMLS_CC
);
1388 switch (Z_TYPE_P(zcookie_new
)) {
1390 if (instanceof_function(Z_OBJCE_P(zcookie_new
), php_http_cookie_class_entry TSRMLS_CC
)) {
1391 Z_ADDREF_P(zcookie_new
);
1396 list
= php_http_cookie_list_from_struct(NULL
, zcookie_new TSRMLS_CC
);
1397 MAKE_STD_ZVAL(zcookie_new
);
1398 ZVAL_OBJVAL(zcookie_new
, php_http_cookie_object_new_ex(php_http_cookie_class_entry
, list
, NULL TSRMLS_CC
), 0);
1402 zcookie_new
= php_http_ztyp(IS_STRING
, zcookie_new
);
1403 list
= php_http_cookie_list_parse(NULL
, Z_STRVAL_P(zcookie_new
), Z_STRLEN_P(zcookie_new
), 0, NULL TSRMLS_CC
);
1404 zval_ptr_dtor(&zcookie_new
);
1405 MAKE_STD_ZVAL(zcookie_new
);
1406 ZVAL_OBJVAL(zcookie_new
, php_http_cookie_object_new_ex(php_http_cookie_class_entry
, list
, NULL TSRMLS_CC
), 0);
1408 zend_restore_error_handling(&zeh TSRMLS_CC
);
1410 set_cookie(getThis(), zcookie_new TSRMLS_CC
);
1411 zval_ptr_dtor(&zcookie_new
);
1413 RETVAL_ZVAL(getThis(), 1, 0);
1416 ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_send
, 0, 0, 0)
1417 ZEND_ARG_INFO(0, stream
)
1418 ZEND_END_ARG_INFO();
1419 static PHP_METHOD(HttpEnvResponse
, send
)
1421 zval
*zstream
= NULL
;
1422 php_stream
*s
= NULL
;
1424 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "|r", &zstream
)) {
1425 /* first flush the output layer to avoid conflicting headers and output;
1426 * also, ob_start($thisEnvResponse) might have been called */
1427 #if PHP_VERSION_ID >= 50400
1428 php_output_end_all(TSRMLS_C
);
1430 php_end_ob_buffers(1 TSRMLS_CC
);
1434 php_http_env_response_t
*r
;
1436 php_stream_from_zval(s
, &zstream
);
1437 r
= php_http_env_response_init(NULL
, getThis(), php_http_env_response_get_stream_ops(), s TSRMLS_CC
);
1442 RETVAL_BOOL(SUCCESS
== php_http_env_response_send(r
));
1443 php_http_env_response_free(&r
);
1445 php_http_env_response_t r
;
1447 if (!php_http_env_response_init(&r
, getThis(), NULL
, NULL TSRMLS_CC
)) {
1451 RETVAL_BOOL(SUCCESS
== php_http_env_response_send(&r
));
1452 php_http_env_response_dtor(&r
);
1457 static zend_function_entry php_http_env_response_methods
[] = {
1458 PHP_ME(HttpEnvResponse
, __construct
, ai_HttpEnvResponse___construct
, ZEND_ACC_PUBLIC
|ZEND_ACC_CTOR
)
1459 PHP_ME(HttpEnvResponse
, __invoke
, ai_HttpEnvResponse___invoke
, ZEND_ACC_PUBLIC
)
1460 PHP_ME(HttpEnvResponse
, setEnvRequest
, ai_HttpEnvResponse_setEnvRequest
, ZEND_ACC_PUBLIC
)
1461 PHP_ME(HttpEnvResponse
, setCookie
, ai_HttpEnvResponse_setCookie
, ZEND_ACC_PUBLIC
)
1462 PHP_ME(HttpEnvResponse
, setContentType
, ai_HttpEnvResponse_setContentType
, ZEND_ACC_PUBLIC
)
1463 PHP_ME(HttpEnvResponse
, setContentDisposition
, ai_HttpEnvResponse_setContentDisposition
, ZEND_ACC_PUBLIC
)
1464 PHP_ME(HttpEnvResponse
, setContentEncoding
, ai_HttpEnvResponse_setContentEncoding
, ZEND_ACC_PUBLIC
)
1465 PHP_ME(HttpEnvResponse
, setCacheControl
, ai_HttpEnvResponse_setCacheControl
, ZEND_ACC_PUBLIC
)
1466 PHP_ME(HttpEnvResponse
, setLastModified
, ai_HttpEnvResponse_setLastModified
, ZEND_ACC_PUBLIC
)
1467 PHP_ME(HttpEnvResponse
, isCachedByLastModified
, ai_HttpEnvResponse_isCachedByLastModified
, ZEND_ACC_PUBLIC
)
1468 PHP_ME(HttpEnvResponse
, setEtag
, ai_HttpEnvResponse_setEtag
, ZEND_ACC_PUBLIC
)
1469 PHP_ME(HttpEnvResponse
, isCachedByEtag
, ai_HttpEnvResponse_isCachedByEtag
, ZEND_ACC_PUBLIC
)
1470 PHP_ME(HttpEnvResponse
, setThrottleRate
, ai_HttpEnvResponse_setThrottleRate
, ZEND_ACC_PUBLIC
)
1471 PHP_ME(HttpEnvResponse
, send
, ai_HttpEnvResponse_send
, ZEND_ACC_PUBLIC
)
1472 EMPTY_FUNCTION_ENTRY
1475 zend_class_entry
*php_http_env_response_class_entry
;
1477 PHP_MINIT_FUNCTION(http_env_response
)
1479 zend_class_entry ce
= {0};
1481 INIT_NS_CLASS_ENTRY(ce
, "http\\Env", "Response", php_http_env_response_methods
);
1482 php_http_env_response_class_entry
= zend_register_internal_class_ex(&ce
, php_http_message_class_entry
, NULL TSRMLS_CC
);
1484 zend_declare_class_constant_long(php_http_env_response_class_entry
, ZEND_STRL("CONTENT_ENCODING_NONE"), PHP_HTTP_CONTENT_ENCODING_NONE TSRMLS_CC
);
1485 zend_declare_class_constant_long(php_http_env_response_class_entry
, ZEND_STRL("CONTENT_ENCODING_GZIP"), PHP_HTTP_CONTENT_ENCODING_GZIP TSRMLS_CC
);
1487 zend_declare_class_constant_long(php_http_env_response_class_entry
, ZEND_STRL("CACHE_NO"), PHP_HTTP_CACHE_NO TSRMLS_CC
);
1488 zend_declare_class_constant_long(php_http_env_response_class_entry
, ZEND_STRL("CACHE_HIT"), PHP_HTTP_CACHE_HIT TSRMLS_CC
);
1489 zend_declare_class_constant_long(php_http_env_response_class_entry
, ZEND_STRL("CACHE_MISS"), PHP_HTTP_CACHE_MISS TSRMLS_CC
);
1491 zend_declare_property_null(php_http_env_response_class_entry
, ZEND_STRL("request"), ZEND_ACC_PROTECTED TSRMLS_CC
);
1492 zend_declare_property_null(php_http_env_response_class_entry
, ZEND_STRL("cookies"), ZEND_ACC_PROTECTED TSRMLS_CC
);
1493 zend_declare_property_null(php_http_env_response_class_entry
, ZEND_STRL("contentType"), ZEND_ACC_PROTECTED TSRMLS_CC
);
1494 zend_declare_property_null(php_http_env_response_class_entry
, ZEND_STRL("contentDisposition"), ZEND_ACC_PROTECTED TSRMLS_CC
);
1495 zend_declare_property_null(php_http_env_response_class_entry
, ZEND_STRL("contentEncoding"), ZEND_ACC_PROTECTED TSRMLS_CC
);
1496 zend_declare_property_null(php_http_env_response_class_entry
, ZEND_STRL("cacheControl"), ZEND_ACC_PROTECTED TSRMLS_CC
);
1497 zend_declare_property_null(php_http_env_response_class_entry
, ZEND_STRL("etag"), ZEND_ACC_PROTECTED TSRMLS_CC
);
1498 zend_declare_property_null(php_http_env_response_class_entry
, ZEND_STRL("lastModified"), ZEND_ACC_PROTECTED TSRMLS_CC
);
1499 zend_declare_property_null(php_http_env_response_class_entry
, ZEND_STRL("throttleDelay"), ZEND_ACC_PROTECTED TSRMLS_CC
);
1500 zend_declare_property_null(php_http_env_response_class_entry
, ZEND_STRL("throttleChunk"), ZEND_ACC_PROTECTED TSRMLS_CC
);
1511 * vim600: noet sw=4 ts=4 fdm=marker
1512 * vim<600: noet sw=4 ts=4