diff options
author | Andreas Gohr <andi@splitbrain.org> | 2009-03-13 20:02:47 +0100 |
---|---|---|
committer | Andreas Gohr <andi@splitbrain.org> | 2009-03-13 20:02:47 +0100 |
commit | 758447cfa419f1a11bc022ef8168447992364e52 (patch) | |
tree | 7f10e61b2a085ac08a786bb1d5fa91cd2f4d09b3 /inc | |
parent | af03da1a940f1ada715dcb574ff12a3e23465d99 (diff) | |
download | rpg-758447cfa419f1a11bc022ef8168447992364e52.tar.gz rpg-758447cfa419f1a11bc022ef8168447992364e52.tar.bz2 |
Support for multirange requests for media FS#1630
Ignore-this: 50de569608231b910a62327d2f3af1de
This patch moves all HTTP sending related functions to inc/httputils.php
Handling of range requests was rewritten completely to support mutirange
requests. This should fix problems with Adobe Reader but needs testing.
darcs-hash:20090313190247-7ad00-e6ec1f81acb9f7ac651357dd034c2689aea6868d.gz
Diffstat (limited to 'inc')
-rw-r--r-- | inc/httputils.php | 198 | ||||
-rw-r--r-- | inc/pageutils.php | 92 |
2 files changed, 198 insertions, 92 deletions
diff --git a/inc/httputils.php b/inc/httputils.php new file mode 100644 index 000000000..271b8272f --- /dev/null +++ b/inc/httputils.php @@ -0,0 +1,198 @@ +<?php +/** + * Utilities for handling HTTP related tasks + * + * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) + * @author Andreas Gohr <andi@splitbrain.org> + */ + +define('HTTP_MULTIPART_BOUNDARY','D0KuW1K1B0uNDARY'); +define('HTTP_HEADER_LF',"\r\n"); +define('HTTP_CHUNK_SIZE',16*1024); + +/** + * Checks and sets HTTP headers for conditional HTTP requests + * + * @author Simon Willison <swillison@gmail.com> + * @link http://simon.incutio.com/archive/2003/04/23/conditionalGet + * @param timestamp $timestamp lastmodified time of the cache file + * @returns void or exits with previously header() commands executed + */ +function http_conditionalRequest($timestamp){ + // A PHP implementation of conditional get, see + // http://fishbowl.pastiche.org/archives/001132.html + $last_modified = substr(gmdate('r', $timestamp), 0, -5).'GMT'; + $etag = '"'.md5($last_modified).'"'; + // Send the headers + header("Last-Modified: $last_modified"); + header("ETag: $etag"); + // See if the client has provided the required headers + if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])){ + $if_modified_since = stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']); + }else{ + $if_modified_since = false; + } + + if (isset($_SERVER['HTTP_IF_NONE_MATCH'])){ + $if_none_match = stripslashes($_SERVER['HTTP_IF_NONE_MATCH']); + }else{ + $if_none_match = false; + } + + if (!$if_modified_since && !$if_none_match){ + return; + } + + // At least one of the headers is there - check them + if ($if_none_match && $if_none_match != $etag) { + return; // etag is there but doesn't match + } + + if ($if_modified_since && $if_modified_since != $last_modified) { + return; // if-modified-since is there but doesn't match + } + + // Nothing has changed since their last request - serve a 304 and exit + header('HTTP/1.0 304 Not Modified'); + + // don't produce output, even if compression is on + ob_end_clean(); + exit; +} + +/** + * Let the webserver send the given file vi x-sendfile method + * + * @author Chris Smith <chris.eureka@jalakai.co.uk> + * @returns void or exits with previously header() commands executed + */ +function http_sendfile($file) { + global $conf; + + //use x-sendfile header to pass the delivery to compatible webservers + if($conf['xsendfile'] == 1){ + header("X-LIGHTTPD-send-file: $file"); + ob_end_clean(); + exit; + }elseif($conf['xsendfile'] == 2){ + header("X-Sendfile: $file"); + ob_end_clean(); + exit; + }elseif($conf['xsendfile'] == 3){ + header("X-Accel-Redirect: $file"); + ob_end_clean(); + exit; + } + + return false; +} + +/** + * Send file contents supporting rangeRequests + * + * This function exits the running script + * + * @param ressource $fh - file handle for an already open file + * @param int $size - size of the whole file + * @param int $mime - MIME type of the file + * + * @author Andreas Gohr <andi@splitbrain.org> + */ +function http_rangeRequest($fh,$size,$mime){ + $ranges = array(); + $isrange = false; + + header('Accept-Ranges: bytes'); + + if(!isset($_SERVER['HTTP_RANGE'])){ + // no range requested - send the whole file + $ranges[] = array(0,$size,$size); + }else{ + $t = explode('=', $_SERVER['HTTP_RANGE']); + if (!$t[0]=='bytes') { + // we only understand byte ranges - send the whole file + $ranges[] = array(0,$size,$size); + }else{ + $isrange = true; + // handle multiple ranges + $r = explode(',',$t[1]); + foreach($r as $x){ + $p = explode('-', $x); + $start = (int)$p[0]; + $end = (int)$p[1]; + if (!$end) $end = $size - 1; + if ($start > $end || $start > $size || $end > $size){ + header('HTTP/1.1 416 Requested Range Not Satisfiable'); + print 'Bad Range Request!'; + exit; + } + $len = $end - $start + 1; + $ranges[] = array($start,$end,$len); + } + } + } + $parts = count($ranges); + + // now send the type and length headers + if(!$isrange){ + header("Content-Type: $mime",true); + }else{ + header('HTTP/1.1 206 Partial Content'); + if($parts == 1){ + header("Content-Type: $mime",true); + }else{ + header('Content-Type: multipart/byteranges; boundary='.HTTP_MULTIPART_BOUNDARY,true); + } + } + + // send all ranges + for($i=0; $i<$parts; $i++){ + list($start,$end,$len) = $ranges[$i]; + + // multipart or normal headers + if($parts > 1){ + echo HTTP_HEADER_LF.'--'.HTTP_MULTIPART_BOUNDARY.HTTP_HEADER_LF; + echo "Content-Type: $mime".HTTP_HEADER_LF; + echo "Content-Range: bytes $start-$end/$size".HTTP_HEADER_LF; + }else{ + header("Content-Length: $len"); + if($isrange){ + header("Content-Range: bytes $start-$end/$size"); + } + } + + // send file content + fseek($fh,$start); //seek to start of range + $chunk = ($len > HTTP_CHUNK_SIZE) ? HTTP_CHUNK_SIZE : $len; + while (!feof($fh) && $chunk > 0) { + @set_time_limit(30); // large files can take a lot of time + print fread($fh, $chunk); + flush(); + $len -= $chunk; + $chunk = ($len > HTTP_CHUNK_SIZE) ? HTTP_CHUNK_SIZE : $len; + } + } + if($parts > 1){ + echo HTTP_HEADER_LF.'--'.HTTP_MULTIPART_BOUNDARY.'--'.HTTP_HEADER_LF; + } + + // everything should be done here, exit + exit; +} + +/** + * Check for a gzipped version and create if necessary + * + * return true if there exists a gzip version of the uncompressed file + * (samepath/samefilename.sameext.gz) created after the uncompressed file + * + * @author Chris Smith <chris.eureka@jalakai.co.uk> + */ +function http_gzip_valid($uncompressed_file) { + $gzip = $uncompressed_file.'.gz'; + if (filemtime($gzip) < filemtime($uncompressed_file)) { // filemtime returns false (0) if file doesn't exist + return copy($uncompressed_file, 'compress.zlib://'.$gzip); + } + + return true; +} diff --git a/inc/pageutils.php b/inc/pageutils.php index 105cfa18e..872191d12 100644 --- a/inc/pageutils.php +++ b/inc/pageutils.php @@ -528,97 +528,5 @@ function isVisiblePage($id){ return !isHiddenPage($id); } -/** - * Checks and sets HTTP headers for conditional HTTP requests - * - * @author Simon Willison <swillison@gmail.com> - * @link http://simon.incutio.com/archive/2003/04/23/conditionalGet - * @param timestamp $timestamp lastmodified time of the cache file - * @returns void or exits with previously header() commands executed - */ -function http_conditionalRequest($timestamp){ - // A PHP implementation of conditional get, see - // http://fishbowl.pastiche.org/archives/001132.html - $last_modified = substr(gmdate('r', $timestamp), 0, -5).'GMT'; - $etag = '"'.md5($last_modified).'"'; - // Send the headers - header("Last-Modified: $last_modified"); - header("ETag: $etag"); - // See if the client has provided the required headers - if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])){ - $if_modified_since = stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']); - }else{ - $if_modified_since = false; - } - - if (isset($_SERVER['HTTP_IF_NONE_MATCH'])){ - $if_none_match = stripslashes($_SERVER['HTTP_IF_NONE_MATCH']); - }else{ - $if_none_match = false; - } - - if (!$if_modified_since && !$if_none_match){ - return; - } - - // At least one of the headers is there - check them - if ($if_none_match && $if_none_match != $etag) { - return; // etag is there but doesn't match - } - - if ($if_modified_since && $if_modified_since != $last_modified) { - return; // if-modified-since is there but doesn't match - } - - // Nothing has changed since their last request - serve a 304 and exit - header('HTTP/1.0 304 Not Modified'); - - // don't produce output, even if compression is on - ob_end_clean(); - exit; -} -/** - * Let the webserver send the given file vi x-sendfile method - * - * @author Chris Smith <chris.eureka@jalakai.co.uk> - * @returns void or exits with previously header() commands executed - */ -function http_sendfile($file) { - global $conf; - - //use x-sendfile header to pass the delivery to compatible webservers - if($conf['xsendfile'] == 1){ - header("X-LIGHTTPD-send-file: $file"); - ob_end_clean(); - exit; - }elseif($conf['xsendfile'] == 2){ - header("X-Sendfile: $file"); - ob_end_clean(); - exit; - }elseif($conf['xsendfile'] == 3){ - header("X-Accel-Redirect: $file"); - ob_end_clean(); - exit; - } - - return false; -} - -/** - * Check for a gzipped version and create if necessary - * - * return true if there exists a gzip version of the uncompressed file - * (samepath/samefilename.sameext.gz) created after the uncompressed file - * - * @author Chris Smith <chris.eureka@jalakai.co.uk> - */ -function http_gzip_valid($uncompressed_file) { - $gzip = $uncompressed_file.'.gz'; - if (filemtime($gzip) < filemtime($uncompressed_file)) { // filemtime returns false (0) if file doesn't exist - return copy($uncompressed_file, 'compress.zlib://'.$gzip); - } - - return true; -} //Setup VIM: ex: et ts=2 enc=utf-8 : |