-PHP_HTTP_API void php_http_env_set_response_throttle_rate(zval *container, size_t chunk_size, double delay TSRMLS_CC)
-{
- if (Z_TYPE_P(container) == IS_OBJECT) {
- zend_update_property_double(Z_OBJCE_P(container), container, ZEND_STRL("throttleDelay"), delay TSRMLS_CC);
- zend_update_property_long(Z_OBJCE_P(container), container, ZEND_STRL("throttleChunk"), chunk_size TSRMLS_CC);
- } else {
- convert_to_array(container);
- add_assoc_double_ex(container, ZEND_STRS("throttleDelay"), delay);
- add_assoc_long_ex(container, ZEND_STRS("throttleChunk"), chunk_size);
- }
-}
-
-static void set_container_value(zval *container, const char *name_str, size_t name_len, int type, const void *value_ptr, size_t value_len TSRMLS_DC)
-{
- if (Z_TYPE_P(container) == IS_OBJECT) {
- /* stupid non-const api */
- char *name = estrndup(name_str, name_len);
- switch (type) {
- case IS_LONG:
- zend_update_property_long(Z_OBJCE_P(container), container, name, name_len, *(long *)value_ptr TSRMLS_CC);
- break;
- case IS_STRING:
- zend_update_property_stringl(Z_OBJCE_P(container), container, name, name_len, value_ptr, value_len TSRMLS_CC);
- break;
- }
- efree(name);
- } else {
- convert_to_array(container);
- switch (type) {
- case IS_LONG:
- add_assoc_long_ex(container, name_str, name_len + 1, *(long *)value_ptr);
- break;
- case IS_STRING: {
- char *value = estrndup(value_ptr, value_len);
- add_assoc_stringl_ex(container, name_str, name_len + 1, value, value_len, 0);
- break;
- }
- }
- }
-}
-
-PHP_HTTP_API STATUS php_http_env_set_response_last_modified(zval *container, time_t t, char **sent_header TSRMLS_DC)
-{
- STATUS ret;
- char *lm_header_str, *date;
- size_t lm_header_len;
-
- if (t) {
- if (!(date = php_format_date(ZEND_STRL(PHP_HTTP_DATE_FORMAT), t, 0 TSRMLS_CC))) {
- return FAILURE;
- }
-
- lm_header_len = spprintf(&lm_header_str, 0, "Last-Modified: %s", date);
- STR_FREE(date);
- } else {
- lm_header_str = "Last-Modified:";
- lm_header_len = lenof("Last-Modified:");
- }
-
- if (SUCCESS == (ret = php_http_env_set_response_header(0, lm_header_str, lm_header_len, 1 TSRMLS_CC))) {
- set_container_value(container, ZEND_STRL("lastModified"), IS_LONG, &t, 0 TSRMLS_CC);
- }
-
- if (sent_header) {
- *sent_header = lm_header_str;
- } else if (t) {
- STR_FREE(lm_header_str);
- }
-
- return ret;
-}
-
-PHP_HTTP_API STATUS php_http_env_set_response_etag(zval *container, const char *etag_str, size_t etag_len, char **sent_header TSRMLS_DC)
-{
- STATUS ret;
- char *etag = NULL, *etag_header_str;
- size_t etag_header_len;
-
- if (etag_len){
- etag_header_len = spprintf(&etag_header_str, 0, "ETag: \"%s\"", etag_str);
- } else {
- etag_header_str = "ETag:";
- etag_header_len = lenof("ETag:");
- }
-
- if (SUCCESS == (ret = php_http_env_set_response_header(0, etag_header_str, etag_header_len, 1 TSRMLS_CC))) {
- set_container_value(container, ZEND_STRL(etag), IS_STRING, etag_str, etag_len TSRMLS_CC);
- }
-
- if (sent_header) {
- *sent_header = etag_header_str;
- } else if (etag_len) {
- STR_FREE(etag_header_str);
- }
-
- return ret;
-}
-
-PHP_HTTP_API STATUS php_http_env_set_response_content_type(zval *container, const char *ct_str, size_t ct_len, char **sent_header TSRMLS_DC)
-{
- STATUS ret;
- char *ct_header_str;
- size_t ct_header_len;
-
- if (ct_len) {
- PHP_HTTP_CHECK_CONTENT_TYPE(ct_str, return FAILURE);
- ct_header_len = spprintf(&ct_header_str, 0, "Content-Type: %s", ct_str);
- } else {
- ct_header_str = "Content-Type:";
- ct_header_len = lenof("Content-Type:");
- }
-
- if (SUCCESS == (ret = php_http_env_set_response_header(0, ct_header_str, ct_header_len, 1 TSRMLS_CC))) {
- set_container_value(container, ZEND_STRL("contentType"), IS_STRING, ct_str, ct_len TSRMLS_CC);
- }
-
- if (sent_header) {
- *sent_header = ct_header_str;
- } else if (ct_len) {
- STR_FREE(ct_header_str);
- }
-
- return ret;
-}
-
-PHP_HTTP_API STATUS php_http_env_set_response_content_disposition(zval *container, php_http_content_disposition_t d, const char *f_str, size_t f_len, char **sent_header TSRMLS_DC)
-{
- STATUS ret;
- char *tmp, *cd_header_str, *new_f_str;
- int new_f_len;
- size_t cd_header_len;
-
- switch (d) {
- case PHP_HTTP_CONTENT_DISPOSITION_NONE:
- break;
- case PHP_HTTP_CONTENT_DISPOSITION_INLINE:
- tmp = "inline";
- break;
- case PHP_HTTP_CONTENT_DISPOSITION_ATTACHMENT:
- tmp = "attachment";
- break;
- default:
- php_http_error(HE_WARNING, PHP_HTTP_E_INVALID_PARAM, "Unknown content disposition (%d)", (int) d);
- return FAILURE;
- }
-
- if (f_len) {
- new_f_str = php_addslashes(estrndup(f_str, f_len), f_len, &new_f_len, 0 TSRMLS_CC);
- cd_header_len = spprintf(&cd_header_str, 0, "Content-Disposition: %s; filename=\"%.*s\"", tmp, new_f_len, new_f_str);
- STR_FREE(new_f_str);
- } else if (d) {
- cd_header_len = spprintf(&cd_header_str, 0, "Content-Disposition: %s", tmp);
- } else {
- cd_header_str = "Content-Disposition:";
- cd_header_len = lenof("Content-Disposition:");
- }
-
- ret = php_http_env_set_response_header(0, cd_header_str, cd_header_len, 1 TSRMLS_CC);
-
- if (sent_header) {
- *sent_header = cd_header_str;
- } else if (f_len || d){
- STR_FREE(cd_header_str);
- }
-
- return ret;
-}
-
-PHP_HTTP_API STATUS php_http_env_set_response_cache_control(zval *container, const char *cc_str, size_t cc_len, char **sent_header TSRMLS_DC)
-{
- STATUS ret;
- char *cc_header_str;
- size_t cc_header_len;
-
- if (cc_len) {
- cc_header_len = spprintf(&cc_header_str, 0, "Cache-Control: %s", cc_str);
- } else {
- cc_header_str = "Content-Disposition:";
- cc_header_len = lenof("Content-Disposition:");
- }
-
- ret = php_http_env_set_response_header(0, cc_header_str, cc_header_len, 1 TSRMLS_CC);
-
- if (sent_header) {
- *sent_header = cc_header_str;
- } else if (cc_len) {
- STR_FREE(cc_header_str);
- }
-
- return ret;
-}
-
-static zval *get_container_value(zval *container, const char *name_str, size_t name_len TSRMLS_CC)
-{
- zval *val, **valptr;
-
- if (Z_TYPE_P(container) == IS_OBJECT) {
- char *name = estrndup(name_str, name_len);
- val = zend_read_property(Z_OBJCE_P(container), container, name, name_len, 0 TSRMLS_CC);
- efree(name);
- } else {
- if (SUCCESS == zend_hash_find(Z_ARRVAL_P(container), name_str, name_len + 1, (void *) &valptr)) {
- val = *valptr;
- } else {
- val = NULL;
- }
- }
- if (val) {
- Z_ADDREF_P(val);
- }
- return val;
-}
-
-PHP_HTTP_API php_http_cache_status_t php_http_env_is_response_cached_by_etag(zval *container, const char *header_str, size_t header_len TSRMLS_DC)
-{
- int ret, free_etag = 0;
- char *header, *etag;
- zval *zetag, *zbody = NULL;
-
- if ( !(header = php_http_env_get_request_header(header_str, header_len TSRMLS_CC))
- || !(zbody = get_container_value(container, ZEND_STRL("body") TSRMLS_CC))
- || !(Z_TYPE_P(zbody) == IS_OBJECT)
- || !instanceof_function(Z_OBJCE_P(zbody), php_http_message_body_class_entry TSRMLS_CC)
- ) {
- STR_FREE(header);
- if (zbody) {
- zval_ptr_dtor(&zbody);
- }
- return PHP_HTTP_CACHE_NO;
- }
-
- if ((zetag = get_container_value(container, ZEND_STRL("etag") TSRMLS_CC))) {
- zval *zetag_copy = php_http_zsep(IS_STRING, zetag);
- zval_ptr_dtor(&zetag);
- zetag = zetag_copy;
- }
-
- if (zetag && Z_STRLEN_P(zetag)) {
- etag = Z_STRVAL_P(zetag);
- } else {
- etag = php_http_message_body_etag(((php_http_message_body_object_t *) zend_object_store_get_object(zbody TSRMLS_CC))->body);
- php_http_env_set_response_etag(container, etag, strlen(etag), NULL TSRMLS_CC);
- free_etag = 1;
- }
-
- if (zetag) {
- zval_ptr_dtor(&zetag);
- }
-
- ret = php_http_match(header, etag, PHP_HTTP_MATCH_WORD);
-
- if (free_etag) {
- efree(etag);
- }
- efree(header);
-
- return ret ? PHP_HTTP_CACHE_HIT : PHP_HTTP_CACHE_MISS;
-}
-
-PHP_HTTP_API php_http_cache_status_t php_http_env_is_response_cached_by_last_modified(zval *container, const char *header_str, size_t header_len TSRMLS_DC)
-{
- char *header;
- time_t ums, lm = 0;
- zval *zbody = NULL, *zlm;
-
- if ( !(header = php_http_env_get_request_header(header_str, header_len TSRMLS_CC))
- || !(zbody = get_container_value(container, ZEND_STRL("body") TSRMLS_CC))
- || !(Z_TYPE_P(zbody) == IS_OBJECT)
- || !instanceof_function(Z_OBJCE_P(zbody), php_http_message_body_class_entry TSRMLS_CC)
- ) {
- STR_FREE(header);
- if (zbody) {
- zval_ptr_dtor(&zbody);
- }
- return PHP_HTTP_CACHE_NO;
- }
-
- if ((zlm = get_container_value(container, ZEND_STRL("lastModified") TSRMLS_CC))) {
- zval *zlm_copy = php_http_zsep(IS_LONG, zlm);
- zval_ptr_dtor(&zlm);
- zlm = zlm_copy;
- }
-
- if (zlm && Z_LVAL_P(zlm) > 0) {
- lm = Z_LVAL_P(zlm);
- } else {
- lm = php_http_message_body_mtime(((php_http_message_body_object_t *) zend_object_store_get_object(zbody TSRMLS_CC))->body);
- php_http_env_set_response_last_modified(container, lm, NULL TSRMLS_CC);
- }
-
- if (zlm) {
- zval_ptr_dtor(&zlm);
- }
-
- ums = php_parse_date(header, NULL TSRMLS_CC);
- efree(header);
-
- if (ums > 0 && ums <= lm) {
- return PHP_HTTP_CACHE_HIT;
- } else {
- return PHP_HTTP_CACHE_MISS;
- }
-}
-
-PHP_HTTP_API void php_http_env_set_response_body(zval *container, php_http_message_body_t *body)
-{
- TSRMLS_FETCH_FROM_CTX(body->ts);
- zend_object_value ov = php_http_message_body_object_new_ex(php_http_message_body_class_entry, php_http_message_body_copy(body, NULL, 0), NULL TSRMLS_CC);
-
- set_container_value(container, ZEND_STRL("body"), IS_OBJECT, &ov, 0 TSRMLS_CC);
-}
-
-struct output_ctx {
- php_http_buffer_t *buf;
- zval *container;
-};
-
-static size_t output(void *context, const char *buf, size_t len TSRMLS_DC)
-{
- struct output_ctx *ctx = context;
-
- if (ctx->buf) {
- zval *zcs;
- size_t chunk_size = PHP_HTTP_SENDBUF_SIZE;
-
- if ((zcs = get_container_value(ctx->container, ZEND_STRL("throttleChunk") TSRMLS_CC))) {
- zval *zcs_copy = php_http_zsep(IS_LONG, zcs);
-
- zval_ptr_dtor(&zcs);
- chunk_size = Z_LVAL_P(zcs_copy);
- zval_ptr_dtor(&zcs_copy);
- }
- php_http_buffer_chunked_output(&ctx->buf, buf, len, buf ? chunk_size : 0, output, NULL TSRMLS_CC);
- } else {
- zval *ztd;
-
-
- PHPWRITE(buf, len);
-
- /* we really only need to flush when throttling is enabled,
- because we push the data as fast as possible anyway if not */
- if ((ztd = get_container_value(ctx->container, ZEND_STRL("throttleDelay") TSRMLS_CC))) {
- double delay;
- zval *ztd_copy = php_http_zsep(IS_DOUBLE, ztd);
-
- zval_ptr_dtor(&ztd);
- delay = Z_DVAL_P(ztd_copy);
- zval_ptr_dtor(&ztd_copy);
-
- if (delay >= PHP_HTTP_DIFFSEC) {
- if (php_output_get_level(TSRMLS_C)) {
- php_output_flush_all(TSRMLS_C);
- }
- if (!(php_output_get_status(TSRMLS_C) & PHP_OUTPUT_IMPLICITFLUSH)) {
- sapi_flush(TSRMLS_C);
- }
- php_http_sleep(delay);
- }
- }
- }
- return len;
-}
-
-PHP_HTTP_API STATUS php_http_env_send_response(zval *container TSRMLS_DC)
-{
- struct output_ctx ctx = {NULL, container};
- zval *zbody, *zheader, *zrcode, *zversion;
- HashTable ranges;
- php_http_range_status_t range_status;
- php_http_message_body_t *body;
- size_t body_size;
-
- if ( !(zbody = get_container_value(container, ZEND_STRL("body") TSRMLS_CC))
- || !(Z_TYPE_P(zbody) == IS_OBJECT)
- || !instanceof_function(Z_OBJCE_P(zbody), php_http_message_body_class_entry TSRMLS_CC)
- ) {
- if (zbody) {
- zval_ptr_dtor(&zbody);
- }
- return FAILURE;
- }
-
- if ((zrcode = get_container_value(container, ZEND_STRL("responseCode") TSRMLS_CC))) {
- zval *zrcode_copy = php_http_zsep(IS_LONG, zrcode);
-
- zval_ptr_dtor(&zrcode);
- if (Z_LVAL_P(zrcode_copy) > 0) {
- php_http_env_set_response_code(Z_LVAL_P(zrcode_copy) TSRMLS_CC);
- }
- zval_ptr_dtor(&zrcode_copy);
- }
-
- if ((zversion = get_container_value(container, ZEND_STRL("httpVersion") TSRMLS_CC))) {
- php_http_version_t v;
- zval *zversion_copy = php_http_zsep(IS_STRING, zversion);
-
- zval_ptr_dtor(&zversion);
- if (Z_STRLEN_P(zversion_copy) && php_http_version_parse(&v, Z_STRVAL_P(zversion_copy) TSRMLS_CC)) {
- php_http_env_set_response_protocol_version(&v TSRMLS_CC);
- php_http_version_dtor(&v);
- }
- zval_ptr_dtor(&zversion_copy);
- }
-
- if ((zheader = get_container_value(container, ZEND_STRL("headers") TSRMLS_CC))) {
- if (Z_TYPE_P(zheader) == IS_ARRAY) {
- zval **val;
- HashPosition pos;
- php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
-
- FOREACH_KEYVAL(pos, zheader, key, val) {
- if (key.type == HASH_KEY_IS_STRING) {
- php_http_env_set_response_header_value(0, key.str, key.len - 1, *val, 1 TSRMLS_CC);
- }
- }
- }
- zval_ptr_dtor(&zheader);
- }
-
- body = ((php_http_message_body_object_t *) zend_object_store_get_object(zbody TSRMLS_CC))->body;
- body_size = php_http_message_body_size(body);
- php_http_env_set_response_header(0, ZEND_STRL("Accept-Ranges: bytes"), 1 TSRMLS_CC);
- zend_hash_init(&ranges, 0, NULL, ZVAL_PTR_DTOR, 0);
- range_status = php_http_env_get_request_ranges(&ranges, body_size TSRMLS_CC);
-
- switch (range_status) {
- case PHP_HTTP_RANGE_ERR:
- zend_hash_destroy(&ranges);
- if (!php_http_env_got_request_header(ZEND_STRL("If-Range") TSRMLS_CC)) {
- char *cr_header_str;
- size_t cr_header_len;
-
- cr_header_len = spprintf(&cr_header_str, 0, "Content-Range: bytes */%zu", body_size);
- php_http_env_set_response_header(416, cr_header_str, cr_header_len, 1 TSRMLS_CC);
- efree(cr_header_str);
- if (zbody) {
- zval_ptr_dtor(&zbody);
- }
- return SUCCESS;
- }
- break;
-
- case PHP_HTTP_RANGE_NO:
- /* send full entity */
- zend_hash_destroy(&ranges);
- break;
-
- case PHP_HTTP_RANGE_OK:
- /* send content-range response */
- if (PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_etag(container, ZEND_STRL("If-Range") TSRMLS_CC)
- || PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(container, ZEND_STRL("If-Range") TSRMLS_CC)
- ) {
- /* send full entity */
- zend_hash_destroy(&ranges);
- break;
- }
- if (PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_etag(container, ZEND_STRL("If-Match") TSRMLS_CC)
- || PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(container, ZEND_STRL("If-Unmodified-Since") TSRMLS_CC)
- || PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(container, ZEND_STRL("Unless-Modified-Since") TSRMLS_CC)
- ) {
- zend_hash_destroy(&ranges);
- php_http_env_set_response_code(412 TSRMLS_CC);
- if (zbody) {
- zval_ptr_dtor(&zbody);
- }
- return SUCCESS;
- }
- if (zend_hash_num_elements(&ranges) == 1) {
- /* single range */
- zval **range, **begin, **end;
-
- if (SUCCESS != zend_hash_index_find(&ranges, 0, (void *) &range)
- || SUCCESS != zend_hash_index_find(Z_ARRVAL_PP(range), 0, (void *) &begin)
- || SUCCESS != zend_hash_index_find(Z_ARRVAL_PP(range), 1, (void *) &end)
- ) {
- /* this should never happen */
- zend_hash_destroy(&ranges);
- php_http_env_set_response_code(500 TSRMLS_CC);
- if (zbody) {
- zval_ptr_dtor(&zbody);
- }
- return FAILURE;
- } else {
- char *cr_header_str;
- size_t cr_header_len;
-
- cr_header_len = spprintf(&cr_header_str, 0, "Content-Range: bytes %ld-%ld/%zu", Z_LVAL_PP(begin), Z_LVAL_PP(end), body_size);
- php_http_env_set_response_header(206, cr_header_str, cr_header_len, 1 TSRMLS_CC);
- efree(cr_header_str);
-
- /* send chunk */
- php_http_message_body_to_callback(body, output, &ctx, Z_LVAL_PP(begin), Z_LVAL_PP(end) - Z_LVAL_PP(begin) + 1);
- output(&ctx, NULL, 0 TSRMLS_CC);
- if (zbody) {
- zval_ptr_dtor(&zbody);
- }
- return SUCCESS;
- }
- } else {
- /* send multipart/byte-ranges message */
- HashPosition pos;
- zval **chunk, *zct;
- php_http_buffer_t preface;
- int free_ct = 0;
- char *content_type = "application/octet-stream";
- char boundary[32], *ct_header_str = "Content-Type: multipart/byteranges; boundary= ";
-
- if ((zct = get_container_value(container, ZEND_STRL("contentType") TSRMLS_CC))) {
- zval *zct_copy = php_http_zsep(IS_STRING, zct);
-
- zval_ptr_dtor(&zct);
- if (Z_STRLEN_P(zct_copy)) {
- content_type = estrndup(Z_STRVAL_P(zct_copy), Z_STRLEN_P(zct_copy));
- free_ct = 1;
- }
-
- zval_ptr_dtor(&zct);
- }
-
- php_http_boundary(boundary, sizeof(boundary));
- strlcpy(&ct_header_str[45], boundary, 32);
-
- php_http_env_set_response_header(206, ct_header_str, strlen(ct_header_str), 1 TSRMLS_CC);
-
- php_http_buffer_init(&preface);
- FOREACH_HASH_VAL(pos, &ranges, chunk) {
- zval **begin, **end;
-
- if (IS_ARRAY == Z_TYPE_PP(chunk)
- && SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(chunk), 0, (void *) &begin)
- && IS_LONG == Z_TYPE_PP(begin)
- && SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(chunk), 1, (void *) &end)
- && IS_LONG == Z_TYPE_PP(end)
- ) {
- php_http_buffer_appendf(&preface,
- PHP_HTTP_CRLF
- "--%s" PHP_HTTP_CRLF
- "Content-Type: %s" PHP_HTTP_CRLF
- "Content-Range: bytes %ld-%ld/%zu" PHP_HTTP_CRLF,
- /* - */
- boundary,
- content_type,
- Z_LVAL_PP(begin),
- Z_LVAL_PP(end),
- body_size
- );
- php_http_buffer_fix(&preface);
- output(&ctx, PHP_HTTP_BUFFER_VAL(&preface), PHP_HTTP_BUFFER_LEN(&preface) TSRMLS_CC);
- php_http_buffer_reset(&preface);
-
- php_http_message_body_to_callback(body, output, &ctx, Z_LVAL_PP(begin), Z_LVAL_PP(end) - Z_LVAL_PP(begin) + 1);
- }
- }
- php_http_buffer_appendf(&preface, PHP_HTTP_CRLF "--%s--", boundary);
- php_http_buffer_fix(&preface);
- output(&ctx, PHP_HTTP_BUFFER_VAL(&preface), PHP_HTTP_BUFFER_LEN(&preface) TSRMLS_CC);
- php_http_buffer_dtor(&preface);
- output(&ctx, NULL, 0 TSRMLS_CC);
- if (zbody) {
- zval_ptr_dtor(&zbody);
- }
- return SUCCESS;
- }
- break;
- }
-
- switch (php_http_env_is_response_cached_by_etag(container, ZEND_STRL("If-None-Match"))) {
- case PHP_HTTP_CACHE_MISS:
- break;
-
- case PHP_HTTP_CACHE_NO:
- if (PHP_HTTP_CACHE_HIT != php_http_env_is_response_cached_by_last_modified(container, ZEND_STRL("If-Modified-Since"))) {
- break;
- }
-
- case PHP_HTTP_CACHE_HIT:
- php_http_env_set_response_code(304 TSRMLS_CC);
- if (zbody) {
- zval_ptr_dtor(&zbody);
- }
- return SUCCESS;
- }
-
- php_http_message_body_to_callback(body, output, &ctx, 0, 0);
- output(&ctx, NULL, 0 TSRMLS_CC);
-
- if (zbody) {
- zval_ptr_dtor(&zbody);
- }
- return SUCCESS;
-}
-