- if (range_status == RANGE_ERR) {
- zend_hash_destroy(&ranges);
- http_send_status(416);
- return FAILURE;
- }
-
- /* Range Request - only send ranges if entity hasn't changed */
- if ( range_status == RANGE_OK &&
- http_match_etag_ex("HTTP_IF_MATCH", HTTP_G(send).unquoted_etag, 0) &&
- http_match_last_modified_ex("HTTP_IF_UNMODIFIED_SINCE", HTTP_G(send).last_modified, 0)) {
- STATUS result = http_send_ranges(&ranges, data_ptr, data_size, data_mode);
- zend_hash_destroy(&ranges);
- return result;
- }
-
- zend_hash_destroy(&ranges);
-
- /* send 304 Not Modified if etag matches - DON'T return on ETag generation failure */
- if (cache_etag) {
- char *etag = NULL;
-
- if (!(etag = http_etag(data_ptr, data_size, data_mode))) {
- http_error(E_NOTICE, HTTP_E_PARAM, "Failed to generate ETag for data source");
- } else {
- http_send_etag(etag, 32);
- if (http_match_etag("HTTP_IF_NONE_MATCH", etag)) {
- return http_cache_exit_ex(etag, 1, 1);
+ switch (range_status) {
+ case RANGE_ERR:
+ {
+ zend_hash_destroy(&ranges);
+ http_send_status(416);
+ return FAILURE;
+ }
+ case RANGE_OK:
+ {
+ /* Range Request - only send ranges if entity hasn't changed */
+ if ( http_got_server_var("HTTP_IF_RANGE") &&
+ !http_match_etag("HTTP_IF_RANGE", HTTP_G->send.unquoted_etag) &&
+ !http_match_last_modified("HTTP_IF_RANGE", HTTP_G->send.last_modified)) {
+ /* fallthrough to send full entity with 200 Ok */
+ no_cache = 1;
+ } else if ( !http_match_etag_ex("HTTP_IF_MATCH", HTTP_G->send.unquoted_etag, 0) ||
+ !http_match_last_modified_ex("HTTP_IF_UNMODIFIED_SINCE", HTTP_G->send.last_modified, 0) ||
+ !http_match_last_modified_ex("HTTP_UNLESS_MODIFIED_SINCE", HTTP_G->send.last_modified, 0)) {
+ /* 412 Precondition failed */
+ zend_hash_destroy(&ranges);
+ http_send_status(412);
+ return FAILURE;
+ } else 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);
+ http_send_status(500);
+ return FAILURE;
+ } else {
+ phpstr header;
+
+ phpstr_init(&header);
+ phpstr_appendf(&header, "Content-Range: bytes %ld-%ld/%zu", Z_LVAL_PP(begin), Z_LVAL_PP(end), data_size);
+ phpstr_fix(&header);
+ http_send_status_header_ex(206, PHPSTR_VAL(&header), PHPSTR_LEN(&header), 1);
+ phpstr_dtor(&header);
+ http_send_response_start(&s, Z_LVAL_PP(end)-Z_LVAL_PP(begin)+1);
+ http_send_response_data_fetch(&s, data_ptr, data_size, data_mode, Z_LVAL_PP(begin), Z_LVAL_PP(end) + 1);
+ http_send_response_finish(&s);
+ zend_hash_destroy(&ranges);
+ return SUCCESS;
+ }
+ } else {
+ /* multi range */
+ HashPosition pos;
+ zval **range, **begin, **end;
+ const char *content_type = HTTP_G->send.content_type;
+ char boundary_str[32];
+ size_t boundary_len;
+ phpstr header, preface;
+
+ boundary_len = http_boundary(boundary_str, sizeof(boundary_str));
+ phpstr_init(&header);
+ phpstr_appendf(&header, "Content-Type: multipart/byteranges; boundary=%s", boundary_str);
+ phpstr_fix(&header);
+ http_send_status_header_ex(206, PHPSTR_VAL(&header), PHPSTR_LEN(&header), 1);
+ phpstr_dtor(&header);
+ http_send_response_start(&s, 0);
+
+ if (!content_type) {
+ content_type = "application/x-octetstream";
+ }
+
+ phpstr_init(&preface);
+ FOREACH_HASH_VAL(pos, &ranges, range) {
+ if ( SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(range), 0, (void *) &begin) &&
+ SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(range), 1, (void *) &end)) {
+
+#define HTTP_RANGE_PREFACE \
+ HTTP_CRLF "--%s" \
+ HTTP_CRLF "Content-Type: %s" \
+ HTTP_CRLF "Content-Range: bytes %ld-%ld/%zu" \
+ HTTP_CRLF HTTP_CRLF
+
+ phpstr_appendf(&preface, HTTP_RANGE_PREFACE, boundary_str, content_type, Z_LVAL_PP(begin), Z_LVAL_PP(end), data_size);
+ phpstr_fix(&preface);
+ http_send_response_data_plain(&s, PHPSTR_VAL(&preface), PHPSTR_LEN(&preface));
+ phpstr_reset(&preface);
+ http_send_response_data_fetch(&s, data_ptr, data_size, data_mode, Z_LVAL_PP(begin), Z_LVAL_PP(end) + 1);
+ }
+ }
+ phpstr_dtor(&preface);
+
+ http_send_response_data_plain(&s, HTTP_CRLF "--", lenof(HTTP_CRLF "--"));
+ http_send_response_data_plain(&s, boundary_str, boundary_len);
+ http_send_response_data_plain(&s, "--", lenof("--"));
+
+ http_send_response_finish(&s);
+ zend_hash_destroy(&ranges);
+ return SUCCESS;