summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAndreas Gohr <gohr@cosmocode.de>2006-03-02 13:50:20 +0100
committerAndreas Gohr <gohr@cosmocode.de>2006-03-02 13:50:20 +0100
commite935fb4a8f253a32fd11d53b3a487de4c8ca310e (patch)
treeab462e09312dd780a30976f04591021dbf7a837b /lib
parenta794f25d0fe66f4a7df518b3ea88160bb3782764 (diff)
downloadrpg-e935fb4a8f253a32fd11d53b3a487de4c8ca310e.tar.gz
rpg-e935fb4a8f253a32fd11d53b3a487de4c8ca310e.tar.bz2
fetch.php now understands partial and conditional requests, fix for #593
darcs-hash:20060302125020-6e07b-25b701312aa4da899f299d82ccac1f5fd66e0255.gz
Diffstat (limited to 'lib')
-rw-r--r--lib/exe/fetch.php125
1 files changed, 107 insertions, 18 deletions
diff --git a/lib/exe/fetch.php b/lib/exe/fetch.php
index 46010917c..64bd35729 100644
--- a/lib/exe/fetch.php
+++ b/lib/exe/fetch.php
@@ -14,6 +14,7 @@
require_once(DOKU_INC.'inc/auth.php');
//close sesseion
session_write_close();
+ if(!defined('CHUNK_SIZE')) define('CHUNK_SIZE',16*1024);
$mimetypes = getMimeTypes();
@@ -53,8 +54,8 @@
exit;
}
$FILE = mediaFN($MEDIA);
- }
-
+ }
+
//check file existance
if(!@file_exists($FILE)){
header("HTTP/1.0 404 Not Found");
@@ -68,35 +69,123 @@
$FILE = get_resized($FILE,$EXT,$WIDTH,$HEIGHT);
}
+ // finally send the file to the client
+ sendFile($FILE,$MIME);
- //FIXME set sane cachecontrol headers
- //FIXME handle conditional and partial requests
+/* ------------------------------------------------------------------------ */
- //send file
- header("Content-Type: $MIME");
- header('Last-Modified: '.date('r',filemtime($FILE)));
- header('Content-Length: '.filesize($FILE));
- header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0');
+/**
+ * Set headers and send the file to the client
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+function sendFile($file,$mime){
+ // send headers
+ header("Content-Type: $mime");
+ http_conditionalRequest(filemtime($file));
+ list($start,$len) = http_rangeRequest(filesize($file));
+ header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+ header('Pragma: public');
+ header('Accept-Ranges: bytes');
//application mime type is downloadable
- if(substr($MIME,0,11) == 'application'){
- header('Content-Disposition: attachment; filename="'.basename($FILE).'"');
+ if(substr($mime,0,11) == 'application'){
+ header('Content-Disposition: attachment; filename="'.basename($file).'";');
}
- $fp = @fopen($FILE,"rb");
+ // send file contents
+ $fp = @fopen($file,"rb");
if($fp){
- while (!feof($fp)) {
+ fseek($fp,$start); //seek to start of range
+
+ $chunk = ($len > CHUNK_SIZE) ? CHUNK_SIZE : $len;
+ while (!feof($fp) && $chunk > 0) {
@set_time_limit(); // large files can take a lot of time
- print fread($fp, 16*1024);
+ print fread($fp, $chunk);
flush();
+ $len -= $chunk;
+ $chunk = ($len > CHUNK_SIZE) ? CHUNK_SIZE : $len;
}
fclose($fp);
}else{
header("HTTP/1.0 500 Internal Server Error");
- print "Could not read $FILE - bad permissions?";
+ print "Could not read $file - bad permissions?";
}
+}
-/* ------------------------------------------------------------------------ */
+/**
+ * Checks and sets headers to handle range requets
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ * @returns array The start byte and the amount of bytes to send
+ */
+function http_rangeRequest($size){
+ if(!isset($_SERVER['HTTP_RANGE'])){
+ // no range requested - send the whole file
+ header("Content-Length: $size");
+ return array(0,$size);
+ }
+
+ $t = explode('=', $_SERVER['HTTP_RANGE']);
+ if (!$t[0]=='bytes') {
+ // we only understand byte ranges - send the whole file
+ header("Content-Length: $size");
+ return array(0,$size);
+ }
+
+ $r = explode('-', $t[1]);
+ $start = (int)$r[0];
+ $end = (int)$r[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;
+ }
+
+ $tot = $end - $start + 1;
+ header('HTTP/1.1 206 Partial Content');
+ header("Content-Range: bytes {$start}-{$end}/{$size}");
+ header("Content-Length: $tot");
+
+ return array($start,$tot);
+}
+
+/**
+ * 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
+ */
+function http_conditionalRequest($timestamp){
+ // A PHP implementation of conditional get, see
+ // http://fishbowl.pastiche.org/archives/001132.html
+ $last_modified = substr(date('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_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ?
+ stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) :
+ false;
+ $if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ?
+ stripslashes($_SERVER['HTTP_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');
+ exit;
+}
/**
* Resizes the given image to the given size
@@ -165,7 +254,7 @@ function get_from_URL($url,$ext,$cache){
return false;
}
}
-
+
//if cache exists use it else
if($mtime) return $local;
@@ -286,7 +375,7 @@ function resize_imageGD($ext,$from,$from_w,$from_h,$to,$to_w,$to_h){
* @param int $mem Size of memory you want to allocate in bytes
* @param int $used already allocated memory (see above)
* @author Filip Oscadal <webmaster@illusionsoftworks.cz>
- * @author Andreas Gohr <andi@splitbrain.org>
+ * @author Andreas Gohr <andi@splitbrain.org>
*/
function is_mem_available($mem,$bytes=1048576){
$limit = trim(ini_get('memory_limit'));