- 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 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;
- }