You are here

function _cdn_transfer_file in CDN 7.2

Same name and namespace in other branches
  1. 6.2 cdn.basic.farfuture.inc \_cdn_transfer_file()

Variant of Drupal's file_transfer(), based on http://www.thomthom.net/blog/2007/09/php-resumable-download-server/ to support ranged requests as well.

Note: ranged requests that request multiple ranges are not supported. They are responded to with a 416. See http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html

1 call to _cdn_transfer_file()
cdn_basic_farfuture_download in ./cdn.basic.farfuture.inc

File

./cdn.basic.farfuture.inc, line 341
Far Future expiration setting for basic mode.

Code

function _cdn_transfer_file($path) {
  $fp = @fopen($path, 'rb');
  $size = filesize($path);

  // File size
  $length = $size;

  // Content length
  $start = 0;

  // Start byte
  $end = $size - 1;

  // End byte
  // In case of a range request, seek within the file to the correct location.
  if (isset($_SERVER['HTTP_RANGE'])) {
    $c_start = $start;
    $c_end = $end;

    // Extract the string containing the requested range.
    list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);

    // If the client requested multiple ranges, repond with a 416.
    if (strpos($range, ',') !== FALSE) {
      header('HTTP/1.1 416 Requested Range Not Satisfiable');
      header("Content-Range: bytes {$start}-{$end}/{$size}");
      exit;
    }

    // Case "Range: -n": final n bytes are requested.
    if ($range[0] == '-') {
      $c_start = $size - substr($range, 1);
    }
    else {
      $range = explode('-', $range);
      $c_start = intval($range[0]);
      $c_end = isset($range[1]) && is_numeric($range[1]) ? intval($range[1]) : $size;
    }

    // Minor normalization: end bytes can not be larger than $end.
    $c_end = $c_end > $end ? $end : $c_end;

    // If the requested range is not valid, respond with a 416.
    if ($c_start > $c_end || $c_start > $end || $c_end > $end) {
      header('HTTP/1.1 416 Requested Range Not Satisfiable');
      header("Content-Range: bytes {$start}-{$end}/{$size}");
      exit;
    }
    $start = $c_start;
    $end = $c_end;
    $length = $end - $start + 1;
    fseek($fp, $start);

    // The ranged request is valid and will be performed, respond with a 206.
    header('HTTP/1.1 206 Partial Content');
    header("Content-Range: bytes {$start}-{$end}/{$size}");
  }
  header("Content-Length: {$length}");

  // Start buffered download. Prevent reading too far for a ranged request.
  $buffer = 1024 * 8;
  while (!feof($fp) && ($p = ftell($fp)) <= $end) {
    if ($p + $buffer > $end) {
      $buffer = $end - $p + 1;
    }
    set_time_limit(0);

    // Reset time limit for big files.
    echo fread($fp, $buffer);
    flush();

    // Free up memory, to prevent triggering PHP's memory limit.
  }
  fclose($fp);
}