LCOV - code coverage report
Current view: top level - ext/http - php_http_env_response.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 604 712 84.8 %
Date: 2015-02-17 20:30:22 Functions: 55 56 98.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :     +--------------------------------------------------------------------+
       3             :     | PECL :: http                                                       |
       4             :     +--------------------------------------------------------------------+
       5             :     | Redistribution and use in source and binary forms, with or without |
       6             :     | modification, are permitted provided that the conditions mentioned |
       7             :     | in the accompanying LICENSE file are met.                          |
       8             :     +--------------------------------------------------------------------+
       9             :     | Copyright (c) 2004-2014, Michael Wallner <mike@php.net>            |
      10             :     +--------------------------------------------------------------------+
      11             : */
      12             : 
      13             : #include "php_http_api.h"
      14             : 
      15          52 : 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)
      16             : {
      17          52 :         if (Z_TYPE_P(options) == IS_OBJECT) {
      18          52 :                 if (value_ptr) {
      19          52 :                         switch (type) {
      20             :                                 case IS_DOUBLE:
      21           1 :                                         zend_update_property_double(Z_OBJCE_P(options), options, name_str, name_len, *(double *)value_ptr TSRMLS_CC);
      22           1 :                                         break;
      23             :                                 case IS_LONG:
      24          22 :                                         zend_update_property_long(Z_OBJCE_P(options), options, name_str, name_len, *(long *)value_ptr TSRMLS_CC);
      25          22 :                                         break;
      26             :                                 case IS_STRING:
      27          21 :                                         zend_update_property_stringl(Z_OBJCE_P(options), options, name_str, name_len, value_ptr, value_len TSRMLS_CC);
      28          21 :                                         break;
      29             :                                 case IS_ARRAY:
      30             :                                 case IS_OBJECT:
      31           8 :                                         zend_update_property(Z_OBJCE_P(options), options, name_str, name_len, value_ptr TSRMLS_CC);
      32           8 :                                         break;
      33             :                         }
      34             :                 } else {
      35           0 :                         zend_update_property_null(Z_OBJCE_P(options), options, name_str, name_len TSRMLS_CC);
      36             :                 }
      37             :         } else {
      38           0 :                 convert_to_array(options);
      39           0 :                 if (value_ptr) {
      40           0 :                         switch (type) {
      41             :                                 case IS_DOUBLE:
      42           0 :                                         add_assoc_double_ex(options, name_str, name_len + 1, *(double *)value_ptr);
      43           0 :                                         break;
      44             :                                 case IS_LONG:
      45           0 :                                         add_assoc_long_ex(options, name_str, name_len + 1, *(long *)value_ptr);
      46           0 :                                         break;
      47             :                                 case IS_STRING: {
      48           0 :                                         char *value = estrndup(value_ptr, value_len);
      49           0 :                                         add_assoc_stringl_ex(options, name_str, name_len + 1, value, value_len, 0);
      50           0 :                                         break;
      51             :                                 case IS_ARRAY:
      52             :                                 case IS_OBJECT:
      53           0 :                                         Z_ADDREF_P(value_ptr);
      54           0 :                                         add_assoc_zval_ex(options, name_str, name_len + 1, value_ptr);
      55           0 :                                         break;
      56             :                                 }
      57             :                         }
      58             :                 } else {
      59           0 :                         add_assoc_null_ex(options, name_str, name_len + 1);
      60             :                 }
      61             :         }
      62          52 : }
      63         380 : static zval *get_option(zval *options, const char *name_str, size_t name_len TSRMLS_DC)
      64             : {
      65             :         zval *val, **valptr;
      66             : 
      67         380 :         if (Z_TYPE_P(options) == IS_OBJECT) {
      68         380 :                 val = zend_read_property(Z_OBJCE_P(options), options, name_str, name_len, 0 TSRMLS_CC);
      69             :         } else {
      70           0 :                 if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(options), name_str, name_len + 1, (void *) &valptr)) {
      71           0 :                         val = *valptr;
      72             :                 } else {
      73           0 :                         val = NULL;
      74             :                 }
      75             :         }
      76         380 :         if (val) {
      77         380 :                 Z_ADDREF_P(val);
      78             :         }
      79         380 :         return val;
      80             : }
      81          89 : static php_http_message_body_t *get_body(zval *options TSRMLS_DC)
      82             : {
      83             :         zval *zbody;
      84          89 :         php_http_message_body_t *body = NULL;
      85             : 
      86          89 :         if ((zbody = get_option(options, ZEND_STRL("body") TSRMLS_CC))) {
      87          89 :                 if ((Z_TYPE_P(zbody) == IS_OBJECT) && instanceof_function(Z_OBJCE_P(zbody), php_http_message_body_class_entry TSRMLS_CC)) {
      88          79 :                         php_http_message_body_object_t *body_obj = zend_object_store_get_object(zbody TSRMLS_CC);
      89             : 
      90          79 :                         body = body_obj->body;
      91             :                 }
      92          89 :                 zval_ptr_dtor(&zbody);
      93             :         }
      94             : 
      95          89 :         return body;
      96             : }
      97          36 : static php_http_message_t *get_request(zval *options TSRMLS_DC)
      98             : {
      99             :         zval *zrequest;
     100          36 :         php_http_message_t *request = NULL;
     101             : 
     102          36 :         if ((zrequest = get_option(options, ZEND_STRL("request") TSRMLS_CC))) {
     103          36 :                 if (Z_TYPE_P(zrequest) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zrequest), php_http_message_class_entry TSRMLS_CC)) {
     104          10 :                         php_http_message_object_t *request_obj = zend_object_store_get_object(zrequest TSRMLS_CC);
     105             : 
     106          10 :                         request = request_obj->message;
     107             :                 }
     108          36 :                 zval_ptr_dtor(&zrequest);
     109             :         }
     110             : 
     111          36 :         return request;
     112             : }
     113           3 : static void set_cookie(zval *options, zval *zcookie_new TSRMLS_DC)
     114             : {
     115             :         HashPosition pos;
     116             :         zval *zcookies_set;
     117           3 :         php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
     118           3 :         php_http_cookie_object_t *obj = zend_object_store_get_object(zcookie_new TSRMLS_CC);
     119             : 
     120           3 :         zcookies_set = get_option(options, ZEND_STRL("cookies") TSRMLS_CC);
     121           3 :         if (!zcookies_set || Z_TYPE_P(zcookies_set) != IS_ARRAY) {
     122           1 :                 if (zcookies_set) {
     123           1 :                         zval_ptr_dtor(&zcookies_set);
     124             :                 }
     125           1 :                 MAKE_STD_ZVAL(zcookies_set);
     126           1 :                 array_init_size(zcookies_set, zend_hash_num_elements(&obj->list->cookies));
     127             :         } else {
     128           2 :                 SEPARATE_ZVAL(&zcookies_set);
     129             :         }
     130             : 
     131           6 :         FOREACH_HASH_KEY(pos, &obj->list->cookies, key) {
     132           3 :                 Z_ADDREF_P(zcookie_new);
     133           3 :                 if (key.type == HASH_KEY_IS_STRING) {
     134           2 :                         add_assoc_zval_ex(zcookies_set, key.str, key.len, zcookie_new);
     135             :                 } else {
     136           1 :                         add_index_zval(zcookies_set, key.num, zcookie_new);
     137             :                 }
     138             :         }
     139             : 
     140           3 :         set_option(options, ZEND_STRL("cookies"), IS_ARRAY, zcookies_set, 0 TSRMLS_CC);
     141           3 :         zval_ptr_dtor(&zcookies_set);
     142           3 : }
     143             : 
     144          24 : 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)
     145             : {
     146          24 :         php_http_cache_status_t ret = PHP_HTTP_CACHE_NO;
     147          24 :         int free_etag = 0;
     148          24 :         char *header = NULL, *etag;
     149             :         php_http_message_body_t *body;
     150             :         zval *zetag;
     151             : 
     152             : 
     153          24 :         if (!(body = get_body(options TSRMLS_CC))) {
     154           2 :                 return ret;
     155             :         }
     156             : 
     157          22 :         if ((zetag = get_option(options, ZEND_STRL("etag") TSRMLS_CC))) {
     158          22 :                 zval *zetag_copy = php_http_ztyp(IS_STRING, zetag);
     159          22 :                 zval_ptr_dtor(&zetag);
     160          22 :                 zetag = zetag_copy;
     161             :         }
     162             : 
     163          22 :         if (zetag && Z_STRLEN_P(zetag)) {
     164          10 :                 etag = Z_STRVAL_P(zetag);
     165          12 :         } else if ((etag = php_http_message_body_etag(body))) {
     166          12 :                 set_option(options, ZEND_STRL("etag"), IS_STRING, etag, strlen(etag) TSRMLS_CC);
     167          12 :                 free_etag = 1;
     168             :         }
     169             : 
     170          22 :         if (zetag) {
     171          22 :                 zval_ptr_dtor(&zetag);
     172             :         }
     173             : 
     174          22 :         if (etag && (header = php_http_env_get_request_header(header_str, header_len, NULL, request TSRMLS_CC))) {
     175           2 :                 ret = php_http_match(header, etag, PHP_HTTP_MATCH_WORD)  ? PHP_HTTP_CACHE_HIT : PHP_HTTP_CACHE_MISS;
     176             :         }
     177             : 
     178          22 :         if (free_etag) {
     179          12 :                 efree(etag);
     180             :         }
     181             : 
     182          22 :         PTR_FREE(header);
     183          22 :         return ret;
     184             : }
     185             : 
     186          26 : 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)
     187             : {
     188          26 :         php_http_cache_status_t ret = PHP_HTTP_CACHE_NO;
     189             :         char *header;
     190          26 :         time_t ums, lm = 0;
     191             :         php_http_message_body_t *body;
     192             :         zval *zlm;
     193             : 
     194          26 :         if (!(body = get_body(options TSRMLS_CC))) {
     195           2 :                 return ret;
     196             :         }
     197             : 
     198          24 :         if ((zlm = get_option(options, ZEND_STRL("lastModified") TSRMLS_CC))) {
     199          24 :                 zval *zlm_copy = php_http_ztyp(IS_LONG, zlm);
     200          24 :                 zval_ptr_dtor(&zlm);
     201          24 :                 zlm = zlm_copy;
     202             :         }
     203             : 
     204          24 :         if (zlm && Z_LVAL_P(zlm) > 0) {
     205          10 :                 lm = Z_LVAL_P(zlm);
     206             :         } else {
     207          14 :                 lm = php_http_message_body_mtime(body);
     208          14 :                 set_option(options, ZEND_STRL("lastModified"), IS_LONG, &lm, 0 TSRMLS_CC);
     209             :         }
     210             : 
     211          24 :         if (zlm) {
     212          24 :                 zval_ptr_dtor(&zlm);
     213             :         }
     214             : 
     215          24 :         if ((header = php_http_env_get_request_header(header_str, header_len, NULL, request TSRMLS_CC))) {
     216           4 :                 ums = php_parse_date(header, NULL);
     217             : 
     218           4 :                 if (ums > 0 && ums >= lm) {
     219           4 :                         ret = PHP_HTTP_CACHE_HIT;
     220             :                 } else {
     221           0 :                         ret = PHP_HTTP_CACHE_MISS;
     222             :                 }
     223             :         }
     224             : 
     225          24 :         PTR_FREE(header);
     226          24 :         return ret;
     227             : }
     228             : 
     229          16 : static zend_bool php_http_env_response_is_cacheable(php_http_env_response_t *r, php_http_message_t *request)
     230             : {
     231             :         TSRMLS_FETCH_FROM_CTX(r->ts);
     232             : 
     233          16 :         if (r->ops->get_status(r) >= 400) {
     234           0 :                 return 0;
     235             :         }
     236             : 
     237          16 :         if (php_http_env_got_request_header(ZEND_STRL("Authorization"), request TSRMLS_CC)) {
     238           0 :                 return 0;
     239             :         }
     240             : 
     241          16 :         if (-1 == php_http_select_str(php_http_env_get_request_method(request TSRMLS_CC), 2, "HEAD", "GET")) {
     242           2 :                 return 0;
     243             :         }
     244             : 
     245          14 :         return 1;
     246             : }
     247             : 
     248        3296 : static size_t output(void *context, char *buf, size_t len TSRMLS_DC)
     249             : {
     250        3296 :         php_http_env_response_t *r = context;
     251             : 
     252        3296 :         if (SUCCESS != r->ops->write(r, buf, len)) {
     253           0 :                 return (size_t) -1;
     254             :         }
     255             : 
     256             :         /*      we really only need to flush when throttling is enabled,
     257             :                 because we push the data as fast as possible anyway if not */
     258        3296 :         if (r->throttle.delay >= PHP_HTTP_DIFFSEC) {
     259           6 :                 r->ops->flush(r);
     260           6 :                 php_http_sleep(r->throttle.delay);
     261             :         }
     262        3296 :         return len;
     263             : }
     264             : 
     265             : #define php_http_env_response_send_done(r) php_http_env_response_send_data((r), NULL, 0)
     266       32802 : static STATUS php_http_env_response_send_data(php_http_env_response_t *r, const char *buf, size_t len)
     267             : {
     268       32802 :         size_t chunks_sent, chunk = r->throttle.chunk ? r->throttle.chunk : PHP_HTTP_SENDBUF_SIZE;
     269             :         TSRMLS_FETCH_FROM_CTX(r->ts);
     270             : 
     271       32802 :         if (r->content.encoder) {
     272           6 :                 char *enc_str = NULL;
     273           6 :                 size_t enc_len = 0;
     274             : 
     275           6 :                 if (buf) {
     276           3 :                         if (SUCCESS != php_http_encoding_stream_update(r->content.encoder, buf, len, &enc_str, &enc_len)) {
     277           0 :                                 return FAILURE;
     278             :                         }
     279             :                 } else {
     280           3 :                         if (SUCCESS != php_http_encoding_stream_finish(r->content.encoder, &enc_str, &enc_len)) {
     281           0 :                                 return FAILURE;
     282             :                         }
     283             :                 }
     284             : 
     285           6 :                 if (!enc_str) {
     286           0 :                         return SUCCESS;
     287             :                 }
     288           6 :                 chunks_sent = php_http_buffer_chunked_output(&r->buffer, enc_str, enc_len, buf ? chunk : 0, output, r TSRMLS_CC);
     289           6 :                 PTR_FREE(enc_str);
     290             :         } else {
     291       32796 :                 chunks_sent = php_http_buffer_chunked_output(&r->buffer, buf, len, buf ? chunk : 0, output, r TSRMLS_CC);
     292             :         }
     293             : 
     294       32802 :         return chunks_sent != (size_t) -1 ? SUCCESS : FAILURE;
     295             : }
     296             : 
     297          21 : 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)
     298             : {
     299             :         zend_bool free_r;
     300             : 
     301          21 :         if ((free_r = !r)) {
     302          11 :                 r = emalloc(sizeof(*r));
     303             :         }
     304          21 :         memset(r, 0, sizeof(*r));
     305             : 
     306          21 :         if (ops) {
     307          11 :                 r->ops = ops;
     308             :         } else {
     309          10 :                 r->ops = php_http_env_response_get_sapi_ops();
     310             :         }
     311             : 
     312          21 :         r->buffer = php_http_buffer_init(NULL);
     313             : 
     314          21 :         Z_ADDREF_P(options);
     315          21 :         r->options = options;
     316             : 
     317             :         TSRMLS_SET_CTX(r->ts);
     318             : 
     319          21 :         if (r->ops->init && (SUCCESS != r->ops->init(r, init_arg))) {
     320           0 :                 if (free_r) {
     321           0 :                         php_http_env_response_free(&r);
     322             :                 } else {
     323           0 :                         php_http_env_response_dtor(r);
     324           0 :                         r = NULL;
     325             :                 }
     326             :         }
     327             : 
     328          21 :         return r;
     329             : }
     330             : 
     331          21 : void php_http_env_response_dtor(php_http_env_response_t *r)
     332             : {
     333          21 :         if (r->ops->dtor) {
     334          11 :                 r->ops->dtor(r);
     335             :         }
     336          21 :         php_http_buffer_free(&r->buffer);
     337          21 :         zval_ptr_dtor(&r->options);
     338          21 :         PTR_FREE(r->content.type);
     339          21 :         PTR_FREE(r->content.encoding);
     340          21 :         if (r->content.encoder) {
     341           3 :                 php_http_encoding_stream_free(&r->content.encoder);
     342             :         }
     343          21 : }
     344             : 
     345          11 : void php_http_env_response_free(php_http_env_response_t **r)
     346             : {
     347          11 :         if (*r) {
     348          11 :                 php_http_env_response_dtor(*r);
     349          11 :                 efree(*r);
     350          11 :                 *r = NULL;
     351             :         }
     352          11 : }
     353             : 
     354          21 : static STATUS php_http_env_response_send_head(php_http_env_response_t *r, php_http_message_t *request)
     355             : {
     356          21 :         STATUS ret = SUCCESS;
     357          21 :         zval *zoption, *options = r->options;
     358             :         TSRMLS_FETCH_FROM_CTX(r->ts);
     359             : 
     360          21 :         if (r->done) {
     361           1 :                 return ret;
     362             :         }
     363             : 
     364          20 :         if ((zoption = get_option(options, ZEND_STRL("headers") TSRMLS_CC))) {
     365          20 :                 if (Z_TYPE_P(zoption) == IS_ARRAY) {
     366          20 :                         php_http_header_to_callback(Z_ARRVAL_P(zoption), 0, (php_http_pass_format_callback_t) r->ops->set_header, r TSRMLS_CC);
     367             :                 }
     368          20 :                 zval_ptr_dtor(&zoption);
     369             :         }
     370             : 
     371          20 :         if (ret != SUCCESS) {
     372           0 :                 return ret;
     373             :         }
     374             : 
     375          20 :         if ((zoption = get_option(options, ZEND_STRL("responseCode") TSRMLS_CC))) {
     376          20 :                 zval *zoption_copy = php_http_ztyp(IS_LONG, zoption);
     377             : 
     378          20 :                 zval_ptr_dtor(&zoption);
     379          20 :                 if (Z_LVAL_P(zoption_copy) > 0) {
     380          20 :                         ret = r->ops->set_status(r, Z_LVAL_P(zoption_copy));
     381             :                 }
     382          20 :                 zval_ptr_dtor(&zoption_copy);
     383             :         }
     384             : 
     385          20 :         if (ret != SUCCESS) {
     386           0 :                 return ret;
     387             :         }
     388             : 
     389          20 :         if ((zoption = get_option(options, ZEND_STRL("httpVersion") TSRMLS_CC))) {
     390             :                 php_http_version_t v;
     391          20 :                 zval *zoption_copy = php_http_ztyp(IS_STRING, zoption);
     392             : 
     393          20 :                 zval_ptr_dtor(&zoption);
     394          20 :                 if (Z_STRLEN_P(zoption_copy) && php_http_version_parse(&v, Z_STRVAL_P(zoption_copy) TSRMLS_CC)) {
     395          20 :                         ret = r->ops->set_protocol_version(r, &v);
     396          20 :                         php_http_version_dtor(&v);
     397             :                 }
     398          20 :                 zval_ptr_dtor(&zoption_copy);
     399             :         }
     400             : 
     401          20 :         if (ret != SUCCESS) {
     402           0 :                 return ret;
     403             :         }
     404             : 
     405          20 :         if ((zoption = get_option(options, ZEND_STRL("cookies") TSRMLS_CC))) {
     406          20 :                 if (Z_TYPE_P(zoption) == IS_ARRAY) {
     407             :                         HashPosition pos;
     408             :                         zval **zcookie;
     409             : 
     410           4 :                         FOREACH_VAL(pos, zoption, zcookie) {
     411           3 :                                 if (Z_TYPE_PP(zcookie) == IS_OBJECT && instanceof_function(Z_OBJCE_PP(zcookie), php_http_cookie_class_entry TSRMLS_CC)) {
     412           3 :                                         php_http_cookie_object_t *obj = zend_object_store_get_object(*zcookie TSRMLS_CC);
     413             :                                         char *str;
     414             :                                         size_t len;
     415             : 
     416           3 :                                         php_http_cookie_list_to_string(obj->list, &str, &len);
     417           3 :                                         if (SUCCESS != (ret = r->ops->add_header(r, "Set-Cookie: %s", str))) {
     418           0 :                                                 efree(str);
     419           0 :                                                 break;
     420             :                                         }
     421           3 :                                         efree(str);
     422             :                                 }
     423             :                         }
     424             :                 }
     425          20 :                 zval_ptr_dtor(&zoption);
     426             :         }
     427             : 
     428          20 :         if (ret != SUCCESS) {
     429           0 :                 return ret;
     430             :         }
     431             : 
     432          20 :         if ((zoption = get_option(options, ZEND_STRL("contentType") TSRMLS_CC))) {
     433          20 :                 zval *zoption_copy = php_http_ztyp(IS_STRING, zoption);
     434             : 
     435          20 :                 zval_ptr_dtor(&zoption);
     436          20 :                 if (Z_STRLEN_P(zoption_copy) && strchr(Z_STRVAL_P(zoption_copy), '/')) {
     437           3 :                         if (SUCCESS == (ret = r->ops->set_header(r, "Content-Type: %.*s", Z_STRLEN_P(zoption_copy), Z_STRVAL_P(zoption_copy)))) {
     438           3 :                                 r->content.type = estrndup(Z_STRVAL_P(zoption_copy), Z_STRLEN_P(zoption_copy));
     439             :                         }
     440             :                 }
     441          20 :                 zval_ptr_dtor(&zoption_copy);
     442             :         }
     443             : 
     444          20 :         if (ret != SUCCESS) {
     445           0 :                 return ret;
     446             :         }
     447             : 
     448          20 :         if (r->range.status == PHP_HTTP_RANGE_OK) {
     449           4 :                 if (zend_hash_num_elements(&r->range.values) == 1) {
     450             :                         zval **range, **begin, **end;
     451             : 
     452           3 :                         if (    1 == php_http_array_list(&r->range.values TSRMLS_CC, 1, &range)
     453           3 :                                 &&      2 == php_http_array_list(Z_ARRVAL_PP(range) TSRMLS_CC, 2, &begin, &end)
     454             :                         ) {
     455           6 :                                 if (SUCCESS == (ret = r->ops->set_status(r, 206))) {
     456           3 :                                         ret = r->ops->set_header(r, "Content-Range: bytes %ld-%ld/%zu", Z_LVAL_PP(begin), Z_LVAL_PP(end), r->content.length);
     457             :                                 }
     458             :                         } else {
     459             :                                 /* this should never happen */
     460           0 :                                 zend_hash_destroy(&r->range.values);
     461           0 :                                 ret = FAILURE;
     462             :                         }
     463             :                 } else {
     464           1 :                         php_http_boundary(r->range.boundary, sizeof(r->range.boundary) TSRMLS_CC);
     465           1 :                         if (SUCCESS == (ret = r->ops->set_status(r, 206))) {
     466           1 :                                 ret = r->ops->set_header(r, "Content-Type: multipart/byteranges; boundary=%s", r->range.boundary);
     467             :                         }
     468             :                 }
     469             :         } else {
     470          16 :                 if ((zoption = get_option(options, ZEND_STRL("cacheControl") TSRMLS_CC))) {
     471          16 :                         zval *zoption_copy = php_http_ztyp(IS_STRING, zoption);
     472             : 
     473          16 :                         zval_ptr_dtor(&zoption);
     474          16 :                         if (Z_STRLEN_P(zoption_copy)) {
     475           2 :                                 ret = r->ops->set_header(r, "Cache-Control: %.*s", Z_STRLEN_P(zoption_copy), Z_STRVAL_P(zoption_copy));
     476             :                         }
     477          16 :                         zval_ptr_dtor(&zoption_copy);
     478             :                 }
     479             : 
     480          16 :                 if (ret != SUCCESS) {
     481           0 :                         return ret;
     482             :                 }
     483             : 
     484          16 :                 if ((zoption = get_option(options, ZEND_STRL("contentDisposition") TSRMLS_CC))) {
     485          16 :                         zval *zoption_copy = php_http_ztyp(IS_ARRAY, zoption);
     486             :                         php_http_buffer_t buf;
     487             : 
     488          16 :                         php_http_buffer_init(&buf);
     489          16 :                         if (php_http_params_to_string(&buf, Z_ARRVAL_P(zoption_copy), ZEND_STRL(","), ZEND_STRL(";"), ZEND_STRL("="), PHP_HTTP_PARAMS_DEFAULT TSRMLS_CC)) {
     490          16 :                                 if (buf.used) {
     491           1 :                                         ret = r->ops->set_header(r, "Content-Disposition: %.*s", buf.used, buf.data);
     492             :                                 }
     493             :                         }
     494             : 
     495          16 :                         php_http_buffer_dtor(&buf);
     496          16 :                         zval_ptr_dtor(&zoption_copy);
     497          16 :                         zval_ptr_dtor(&zoption);
     498             :                 }
     499             : 
     500          16 :                 if (ret != SUCCESS) {
     501           0 :                         return ret;
     502             :                 }
     503             : 
     504          16 :                 if ((zoption = get_option(options, ZEND_STRL("contentEncoding") TSRMLS_CC))) {
     505          16 :                         zval *zoption_copy = php_http_ztyp(IS_LONG, zoption);
     506             :                         zval zsupported;
     507          16 :                         HashTable *result = NULL;
     508             : 
     509          16 :                         zval_ptr_dtor(&zoption);
     510          16 :                         switch (Z_LVAL_P(zoption_copy)) {
     511             :                                 case PHP_HTTP_CONTENT_ENCODING_GZIP:
     512           3 :                                         INIT_PZVAL(&zsupported);
     513           3 :                                         array_init(&zsupported);
     514           3 :                                         add_next_index_stringl(&zsupported, ZEND_STRL("none"), 1);
     515           3 :                                         add_next_index_stringl(&zsupported, ZEND_STRL("gzip"), 1);
     516           3 :                                         add_next_index_stringl(&zsupported, ZEND_STRL("deflate"), 1);
     517             : 
     518           3 :                                         if ((result = php_http_negotiate_encoding(Z_ARRVAL(zsupported), request TSRMLS_CC))) {
     519           3 :                                                 char *key_str = NULL;
     520           3 :                                                 uint key_len = 0;
     521             : 
     522           3 :                                                 zend_hash_internal_pointer_reset(result);
     523           3 :                                                 if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(result, &key_str, &key_len, NULL, 0, NULL)) {
     524           3 :                                                         if (!strcmp(key_str, "gzip")) {
     525           2 :                                                                 if (!(r->content.encoder = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), PHP_HTTP_DEFLATE_TYPE_GZIP TSRMLS_CC))) {
     526           0 :                                                                         ret = FAILURE;
     527           2 :                                                                 } else if (SUCCESS == (ret = r->ops->set_header(r, "Content-Encoding: gzip"))) {
     528           2 :                                                                         r->content.encoding = estrndup(key_str, key_len - 1);
     529             :                                                                 }
     530           1 :                                                         } else if (!strcmp(key_str, "deflate")) {
     531           1 :                                                                 if (!(r->content.encoder = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), PHP_HTTP_DEFLATE_TYPE_ZLIB TSRMLS_CC))) {
     532           0 :                                                                         ret = FAILURE;
     533           1 :                                                                 } else if (SUCCESS == (ret = r->ops->set_header(r, "Content-Encoding: deflate"))) {
     534           1 :                                                                         r->content.encoding = estrndup(key_str, key_len - 1);
     535             :                                                                 }
     536             :                                                         } else {
     537           0 :                                                                 ret = r->ops->del_header(r, ZEND_STRL("Content-Encoding"));
     538             :                                                         }
     539             : 
     540           3 :                                                         if (SUCCESS == ret) {
     541           3 :                                                                 ret = r->ops->add_header(r, "Vary: Accept-Encoding");
     542             :                                                         }
     543             :                                                 }
     544             : 
     545           3 :                                                 zend_hash_destroy(result);
     546           3 :                                                 FREE_HASHTABLE(result);
     547             :                                         }
     548             : 
     549           3 :                                         zval_dtor(&zsupported);
     550           3 :                                         break;
     551             : 
     552             :                                 case PHP_HTTP_CONTENT_ENCODING_NONE:
     553             :                                 default:
     554          13 :                                         ret = r->ops->del_header(r, ZEND_STRL("Content-Encoding"));
     555          13 :                                         break;
     556             :                         }
     557          16 :                         zval_ptr_dtor(&zoption_copy);
     558             :                 }
     559             : 
     560          16 :                 if (SUCCESS != ret) {
     561           0 :                         return ret;
     562             :                 }
     563             : 
     564          16 :                 if (php_http_env_response_is_cacheable(r, request)) {
     565          14 :                         switch (php_http_env_is_response_cached_by_etag(options, ZEND_STRL("If-None-Match"), request TSRMLS_CC)) {
     566             :                                 case PHP_HTTP_CACHE_MISS:
     567           2 :                                         break;
     568             : 
     569             :                                 case PHP_HTTP_CACHE_NO:
     570          12 :                                         if (PHP_HTTP_CACHE_HIT != php_http_env_is_response_cached_by_last_modified(options, ZEND_STRL("If-Modified-Since"), request TSRMLS_CC)) {
     571          10 :                                                 break;
     572             :                                         }
     573             :                                         /*  no break */
     574             : 
     575             :                                 case PHP_HTTP_CACHE_HIT:
     576           2 :                                         ret = r->ops->set_status(r, 304);
     577           2 :                                         r->done = 1;
     578           2 :                                         break;
     579             :                         }
     580             : 
     581          14 :                         if ((zoption = get_option(options, ZEND_STRL("etag") TSRMLS_CC))) {
     582          14 :                                 zval *zoption_copy = php_http_ztyp(IS_STRING, zoption);
     583             : 
     584          14 :                                 zval_ptr_dtor(&zoption);
     585          14 :                                 if (*Z_STRVAL_P(zoption_copy) != '"' &&    strncmp(Z_STRVAL_P(zoption_copy), "W/\"", 3)) {
     586          14 :                                         ret = r->ops->set_header(r, "ETag: \"%s\"", Z_STRVAL_P(zoption_copy));
     587             :                                 } else {
     588           0 :                                         ret = r->ops->set_header(r, "ETag: %s", Z_STRVAL_P(zoption_copy));
     589             :                                 }
     590          14 :                                 zval_ptr_dtor(&zoption_copy);
     591             :                         }
     592          14 :                         if ((zoption = get_option(options, ZEND_STRL("lastModified") TSRMLS_CC))) {
     593          14 :                                 zval *zoption_copy = php_http_ztyp(IS_LONG, zoption);
     594             : 
     595          14 :                                 zval_ptr_dtor(&zoption);
     596          14 :                                 if (Z_LVAL_P(zoption_copy)) {
     597           8 :                                         char *date = php_format_date(ZEND_STRL(PHP_HTTP_DATE_FORMAT), Z_LVAL_P(zoption_copy), 0 TSRMLS_CC);
     598           8 :                                         if (date) {
     599           8 :                                                 ret = r->ops->set_header(r, "Last-Modified: %s", date);
     600           8 :                                                 efree(date);
     601             :                                         }
     602             :                                 }
     603          14 :                                 zval_ptr_dtor(&zoption_copy);
     604             :                         }
     605             :                 }
     606             :         }
     607             : 
     608          20 :         return ret;
     609             : }
     610             : 
     611          21 : static STATUS php_http_env_response_send_body(php_http_env_response_t *r)
     612             : {
     613          21 :         STATUS ret = SUCCESS;
     614             :         zval *zoption;
     615             :         php_http_message_body_t *body;
     616             :         TSRMLS_FETCH_FROM_CTX(r->ts);
     617             : 
     618          21 :         if (r->done) {
     619           3 :                 return ret;
     620             :         }
     621             : 
     622          18 :         if ((body = get_body(r->options TSRMLS_CC))) {
     623          15 :                 if ((zoption = get_option(r->options, ZEND_STRL("throttleDelay") TSRMLS_CC))) {
     624          15 :                         if (Z_TYPE_P(zoption) == IS_DOUBLE) {
     625           1 :                                 r->throttle.delay =  Z_DVAL_P(zoption);
     626             :                         }
     627          15 :                         zval_ptr_dtor(&zoption);
     628             :                 }
     629          15 :                 if ((zoption = get_option(r->options, ZEND_STRL("throttleChunk") TSRMLS_CC))) {
     630          15 :                         if (Z_TYPE_P(zoption) == IS_LONG) {
     631           1 :                                 r->throttle.chunk = Z_LVAL_P(zoption);
     632             :                         }
     633          15 :                         zval_ptr_dtor(&zoption);
     634             :                 }
     635             : 
     636          15 :                 if (r->range.status == PHP_HTTP_RANGE_OK) {
     637           4 :                         if (zend_hash_num_elements(&r->range.values) == 1) {
     638             :                                 /* single range */
     639             :                                 zval **range, **begin, **end;
     640             : 
     641           3 :                                 if (    1 == php_http_array_list(&r->range.values TSRMLS_CC, 1, &range)
     642           3 :                                         &&      2 == php_http_array_list(Z_ARRVAL_PP(range) TSRMLS_CC, 2, &begin, &end)
     643             :                                 ) {
     644             :                                         /* send chunk */
     645           3 :                                         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);
     646           3 :                                         if (ret == SUCCESS) {
     647           3 :                                                 ret = php_http_env_response_send_done(r);
     648             :                                         }
     649           3 :                                         zend_hash_destroy(&r->range.values);
     650             :                                 } else {
     651             :                                         /* this should never happen */
     652           0 :                                         zend_hash_destroy(&r->range.values);
     653           0 :                                         r->ops->set_status(r, 500);
     654           0 :                                         ret = FAILURE;
     655             :                                 }
     656             : 
     657             :                         } else {
     658             :                                 /* send multipart/byte-ranges message */
     659             :                                 HashPosition pos;
     660             :                                 zval **chunk;
     661             : 
     662           6 :                                 FOREACH_HASH_VAL(pos, &r->range.values, chunk) {
     663             :                                         zval **begin, **end;
     664             : 
     665           5 :                                         if (2 == php_http_array_list(Z_ARRVAL_PP(chunk) TSRMLS_CC, 2, &begin, &end)) {
     666          25 :                                                 php_http_buffer_appendf(r->buffer,
     667             :                                                                 PHP_HTTP_CRLF
     668             :                                                                 "--%s" PHP_HTTP_CRLF
     669             :                                                                 "Content-Type: %s" PHP_HTTP_CRLF
     670             :                                                                 "Content-Range: bytes %ld-%ld/%zu" PHP_HTTP_CRLF PHP_HTTP_CRLF,
     671             :                                                                 /* - */
     672           5 :                                                                 r->range.boundary,
     673           5 :                                                                 r->content.type ? r->content.type : "application/octet-stream",
     674           5 :                                                                 Z_LVAL_PP(begin),
     675           5 :                                                                 Z_LVAL_PP(end),
     676             :                                                                 r->content.length
     677             :                                                 );
     678           5 :                                                 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);
     679             :                                         }
     680             :                                 }
     681             : 
     682           1 :                                 if (ret == SUCCESS) {
     683           1 :                                         php_http_buffer_appendf(r->buffer, PHP_HTTP_CRLF "--%s--", r->range.boundary);
     684           1 :                                         ret = php_http_env_response_send_done(r);
     685             :                                 }
     686           1 :                                 zend_hash_destroy(&r->range.values);
     687             :                         }
     688             : 
     689             :                 } else {
     690          11 :                         ret = php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_env_response_send_data, r, 0, 0);
     691          11 :                         if (ret == SUCCESS) {
     692          11 :                                 ret = php_http_env_response_send_done(r);
     693             :                         }
     694             :                 }
     695             :         }
     696          18 :         return ret;
     697             : }
     698             : 
     699          21 : STATUS php_http_env_response_send(php_http_env_response_t *r)
     700             : {
     701             :         php_http_message_t *request;
     702             :         php_http_message_body_t *body;
     703             :         TSRMLS_FETCH_FROM_CTX(r->ts);
     704             : 
     705          21 :         request = get_request(r->options TSRMLS_CC);
     706             : 
     707             :         /* check for ranges */
     708          21 :         if ((body = get_body(r->options TSRMLS_CC))) {
     709          18 :                 r->content.length = php_http_message_body_size(body);
     710             : 
     711          18 :                 if (SUCCESS != r->ops->set_header(r, "Accept-Ranges: bytes")) {
     712           0 :                         return FAILURE;
     713             :                 } else {
     714          18 :                         zend_hash_init(&r->range.values, 0, NULL, ZVAL_PTR_DTOR, 0);
     715          18 :                         r->range.status = php_http_env_get_request_ranges(&r->range.values, r->content.length, request TSRMLS_CC);
     716             : 
     717          18 :                         switch (r->range.status) {
     718             :                                 case PHP_HTTP_RANGE_NO:
     719          13 :                                         zend_hash_destroy(&r->range.values);
     720          13 :                                         break;
     721             : 
     722             :                                 case PHP_HTTP_RANGE_ERR:
     723           1 :                                         if (php_http_env_got_request_header(ZEND_STRL("If-Range"), request TSRMLS_CC)) {
     724           0 :                                                 r->range.status = PHP_HTTP_RANGE_NO;
     725           0 :                                                 zend_hash_destroy(&r->range.values);
     726             :                                         } else {
     727           1 :                                                 r->done = 1;
     728           1 :                                                 zend_hash_destroy(&r->range.values);
     729           1 :                                                 if (SUCCESS != r->ops->set_status(r, 416)) {
     730           0 :                                                         return FAILURE;
     731             :                                                 }
     732           1 :                                                 if (SUCCESS != r->ops->set_header(r, "Content-Range: bytes */%zu", r->content.length)) {
     733           0 :                                                         return FAILURE;
     734             :                                                 }
     735             :                                         }
     736           1 :                                         break;
     737             : 
     738             :                                 case PHP_HTTP_RANGE_OK:
     739           4 :                                         if (PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_etag(r->options, ZEND_STRL("If-Range"), request TSRMLS_CC)
     740           4 :                                         ||      PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("If-Range"), request TSRMLS_CC)
     741             :                                         ) {
     742           0 :                                                 r->range.status = PHP_HTTP_RANGE_NO;
     743           0 :                                                 zend_hash_destroy(&r->range.values);
     744           0 :                                                 break;
     745             :                                         }
     746           4 :                                         if (PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_etag(r->options, ZEND_STRL("If-Match"), request TSRMLS_CC)
     747           4 :                                         ||      PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("If-Unmodified-Since"), request TSRMLS_CC)
     748           4 :                                         ||      PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("Unless-Modified-Since"), request TSRMLS_CC)
     749             :                                         ) {
     750           0 :                                                 r->done = 1;
     751           0 :                                                 zend_hash_destroy(&r->range.values);
     752           0 :                                                 if (SUCCESS != r->ops->set_status(r, 412)) {
     753           0 :                                                         return FAILURE;
     754             :                                                 }
     755           0 :                                                 break;
     756             :                                         }
     757             : 
     758           4 :                                         break;
     759             :                         }
     760             :                 }
     761             :         }
     762             : 
     763          21 :         if (SUCCESS != php_http_env_response_send_head(r, request)) {
     764           0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to send response headers");
     765           0 :                 return FAILURE;
     766             :         }
     767             : 
     768          21 :         if (SUCCESS != php_http_env_response_send_body(r)) {
     769           0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to send response body");
     770           0 :                 return FAILURE;
     771             :         }
     772             : 
     773          21 :         if (SUCCESS != r->ops->finish(r)) {
     774           0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to finish response");
     775           0 :                 return FAILURE;
     776             :         }
     777             : 
     778          21 :         return SUCCESS;
     779             : }
     780             : 
     781           8 : static long php_http_env_response_sapi_get_status(php_http_env_response_t *r)
     782             : {
     783             :         TSRMLS_FETCH_FROM_CTX(r->ts);
     784             : 
     785           8 :         return php_http_env_get_response_code(TSRMLS_C);
     786             : }
     787          14 : static STATUS php_http_env_response_sapi_set_status(php_http_env_response_t *r, long http_code)
     788             : {
     789             :         TSRMLS_FETCH_FROM_CTX(r->ts);
     790             : 
     791          14 :         return php_http_env_set_response_code(http_code TSRMLS_CC);
     792             : }
     793          10 : static STATUS php_http_env_response_sapi_set_protocol_version(php_http_env_response_t *r, php_http_version_t *v)
     794             : {
     795             :         TSRMLS_FETCH_FROM_CTX(r->ts);
     796             : 
     797          10 :         return php_http_env_set_response_protocol_version(v TSRMLS_CC);
     798             : }
     799          40 : static STATUS php_http_env_response_sapi_set_header(php_http_env_response_t *r, const char *fmt, ...)
     800             : {
     801             :         STATUS ret;
     802             :         va_list args;
     803             :         TSRMLS_FETCH_FROM_CTX(r->ts);
     804             : 
     805          40 :         va_start(args, fmt);
     806          40 :         ret = php_http_env_set_response_header_va(0, 1, fmt, args TSRMLS_CC);
     807          40 :         va_end(args);
     808             : 
     809          40 :         return ret;
     810             : }
     811           2 : static STATUS php_http_env_response_sapi_add_header(php_http_env_response_t *r, const char *fmt, ...)
     812             : {
     813             :         STATUS ret;
     814             :         va_list args;
     815             :         TSRMLS_FETCH_FROM_CTX(r->ts);
     816             : 
     817           2 :         va_start(args, fmt);
     818           2 :         ret = php_http_env_set_response_header_va(0, 0, fmt, args TSRMLS_CC);
     819           2 :         va_end(args);
     820             : 
     821           2 :         return ret;
     822             : }
     823           6 : static STATUS php_http_env_response_sapi_del_header(php_http_env_response_t *r, const char *header_str, size_t header_len)
     824             : {
     825             :         TSRMLS_FETCH_FROM_CTX(r->ts);
     826             : 
     827           6 :         return php_http_env_set_response_header_value(0, header_str, header_len, NULL, 1 TSRMLS_CC);
     828             : }
     829          12 : static STATUS php_http_env_response_sapi_write(php_http_env_response_t *r, const char *data_str, size_t data_len)
     830             : {
     831             :         TSRMLS_FETCH_FROM_CTX(r->ts);
     832             : 
     833          12 :         if (0 < PHPWRITE(data_str, data_len)) {
     834          12 :                 return SUCCESS;
     835             :         }
     836           0 :         return FAILURE;
     837             : }
     838           6 : static STATUS php_http_env_response_sapi_flush(php_http_env_response_t *r)
     839             : {
     840             :         TSRMLS_FETCH_FROM_CTX(r->ts);
     841             : 
     842             : #if PHP_VERSION_ID >= 50400
     843           6 :         if (php_output_get_level(TSRMLS_C)) {
     844           0 :                 php_output_flush_all(TSRMLS_C);
     845             :         }
     846           6 :         if (!(php_output_get_status(TSRMLS_C) & PHP_OUTPUT_IMPLICITFLUSH)) {
     847           6 :                 sapi_flush(TSRMLS_C);
     848             :         }
     849             : #else
     850             :         php_end_ob_buffer(1, 1 TSRMLS_CC);
     851             :         sapi_flush(TSRMLS_C);
     852             : #endif
     853             : 
     854           6 :         return SUCCESS;
     855             : }
     856          10 : static STATUS php_http_env_response_sapi_finish(php_http_env_response_t *r)
     857             : {
     858          10 :         return SUCCESS;
     859             : }
     860             : 
     861             : static php_http_env_response_ops_t php_http_env_response_sapi_ops = {
     862             :         NULL,
     863             :         NULL,
     864             :         php_http_env_response_sapi_get_status,
     865             :         php_http_env_response_sapi_set_status,
     866             :         php_http_env_response_sapi_set_protocol_version,
     867             :         php_http_env_response_sapi_set_header,
     868             :         php_http_env_response_sapi_add_header,
     869             :         php_http_env_response_sapi_del_header,
     870             :         php_http_env_response_sapi_write,
     871             :         php_http_env_response_sapi_flush,
     872             :         php_http_env_response_sapi_finish
     873             : };
     874             : 
     875          10 : php_http_env_response_ops_t *php_http_env_response_get_sapi_ops(void)
     876             : {
     877          10 :         return &php_http_env_response_sapi_ops;
     878             : }
     879             : 
     880             : typedef struct php_http_env_response_stream_ctx {
     881             :         HashTable header;
     882             :         php_http_version_t version;
     883             :         long status_code;
     884             : 
     885             :         php_stream *stream;
     886             :         php_stream_filter *chunked_filter;
     887             :         php_http_message_t *request;
     888             : 
     889             :         unsigned started:1;
     890             :         unsigned finished:1;
     891             :         unsigned chunked:1;
     892             : } php_http_env_response_stream_ctx_t;
     893             : 
     894          11 : static STATUS php_http_env_response_stream_init(php_http_env_response_t *r, void *init_arg)
     895             : {
     896             :         php_http_env_response_stream_ctx_t *ctx;
     897          11 :         size_t buffer_size = 0x1000;
     898             :         TSRMLS_FETCH_FROM_CTX(r->ts);
     899             : 
     900          11 :         ctx = ecalloc(1, sizeof(*ctx));
     901             : 
     902          11 :         ctx->stream = init_arg;
     903          11 :         if (!ctx->stream || SUCCESS != zend_list_addref(ctx->stream->rsrc_id)) {
     904           0 :                 efree(ctx);
     905           0 :                 return FAILURE;
     906             :         }
     907          11 :         php_stream_set_option(ctx->stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_FULL, &buffer_size);
     908          11 :         zend_hash_init(&ctx->header, 0, NULL, ZVAL_PTR_DTOR, 0);
     909          11 :         php_http_version_init(&ctx->version, 1, 1 TSRMLS_CC);
     910          11 :         ctx->status_code = 200;
     911          11 :         ctx->chunked = 1;
     912          11 :         ctx->request = get_request(r->options TSRMLS_CC);
     913             : 
     914             :         /* there are some limitations regarding TE:chunked, see https://tools.ietf.org/html/rfc7230#section-3.3.1 */
     915          11 :         if (ctx->request && ctx->request->http.version.major == 1 && ctx->request->http.version.minor == 0) {
     916           0 :                 ctx->version.minor = 0;
     917             :         }
     918             : 
     919          11 :         r->ctx = ctx;
     920             : 
     921          11 :         return SUCCESS;
     922             : }
     923          11 : static void php_http_env_response_stream_dtor(php_http_env_response_t *r)
     924             : {
     925          11 :         php_http_env_response_stream_ctx_t *ctx = r->ctx;
     926             :         TSRMLS_FETCH_FROM_CTX(r->ts);
     927             : 
     928          11 :         if (ctx->chunked_filter) {
     929           0 :                 php_stream_filter_free(ctx->chunked_filter TSRMLS_CC);
     930             :         }
     931          11 :         zend_hash_destroy(&ctx->header);
     932          11 :         zend_list_delete(ctx->stream->rsrc_id);
     933          11 :         efree(ctx);
     934          11 :         r->ctx = NULL;
     935          11 : }
     936          12 : 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)
     937             : {
     938             :         HashPosition pos;
     939             :         zval **val;
     940             : 
     941          48 :         FOREACH_HASH_VAL(pos, header, val) {
     942          36 :                 if (Z_TYPE_PP(val) == IS_ARRAY) {
     943           1 :                         php_http_env_response_stream_header(ctx, Z_ARRVAL_PP(val), buf TSRMLS_CC);
     944             :                 } else {
     945          35 :                         zval *tmp = php_http_ztyp(IS_STRING, *val);
     946             : 
     947          35 :                         if (ctx->chunked) {
     948             :                                 /* disable chunked transfer encoding if we've got an explicit content-length */
     949          35 :                                 if (!strncasecmp(Z_STRVAL_P(tmp), "Content-Length:", lenof("Content-Length:"))) {
     950           0 :                                         ctx->chunked = 0;
     951             :                                 }
     952             :                         }
     953          35 :                         php_http_buffer_append(buf, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
     954          35 :                         php_http_buffer_appends(buf, PHP_HTTP_CRLF);
     955          35 :                         zval_ptr_dtor(&tmp);
     956             :                 }
     957             :         }
     958          12 : }
     959          11 : static STATUS php_http_env_response_stream_start(php_http_env_response_stream_ctx_t *ctx TSRMLS_DC)
     960             : {
     961             :         php_http_buffer_t header_buf;
     962             : 
     963          11 :         if (ctx->started || ctx->finished) {
     964           0 :                 return FAILURE;
     965             :         }
     966             : 
     967          11 :         php_http_buffer_init(&header_buf);
     968          11 :         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));
     969             : 
     970             :         /* there are some limitations regarding TE:chunked, see https://tools.ietf.org/html/rfc7230#section-3.3.1 */
     971          11 :         if (ctx->version.major == 1 && ctx->version.minor == 0) {
     972           0 :                 ctx->chunked = 0;
     973          11 :         } else if (ctx->status_code == 204 || ctx->status_code/100 == 1) {
     974           0 :                 ctx->chunked = 0;
     975          11 :         } else if (ctx->request && ctx->status_code/100 == 2 && !strcasecmp(ctx->request->http.info.request.method, "CONNECT")) {
     976           1 :                 ctx->chunked = 0;
     977             :         }
     978             : 
     979          11 :         php_http_env_response_stream_header(ctx, &ctx->header, &header_buf TSRMLS_CC);
     980             : 
     981             :         /* enable chunked transfer encoding */
     982          11 :         if (ctx->chunked) {
     983          10 :                 php_http_buffer_appends(&header_buf, "Transfer-Encoding: chunked" PHP_HTTP_CRLF);
     984             :         }
     985          11 :         php_http_buffer_appends(&header_buf, PHP_HTTP_CRLF);
     986             : 
     987          11 :         if (header_buf.used == php_stream_write(ctx->stream, header_buf.data, header_buf.used)) {
     988          11 :                 ctx->started = 1;
     989             :         }
     990          11 :         php_http_buffer_dtor(&header_buf);
     991          11 :         php_stream_flush(ctx->stream);
     992             : 
     993          11 :         if (ctx->chunked) {
     994          10 :                 ctx->chunked_filter = php_stream_filter_create("http.chunked_encode", NULL, 0 TSRMLS_CC);
     995          10 :                 php_stream_filter_append(&ctx->stream->writefilters, ctx->chunked_filter);
     996             :         }
     997             : 
     998          11 :         return ctx->started ? SUCCESS : FAILURE;
     999             : }
    1000           8 : static long php_http_env_response_stream_get_status(php_http_env_response_t *r)
    1001             : {
    1002           8 :         php_http_env_response_stream_ctx_t *ctx = r->ctx;
    1003             : 
    1004           8 :         return ctx->status_code;
    1005             : }
    1006          13 : static STATUS php_http_env_response_stream_set_status(php_http_env_response_t *r, long http_code)
    1007             : {
    1008          13 :         php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
    1009             : 
    1010          13 :         if (stream_ctx->started || stream_ctx->finished) {
    1011           0 :                 return FAILURE;
    1012             :         }
    1013             : 
    1014          13 :         stream_ctx->status_code = http_code;
    1015             : 
    1016          13 :         return SUCCESS;
    1017             : }
    1018          10 : static STATUS php_http_env_response_stream_set_protocol_version(php_http_env_response_t *r, php_http_version_t *v)
    1019             : {
    1020          10 :         php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
    1021             : 
    1022          10 :         if (stream_ctx->started || stream_ctx->finished) {
    1023           0 :                 return FAILURE;
    1024             :         }
    1025             : 
    1026          10 :         memcpy(&stream_ctx->version, v, sizeof(stream_ctx->version));
    1027             : 
    1028          10 :         return SUCCESS;
    1029             : }
    1030          36 : static STATUS php_http_env_response_stream_set_header_ex(php_http_env_response_t *r, zend_bool replace, const char *fmt, va_list argv)
    1031             : {
    1032          36 :         php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
    1033          36 :         char *header_end, *header_str = NULL;
    1034          36 :         size_t header_len = 0;
    1035             :         zval *zheader, **zheader_ptr;
    1036             : 
    1037          36 :         if (stream_ctx->started || stream_ctx->finished) {
    1038           0 :                 return FAILURE;
    1039             :         }
    1040             : 
    1041          36 :         header_len = vspprintf(&header_str, 0, fmt, argv);
    1042             : 
    1043          36 :         if (!(header_end = strchr(header_str, ':'))) {
    1044           0 :                 efree(header_str);
    1045           0 :                 return FAILURE;
    1046             :         }
    1047             : 
    1048          36 :         *header_end = '\0';
    1049             : 
    1050          36 :         if (!replace && (SUCCESS == zend_hash_find(&stream_ctx->header, header_str, header_end - header_str + 1, (void *) &zheader_ptr))) {
    1051           2 :                 convert_to_array(*zheader_ptr);
    1052           2 :                 *header_end = ':';
    1053           2 :                 return add_next_index_stringl(*zheader_ptr, header_str, header_len, 0);
    1054             :         } else {
    1055          34 :                 MAKE_STD_ZVAL(zheader);
    1056          34 :                 ZVAL_STRINGL(zheader, header_str, header_len, 0);
    1057             : 
    1058          34 :                 if (SUCCESS != zend_hash_update(&stream_ctx->header, header_str, header_end - header_str + 1, (void *) &zheader, sizeof(zval *), NULL)) {
    1059           0 :                         zval_ptr_dtor(&zheader);
    1060           0 :                         return FAILURE;
    1061             :                 }
    1062             : 
    1063          34 :                 *header_end = ':';
    1064          34 :                 return SUCCESS;
    1065             :         }
    1066             : }
    1067          32 : static STATUS php_http_env_response_stream_set_header(php_http_env_response_t *r, const char *fmt, ...)
    1068             : {
    1069             :         STATUS ret;
    1070             :         va_list argv;
    1071             : 
    1072          32 :         va_start(argv, fmt);
    1073          32 :         ret = php_http_env_response_stream_set_header_ex(r, 1, fmt, argv);
    1074          32 :         va_end(argv);
    1075             : 
    1076          32 :         return ret;
    1077             : }
    1078           4 : static STATUS php_http_env_response_stream_add_header(php_http_env_response_t *r, const char *fmt, ...)
    1079             : {
    1080             :         STATUS ret;
    1081             :         va_list argv;
    1082             : 
    1083           4 :         va_start(argv, fmt);
    1084           4 :         ret = php_http_env_response_stream_set_header_ex(r, 0, fmt, argv);
    1085           4 :         va_end(argv);
    1086             : 
    1087           4 :         return ret;
    1088             : }
    1089           7 : static STATUS php_http_env_response_stream_del_header(php_http_env_response_t *r, const char *header_str, size_t header_len)
    1090             : {
    1091           7 :         php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
    1092             : 
    1093           7 :         if (stream_ctx->started || stream_ctx->finished) {
    1094           0 :                 return FAILURE;
    1095             :         }
    1096             : 
    1097           7 :         zend_hash_del(&stream_ctx->header, header_str, header_len + 1);
    1098           7 :         return SUCCESS;
    1099             : }
    1100        3284 : static STATUS php_http_env_response_stream_write(php_http_env_response_t *r, const char *data_str, size_t data_len)
    1101             : {
    1102        3284 :         php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
    1103             :         TSRMLS_FETCH_FROM_CTX(r->ts);
    1104             : 
    1105        3284 :         if (stream_ctx->finished) {
    1106           0 :                 return FAILURE;
    1107             :         }
    1108        3284 :         if (!stream_ctx->started) {
    1109           8 :                 if (SUCCESS != php_http_env_response_stream_start(stream_ctx TSRMLS_CC)) {
    1110           0 :                         return FAILURE;
    1111             :                 }
    1112             :         }
    1113             : 
    1114        3284 :         if (data_len != php_stream_write(stream_ctx->stream, data_str, data_len)) {
    1115           0 :                 return FAILURE;
    1116             :         }
    1117             : 
    1118        3284 :         return SUCCESS;
    1119             : }
    1120           0 : static STATUS php_http_env_response_stream_flush(php_http_env_response_t *r)
    1121             : {
    1122           0 :         php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
    1123             :         TSRMLS_FETCH_FROM_CTX(r->ts);
    1124             : 
    1125           0 :         if (stream_ctx->finished) {
    1126           0 :                 return FAILURE;
    1127             :         }
    1128           0 :         if (!stream_ctx->started) {
    1129           0 :                 if (SUCCESS != php_http_env_response_stream_start(stream_ctx TSRMLS_CC)) {
    1130           0 :                         return FAILURE;
    1131             :                 }
    1132             :         }
    1133             : 
    1134           0 :         return php_stream_flush(stream_ctx->stream);
    1135             : }
    1136          11 : static STATUS php_http_env_response_stream_finish(php_http_env_response_t *r)
    1137             : {
    1138          11 :         php_http_env_response_stream_ctx_t *ctx = r->ctx;
    1139             :         TSRMLS_FETCH_FROM_CTX(r->ts);
    1140             : 
    1141          11 :         if (ctx->finished) {
    1142           0 :                 return FAILURE;
    1143             :         }
    1144          11 :         if (!ctx->started) {
    1145           3 :                 if (SUCCESS != php_http_env_response_stream_start(ctx TSRMLS_CC)) {
    1146           0 :                         return FAILURE;
    1147             :                 }
    1148             :         }
    1149             : 
    1150          11 :         php_stream_flush(ctx->stream);
    1151          11 :         if (ctx->chunked && ctx->chunked_filter) {
    1152          10 :                 php_stream_filter_flush(ctx->chunked_filter, 1);
    1153          10 :                 ctx->chunked_filter = php_stream_filter_remove(ctx->chunked_filter, 1 TSRMLS_CC);
    1154             :         }
    1155             : 
    1156          11 :         ctx->finished = 1;
    1157             : 
    1158          11 :         return SUCCESS;
    1159             : }
    1160             : 
    1161             : static php_http_env_response_ops_t php_http_env_response_stream_ops = {
    1162             :         php_http_env_response_stream_init,
    1163             :         php_http_env_response_stream_dtor,
    1164             :         php_http_env_response_stream_get_status,
    1165             :         php_http_env_response_stream_set_status,
    1166             :         php_http_env_response_stream_set_protocol_version,
    1167             :         php_http_env_response_stream_set_header,
    1168             :         php_http_env_response_stream_add_header,
    1169             :         php_http_env_response_stream_del_header,
    1170             :         php_http_env_response_stream_write,
    1171             :         php_http_env_response_stream_flush,
    1172             :         php_http_env_response_stream_finish
    1173             : };
    1174             : 
    1175          11 : php_http_env_response_ops_t *php_http_env_response_get_stream_ops(void)
    1176             : {
    1177          11 :         return &php_http_env_response_stream_ops;
    1178             : }
    1179             : 
    1180             : #define PHP_HTTP_ENV_RESPONSE_OBJECT_INIT(obj) \
    1181             :         do { \
    1182             :                 if (!obj->message) { \
    1183             :                         obj->message = php_http_message_init_env(NULL, PHP_HTTP_RESPONSE TSRMLS_CC); \
    1184             :                 } \
    1185             :         } while (0)
    1186             : 
    1187             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse___construct, 0, 0, 0)
    1188             : ZEND_END_ARG_INFO();
    1189          24 : static PHP_METHOD(HttpEnvResponse, __construct)
    1190             : {
    1191             :         php_http_message_object_t *obj;
    1192             : 
    1193          24 :         php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
    1194             : 
    1195          24 :         obj = zend_object_store_get_object(getThis() TSRMLS_CC);
    1196             : 
    1197          24 :         php_http_expect(obj->message = php_http_message_init_env(obj->message, PHP_HTTP_RESPONSE TSRMLS_CC), unexpected_val, return);
    1198             : }
    1199             : 
    1200             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse___invoke, 0, 0, 1)
    1201             :         ZEND_ARG_INFO(0, ob_string)
    1202             :         ZEND_ARG_INFO(0, ob_flags)
    1203             : ZEND_END_ARG_INFO();
    1204           3 : static PHP_METHOD(HttpEnvResponse, __invoke)
    1205             : {
    1206             :         char *ob_str;
    1207             :         int ob_len;
    1208           3 :         long ob_flags = 0;
    1209             : 
    1210           3 :         if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &ob_str, &ob_len, &ob_flags)) {
    1211           3 :                 php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
    1212             : 
    1213           3 :                 PHP_HTTP_ENV_RESPONSE_OBJECT_INIT(obj);
    1214             : 
    1215           3 :                 php_http_message_object_init_body_object(obj);
    1216           3 :                 php_http_message_body_append(obj->message->body, ob_str, ob_len);
    1217             : #if PHP_VERSION_ID >= 50400
    1218           3 :                 RETURN_TRUE;
    1219             : #else
    1220             :                 RETURN_EMPTY_STRING();
    1221             : #endif
    1222             :         }
    1223             : }
    1224             : 
    1225             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setEnvRequest, 0, 0, 1)
    1226             :         ZEND_ARG_OBJ_INFO(0, env_request, http\\Message, 1)
    1227             : ZEND_END_ARG_INFO();
    1228           5 : static PHP_METHOD(HttpEnvResponse, setEnvRequest)
    1229             : {
    1230           5 :         zval *env_req = NULL;
    1231             : 
    1232          10 :         php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|O", &env_req, php_http_message_class_entry), invalid_arg, return);
    1233             : 
    1234           5 :         set_option(getThis(), ZEND_STRL("request"), IS_OBJECT, env_req, 0 TSRMLS_CC);
    1235           5 :         RETVAL_ZVAL(getThis(), 1, 0);
    1236             : }
    1237             : 
    1238             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setContentType, 0, 0, 1)
    1239             :         ZEND_ARG_INFO(0, content_type)
    1240             : ZEND_END_ARG_INFO();
    1241           3 : static PHP_METHOD(HttpEnvResponse, setContentType)
    1242             : {
    1243           3 :         char *ct_str = NULL;
    1244           3 :         int ct_len = 0;
    1245             : 
    1246           6 :         php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &ct_str, &ct_len), invalid_arg, return);
    1247             : 
    1248           3 :         set_option(getThis(), ZEND_STRL("contentType"), IS_STRING, ct_str, ct_len TSRMLS_CC);
    1249           3 :         RETVAL_ZVAL(getThis(), 1, 0);
    1250             : }
    1251             : 
    1252             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setContentDisposition, 0, 0, 1)
    1253             :         ZEND_ARG_ARRAY_INFO(0, disposition_params, 1)
    1254             : ZEND_END_ARG_INFO();
    1255           3 : static PHP_METHOD(HttpEnvResponse, setContentDisposition)
    1256             : {
    1257             :         zval *zdisposition;
    1258             : 
    1259           6 :         php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &zdisposition), invalid_arg, return);
    1260             : 
    1261           3 :         zend_update_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("contentDisposition"), zdisposition TSRMLS_CC);
    1262           3 :         RETVAL_ZVAL(getThis(), 1, 0);
    1263             : }
    1264             : 
    1265             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setContentEncoding, 0, 0, 1)
    1266             :         ZEND_ARG_INFO(0, content_encoding)
    1267             : ZEND_END_ARG_INFO();
    1268           3 : static PHP_METHOD(HttpEnvResponse, setContentEncoding)
    1269             : {
    1270             :         long ce;
    1271             : 
    1272           6 :         php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &ce), invalid_arg, return);
    1273             : 
    1274           3 :         set_option(getThis(), ZEND_STRL("contentEncoding"), IS_LONG, &ce, 0 TSRMLS_CC);
    1275           3 :         RETVAL_ZVAL(getThis(), 1, 0);
    1276             : }
    1277             : 
    1278             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setCacheControl, 0, 0, 1)
    1279             :         ZEND_ARG_INFO(0, cache_control)
    1280             : ZEND_END_ARG_INFO();
    1281           2 : static PHP_METHOD(HttpEnvResponse, setCacheControl)
    1282             : {
    1283           2 :         char *cc_str = NULL;
    1284           2 :         int cc_len = 0;
    1285             : 
    1286           4 :         php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &cc_str, &cc_len), invalid_arg, return);
    1287             : 
    1288           2 :         set_option(getThis(), ZEND_STRL("cacheControl"), IS_STRING, cc_str, cc_len TSRMLS_CC);
    1289           2 :         RETVAL_ZVAL(getThis(), 1, 0);
    1290             : }
    1291             : 
    1292             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setLastModified, 0, 0, 1)
    1293             :         ZEND_ARG_INFO(0, last_modified)
    1294             : ZEND_END_ARG_INFO();
    1295           4 : static PHP_METHOD(HttpEnvResponse, setLastModified)
    1296             : {
    1297             :         long last_modified;
    1298             : 
    1299           8 :         php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &last_modified), invalid_arg, return);
    1300             : 
    1301           4 :         set_option(getThis(), ZEND_STRL("lastModified"), IS_LONG, &last_modified, 0 TSRMLS_CC);
    1302           4 :         RETVAL_ZVAL(getThis(), 1, 0);
    1303             : }
    1304             : 
    1305             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_isCachedByLastModified, 0, 0, 0)
    1306             :         ZEND_ARG_INFO(0, header_name)
    1307             : ZEND_END_ARG_INFO();
    1308           2 : static PHP_METHOD(HttpEnvResponse, isCachedByLastModified)
    1309             : {
    1310           2 :         char *header_name_str = NULL;
    1311           2 :         int header_name_len = 0;
    1312             : 
    1313           2 :         if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &header_name_str, &header_name_len)) {
    1314           2 :                 if (!header_name_str || !header_name_len) {
    1315           0 :                         header_name_str = "If-Modified-Since";
    1316           0 :                         header_name_len = lenof("If-Modified-Since");
    1317             :                 }
    1318             : 
    1319           2 :                 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));
    1320             :         }
    1321             : }
    1322             : 
    1323             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setEtag, 0, 0, 1)
    1324             :         ZEND_ARG_INFO(0, etag)
    1325             : ZEND_END_ARG_INFO();
    1326           4 : static PHP_METHOD(HttpEnvResponse, setEtag)
    1327             : {
    1328           4 :         char *etag_str = NULL;
    1329           4 :         int etag_len = 0;
    1330             : 
    1331           8 :         php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &etag_str, &etag_len), invalid_arg, return);
    1332             : 
    1333           4 :         set_option(getThis(), ZEND_STRL("etag"), IS_STRING, etag_str, etag_len TSRMLS_CC);
    1334           4 :         RETVAL_ZVAL(getThis(), 1, 0);
    1335             : }
    1336             : 
    1337             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_isCachedByEtag, 0, 0, 0)
    1338             :         ZEND_ARG_INFO(0, header_name)
    1339             : ZEND_END_ARG_INFO();
    1340           2 : static PHP_METHOD(HttpEnvResponse, isCachedByEtag)
    1341             : {
    1342           2 :         char *header_name_str = NULL;
    1343           2 :         int header_name_len = 0;
    1344             : 
    1345           2 :         if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &header_name_str, &header_name_len)) {
    1346           2 :                 if (!header_name_str || !header_name_len) {
    1347           0 :                         header_name_str = "If-None-Match";
    1348           0 :                         header_name_len = lenof("If-None-Match");
    1349             :                 }
    1350           2 :                 RETURN_LONG(php_http_env_is_response_cached_by_etag(getThis(), header_name_str, header_name_len, get_request(getThis() TSRMLS_CC) TSRMLS_CC));
    1351             :         }
    1352             : }
    1353             : 
    1354             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setThrottleRate, 0, 0, 1)
    1355             :         ZEND_ARG_INFO(0, chunk_size)
    1356             :         ZEND_ARG_INFO(0, delay)
    1357             : ZEND_END_ARG_INFO();
    1358           1 : static PHP_METHOD(HttpEnvResponse, setThrottleRate)
    1359             : {
    1360             :         long chunk_size;
    1361           1 :         double delay = 1;
    1362             : 
    1363           2 :         php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|d", &chunk_size, &delay), invalid_arg, return);
    1364             : 
    1365           1 :         set_option(getThis(), ZEND_STRL("throttleDelay"), IS_DOUBLE, &delay, 0 TSRMLS_CC);
    1366           1 :         set_option(getThis(), ZEND_STRL("throttleChunk"), IS_LONG, &chunk_size, 0 TSRMLS_CC);
    1367           1 :         RETVAL_ZVAL(getThis(), 1, 0);
    1368             : }
    1369             : 
    1370             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setCookie, 0, 0, 1)
    1371             :         ZEND_ARG_INFO(0, cookie)
    1372             : ZEND_END_ARG_INFO();
    1373           3 : static PHP_METHOD(HttpEnvResponse, setCookie)
    1374             : {
    1375             :         zval *zcookie_new;
    1376             :         zend_error_handling zeh;
    1377           3 :         php_http_cookie_list_t *list = NULL;
    1378             : 
    1379           6 :         php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zcookie_new), invalid_arg, return);
    1380             : 
    1381           3 :         zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh TSRMLS_CC);
    1382           3 :         switch (Z_TYPE_P(zcookie_new)) {
    1383             :         case IS_OBJECT:
    1384           1 :                 if (instanceof_function(Z_OBJCE_P(zcookie_new), php_http_cookie_class_entry TSRMLS_CC)) {
    1385           1 :                         Z_ADDREF_P(zcookie_new);
    1386           1 :                         break;
    1387             :                 }
    1388             :                 /* no break */
    1389             :         case IS_ARRAY:
    1390           0 :                 list = php_http_cookie_list_from_struct(NULL, zcookie_new TSRMLS_CC);
    1391           0 :                 MAKE_STD_ZVAL(zcookie_new);
    1392           0 :                 ZVAL_OBJVAL(zcookie_new, php_http_cookie_object_new_ex(php_http_cookie_class_entry, list, NULL TSRMLS_CC), 0);
    1393           0 :                 break;
    1394             : 
    1395             :         default:
    1396           2 :                 zcookie_new = php_http_ztyp(IS_STRING, zcookie_new);
    1397           2 :                 list = php_http_cookie_list_parse(NULL, Z_STRVAL_P(zcookie_new), Z_STRLEN_P(zcookie_new), 0, NULL TSRMLS_CC);
    1398           2 :                 zval_ptr_dtor(&zcookie_new);
    1399           2 :                 MAKE_STD_ZVAL(zcookie_new);
    1400           2 :                 ZVAL_OBJVAL(zcookie_new, php_http_cookie_object_new_ex(php_http_cookie_class_entry, list, NULL TSRMLS_CC), 0);
    1401             :         }
    1402           3 :         zend_restore_error_handling(&zeh TSRMLS_CC);
    1403             : 
    1404           3 :         set_cookie(getThis(), zcookie_new TSRMLS_CC);
    1405           3 :         zval_ptr_dtor(&zcookie_new);
    1406             : 
    1407           3 :         RETVAL_ZVAL(getThis(), 1, 0);
    1408             : }
    1409             : 
    1410             : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_send, 0, 0, 0)
    1411             :         ZEND_ARG_INFO(0, stream)
    1412             : ZEND_END_ARG_INFO();
    1413          21 : static PHP_METHOD(HttpEnvResponse, send)
    1414             : {
    1415          21 :         zval *zstream = NULL;
    1416          21 :         php_stream *s = NULL;
    1417             : 
    1418          21 :         if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &zstream)) {
    1419             :                 /* first flush the output layer to avoid conflicting headers and output;
    1420             :                  * also, ob_start($thisEnvResponse) might have been called */
    1421             : #if PHP_VERSION_ID >= 50400
    1422          21 :                 php_output_end_all(TSRMLS_C);
    1423             : #else
    1424             :                 php_end_ob_buffers(1 TSRMLS_CC);
    1425             : #endif
    1426             : 
    1427          21 :                 if (zstream) {
    1428             :                         php_http_env_response_t *r;
    1429             : 
    1430          11 :                         php_stream_from_zval(s, &zstream);
    1431          11 :                         r = php_http_env_response_init(NULL, getThis(), php_http_env_response_get_stream_ops(), s TSRMLS_CC);
    1432          11 :                         if (!r) {
    1433           0 :                                 RETURN_FALSE;
    1434             :                         }
    1435             : 
    1436          11 :                         RETVAL_BOOL(SUCCESS == php_http_env_response_send(r));
    1437          11 :                         php_http_env_response_free(&r);
    1438             :                 } else {
    1439             :                         php_http_env_response_t r;
    1440             : 
    1441          10 :                         if (!php_http_env_response_init(&r, getThis(), NULL, NULL TSRMLS_CC)) {
    1442           0 :                                 RETURN_FALSE;
    1443             :                         }
    1444             : 
    1445          10 :                         RETVAL_BOOL(SUCCESS == php_http_env_response_send(&r));
    1446          10 :                         php_http_env_response_dtor(&r);
    1447             :                 }
    1448             :         }
    1449             : }
    1450             : 
    1451             : static zend_function_entry php_http_env_response_methods[] = {
    1452             :         PHP_ME(HttpEnvResponse, __construct,             ai_HttpEnvResponse___construct,             ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
    1453             :         PHP_ME(HttpEnvResponse, __invoke,                ai_HttpEnvResponse___invoke,                ZEND_ACC_PUBLIC)
    1454             :         PHP_ME(HttpEnvResponse, setEnvRequest,           ai_HttpEnvResponse_setEnvRequest,           ZEND_ACC_PUBLIC)
    1455             :         PHP_ME(HttpEnvResponse, setCookie,               ai_HttpEnvResponse_setCookie,               ZEND_ACC_PUBLIC)
    1456             :         PHP_ME(HttpEnvResponse, setContentType,          ai_HttpEnvResponse_setContentType,          ZEND_ACC_PUBLIC)
    1457             :         PHP_ME(HttpEnvResponse, setContentDisposition,   ai_HttpEnvResponse_setContentDisposition,   ZEND_ACC_PUBLIC)
    1458             :         PHP_ME(HttpEnvResponse, setContentEncoding,      ai_HttpEnvResponse_setContentEncoding,      ZEND_ACC_PUBLIC)
    1459             :         PHP_ME(HttpEnvResponse, setCacheControl,         ai_HttpEnvResponse_setCacheControl,         ZEND_ACC_PUBLIC)
    1460             :         PHP_ME(HttpEnvResponse, setLastModified,         ai_HttpEnvResponse_setLastModified,         ZEND_ACC_PUBLIC)
    1461             :         PHP_ME(HttpEnvResponse, isCachedByLastModified,  ai_HttpEnvResponse_isCachedByLastModified,  ZEND_ACC_PUBLIC)
    1462             :         PHP_ME(HttpEnvResponse, setEtag,                 ai_HttpEnvResponse_setEtag,                 ZEND_ACC_PUBLIC)
    1463             :         PHP_ME(HttpEnvResponse, isCachedByEtag,          ai_HttpEnvResponse_isCachedByEtag,          ZEND_ACC_PUBLIC)
    1464             :         PHP_ME(HttpEnvResponse, setThrottleRate,         ai_HttpEnvResponse_setThrottleRate,         ZEND_ACC_PUBLIC)
    1465             :         PHP_ME(HttpEnvResponse, send,                    ai_HttpEnvResponse_send,                    ZEND_ACC_PUBLIC)
    1466             :         EMPTY_FUNCTION_ENTRY
    1467             : };
    1468             : 
    1469             : zend_class_entry *php_http_env_response_class_entry;
    1470             : 
    1471         374 : PHP_MINIT_FUNCTION(http_env_response)
    1472             : {
    1473         374 :         zend_class_entry ce = {0};
    1474             : 
    1475         374 :         INIT_NS_CLASS_ENTRY(ce, "http\\Env", "Response", php_http_env_response_methods);
    1476         374 :         php_http_env_response_class_entry = zend_register_internal_class_ex(&ce, php_http_message_class_entry, NULL TSRMLS_CC);
    1477             : 
    1478         374 :         zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CONTENT_ENCODING_NONE"), PHP_HTTP_CONTENT_ENCODING_NONE TSRMLS_CC);
    1479         374 :         zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CONTENT_ENCODING_GZIP"), PHP_HTTP_CONTENT_ENCODING_GZIP TSRMLS_CC);
    1480             : 
    1481         374 :         zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_NO"), PHP_HTTP_CACHE_NO TSRMLS_CC);
    1482         374 :         zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_HIT"), PHP_HTTP_CACHE_HIT TSRMLS_CC);
    1483         374 :         zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_MISS"), PHP_HTTP_CACHE_MISS TSRMLS_CC);
    1484             : 
    1485         374 :         zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("request"), ZEND_ACC_PROTECTED TSRMLS_CC);
    1486         374 :         zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("cookies"), ZEND_ACC_PROTECTED TSRMLS_CC);
    1487         374 :         zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentType"), ZEND_ACC_PROTECTED TSRMLS_CC);
    1488         374 :         zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentDisposition"), ZEND_ACC_PROTECTED TSRMLS_CC);
    1489         374 :         zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentEncoding"), ZEND_ACC_PROTECTED TSRMLS_CC);
    1490         374 :         zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("cacheControl"), ZEND_ACC_PROTECTED TSRMLS_CC);
    1491         374 :         zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("etag"), ZEND_ACC_PROTECTED TSRMLS_CC);
    1492         374 :         zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("lastModified"), ZEND_ACC_PROTECTED TSRMLS_CC);
    1493         374 :         zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("throttleDelay"), ZEND_ACC_PROTECTED TSRMLS_CC);
    1494         374 :         zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("throttleChunk"), ZEND_ACC_PROTECTED TSRMLS_CC);
    1495             : 
    1496         374 :         return SUCCESS;
    1497             : }
    1498             : 
    1499             : 
    1500             : /*
    1501             :  * Local variables:
    1502             :  * tab-width: 4
    1503             :  * c-basic-offset: 4
    1504             :  * End:
    1505             :  * vim600: noet sw=4 ts=4 fdm=marker
    1506             :  * vim<600: noet sw=4 ts=4
    1507             :  */

Generated by: LCOV version 1.11