Ejemplo n.º 1
0
function start($PARAMS)
{
    if (isset($PARAMS["secure"]) and !isset($_SERVER["HTTPS"])) {
        //logg("server name: ".$_SERVER["SERVER_NAME"]."\nrequest uri: ".$_SERVER["REQUEST_URI"]);
        //header("Location: https://".$_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"]);               // redirect to https
        header("Location: https://t.oslo.opera.com" . $_SERVER["REQUEST_URI"]);
        // redirect to https [T version]
        exit(0);
    }
    // defined constants
    define("DEFAULT_CHUNK_SIZE", 1024 * 1024);
    // DEFAULT_CHUNK_SIZE is 1 MB
    // define startup defaults
    $SUPPORT_RANGE_HEADER = true;
    // whether or not we (the server) support the range header  [default: true                      ]
    $FORCE_BROWSER_NOCACHE = false;
    // whether or not to send no-cache (and friends) headers    [default: false                     ]
    $FORCE_CHUNKED = false;
    // whether or not to send chunked data                      [default: false                     ]
    $filename = "11s.webm";
    // media file to serve                                      [default: 11s.webm                  ]
    //$filename               = "../../../../../../resources/media/webm/11s.webm";                    // media file to serve                                      [default: 11s.webm                  ]
    $rate = 0.0;
    // data burst rate                                          [default: no limit                  ]
    $chunksize = 1.0 * DEFAULT_CHUNK_SIZE;
    // max chunk size                                           [default: 1 MB                      ]
    $sleep = 0.0;
    // delay between chunks                                     [default: 0s                        ]
    $timeout = 3;
    // timeout for the connection                               [default: 3s                        ]
    $limitsize = false;
    // limit the number of bytes to send                        [default: disabled                  ]
    $ifrange = false;
    // control if-range behavior                                [default: disabled                  ]
    // the below parameter has meaning only if USE_CONTENTRANGE is true and SUPPORT_HEADER_RANGE is true
    $offset = 0;
    // bytes added to range end to invoke invalid response      [default: 0                         ]
    // list of headers to send
    $HEADERS = array();
    $USE_ETAG = false;
    // whether or not to send the etag header                   [default: false                     ]
    $HTTP_STATUS = false;
    // http status header to send (use 206 or 200 if not set)   [default: false                     ]
    $date = false;
    // date header to send                                      [default: don't send any            ]
    $contenttype = "application/octet-stream";
    // content type header to send (autodetected if not set)    [default: application/octet-stream  ]
    // the below headers have meaning only if SUPPORT_RANGE_HEADER is true (norange parameter is not set)
    $USE_CONTENTRANGE = true;
    // whether or not to send the content range header          [default: true                      ]
    $acceptranges = "bytes";
    // accept ranges header to send                             [default: bytes                     ]
    // the below headers have meaning only if FORCE_BROWSER_CACHE is false (nocache parameter is not set)
    $expires = false;
    // expires header to send                                   [default: don't send any            ]
    $lastmodified = false;
    // last modified header to send                             [default: don't send any            ]
    $cachecontrol = false;
    // cache control header to send                             [default: don't send any            ]
    $id = $_SERVER["REMOTE_ADDR"];
    // set the script execution ID (for use with force timeout) [default: client IP                 ]
    // override default values with provided parameters (+sanity checks)
    if (isset($PARAMS["norange"])) {
        $SUPPORT_RANGE_HEADER = false;
    }
    // disable server support for the range header
    if (isset($PARAMS["nocache"])) {
        $FORCE_BROWSER_NOCACHE = true;
    }
    // enable sending of no-cache (and friends) headers
    if (isset($PARAMS["chunked"])) {
        $FORCE_CHUNKED = true;
    }
    // enable Transfer-Encoding: chunked (disable Content-Length)
    if (isset($PARAMS["contentrange"])) {
        $USE_CONTENTRANGE = false;
    }
    // disable sending the Content-Range header
    if (isset($PARAMS["acceptranges"])) {
        $acceptranges = $PARAMS["acceptranges"];
    }
    // change the default Accept-Ranges header
    if (isset($PARAMS["etag"])) {
        $USE_ETAG = true;
    }
    // enable sending the ETag header
    if (isset($PARAMS["status"])) {
        $HTTP_STATUS = $PARAMS["status"];
    }
    // change the default HTTP/1.1 status header
    if (isset($PARAMS["date"])) {
        $date = $PARAMS["date"];
    }
    // enable sending the Date header and set it's value or 'yes' for current date -1s
    if (isset($PARAMS["expires"])) {
        $expires = $PARAMS["expires"];
    }
    // enable sending the Expires header and set to how many seconds from current date it should be set
    if (isset($PARAMS["lastmodified"])) {
        $lastmodified = $PARAMS["lastmodified"];
    }
    // enable sending the Last-Modified header and set it's value or 'yes' for the actual file modification date
    if (isset($PARAMS["cachecontrol"])) {
        $cachecontrol = $PARAMS["cachecontrol"];
    }
    // enable sending the Cache-Control header and set it's value
    if (isset($PARAMS["file"])) {
        $filename = $PARAMS["file"];
    }
    // set custom file name
    if (isset($PARAMS["size"])) {
        $limitsize = $PARAMS["size"];
    }
    // limit the number of bytes to send
    if (isset($PARAMS["if-range"])) {
        $ifrange = $PARAMS["if-range"];
    }
    // control the if-range behavior
    if (isset($PARAMS["timeout"]) and is_numeric($PARAMS["timeout"])) {
        $timeout = floatval($PARAMS["timeout"]);
    }
    // set custom timeout
    if (isset($PARAMS["rate"]) and is_numeric($PARAMS["rate"])) {
        $rate = floatval($PARAMS["rate"]) * DEFAULT_CHUNK_SIZE;
    }
    // set custom size
    if (isset($PARAMS["contenttype"])) {
        $contenttype = $PARAMS["contenttype"];
    } else {
        $contenttype = getcontenttype($filename, $contenttype);
    }
    // or try to determine what it is based on filename
    if (isset($PARAMS["contentrangeoffset"])) {
        $offset = intval($PARAMS["offset"]);
    }
    // add some bytes to the range end to invoke an invalid range response
    if (isset($PARAMS["forcetimeout"])) {
        die(setForceTimeoutLock($PARAMS["forcetimeout"]));
    }
    // enable instant "timeout" simulation
    if (isset($PARAMS["id"])) {
        $id = $PARAMS["id"];
    }
    // set script execution ID (for use with force timeout)
    if (!($video = fopen($filename, "r"))) {
        exit(0);
    }
    // check if file exists and can be opened for reading
    $filesize = $limitsize ? intval($limitsize) : filesize($filename);
    // get filesize (used frequently later on)
    if ($ifrange and $_SERVER["HTTP_IF_RANGE"]) {
        switch ($ifrange) {
            case "200":
                // case 200
                $SUPPORT_RANGE_HEADER = false;
                // disable range support (same as norange=1)
                break;
            case "400":
                // case 400
                header("HTTP/1.1 400 Bad Request");
                // send bad request status
                die("Sorry, requested range (" . $_SERVER["HTTP_RANGE"] . ") is not satisfiable!");
                // and exit the script
                break;
        }
    }
    if (isset($_SERVER["HTTP_RANGE"]) and $SUPPORT_RANGE_HEADER) {
        $RANGE = explode("=", $_SERVER["HTTP_RANGE"]);
        // split the header into type and range
        // sanity check
        if (count($RANGE, 2)) {
            $RANGE = explode("-", $RANGE[1]);
            // split the range into start and end
            // sanity checks
            if (count($RANGE) == 1) {
                array_push($RANGE, $filesize - 1);
            }
            // make sure the range end value is set
            if (is_numeric($RANGE[0])) {
                $RANGE[0] = intval($RANGE[0]);
            } else {
                $RANGE[0] = 0;
            }
            // if not then set to default: 0
            if (is_numeric($RANGE[1])) {
                $RANGE[1] = intval($RANGE[1]);
            } else {
                $RANGE[1] = $filesize - 1;
            }
            // if not then set to default: max (filesize - 1 as per RFC)
        } else {
            $RANGE = array(0, $filesize - 1);
        }
        // set default start and end values
        array_unshift($RANGE, $_SERVER["HTTP_RANGE"]);
        // just because :)
        // set the required response headers for a range request
        array_push($HEADERS, "HTTP/1.1 " . ($HTTP_STATUS ? $HTTP_STATU : "206 Partial Content"));
        array_push($HEADERS, "Accept-Ranges: " . $acceptranges);
        if ($USE_CONTENTRANGE) {
            array_push($HEADERS, "Content-Range: bytes " . $RANGE[1] . "-" . ($RANGE[2] + $offset) . "/" . $filesize);
        }
    } else {
        // set headers for a standard http server response
        array_push($HEADERS, "HTTP/1.1 " . ($HTTP_STATUS ? $HTTP_STATUS : "200 OK"));
        $RANGE = array("not set", 0, $filesize - 1);
    }
    // set additional required and optional headers
    if ($FORCE_CHUNKED) {
        array_push($HEADERS, "Transfer-Encoding: chunked");
    } else {
        array_push($HEADERS, "Content-Length: " . ($RANGE[2] - $RANGE[1] + 1));
    }
    // ELSE set the Content-Length header
    if ($USE_ETAG) {
        array_push($HEADERS, "ETag: " . md5(floor(mktime() / 30) * 30));
    }
    // IF etag was requested set the ETag header
    if ($date) {
        $date = $date == "yes" ? httpdate(time() - 1) : $date;
        // use the date provided or create a data 1s in the past
        array_push($HEADERS, "Date: " . $date . " GMT");
        // and set the Date header
    }
    if ($FORCE_BROWSER_NOCACHE) {
        array_push($HEADERS, "Last-Modified: " . httpdate(filemtime($filename)) . " GMT");
        array_push($HEADERS, "Cache-Control: max-age=0, no-cache, no-store, must-revalidate");
        array_push($HEADERS, "Pragma: no-cache");
        array_push($HEADERS, "Expires: Wed, 11 Jan 1984 05:00:00 GMT");
    } else {
        if ($cachecontrol) {
            array_push($HEADERS, "Cache-Control: " . $cachecontrol);
        }
        // IF cache control was requested set Cache-Control header
        if ($lastmodified) {
            $lastmodified = $lastmodified == "yes" ? httpdate(filemtime($filename)) : $lastmodified;
            // use the lastmodified provided or get the actual file modification date
            array_push($HEADERS, "Last-Modified: " . $lastmodified);
            // set Last-Modified header
        }
        if ($expires) {
            array_push($HEADERS, "Expires: " . gmdate("D, j M Y H:i:s T", time() + $expires));
            // set the Expires header
            array_push($HEADERS, "Cache-Control: max-age=" . $expires . ", must-revalidate");
            // and the Cache-Control header with max-age and must-revalidate
        }
    }
    array_push($HEADERS, "Content-Type: " . $contenttype);
    // add the content type header
    foreach ($HEADERS as $header) {
        header($header);
    }
    // send the headers
    // move the file pointer to the range start (with sanity check)
    if (fseek($video, $RANGE[1]) == -1) {
        $position = 0;
    } else {
        $position = $RANGE[1];
    }
    // set the position to where we seeked in the file
    // initialize timers and counters
    $chunks = 0;
    // initialize the chunk counter
    $bytes = 0;
    if ($rate != 0) {
        $usleep = 10;
        // initialize time interval for sending data chunks to 10 microseconds when data rate is limited
        $chunksize = round($rate * ($usleep / 1000000));
        // initialize chunk size based on data rate limit
    }
    logg("\n");
    logg("-------------------");
    logg("Range:             " . $RANGE[0]);
    logg("script id:         " . $id);
    logg("rate:              " . ($rate != 0 ? $rate . " B/s" : "no limit") . ", sleep: " . $sleep / 1000000 . ", usleep: " . $usleep . ", chunksize: " . $chunksize);
    logg("timeout:           " . $timeout . "s");
    logg("clear locks:       " . clearForceTimeoutLocks());
    $timers = new TimerManager();
    $timers->start("total");
    // initialize data burst timer
    $timers->start("timeout");
    // initialize the connection timer
    // start the data sending loop
    while (connection_status() == CONNECTION_NORMAL) {
        ignore_user_abort(true);
        // allow the client to abort the connection
        set_time_limit($timeout);
        // this doesn't seem to work at all
        if (checkForceTimeoutLock($id)) {
            logg("exit reason:       forced timeout");
            break;
        }
        // if forced timeout is enabled
        if ($timers->elapsed("timeout") > $timeout) {
            logg("exit reason:       timeout");
            break;
        }
        // if timeout, then break
        if ($RANGE[2] + 1 <= $position) {
            logg("exit reason:       done");
            break;
        }
        // break if we've sent all the requested data (range end limit)
        if ($rate != 0) {
            $chunksize = max($chunksize + (round($timers->elapsed("total") * $rate) - ($position + $chunksize)), 0);
        }
        if ($RANGE[2] + 1 < $position + $chunksize) {
            // otherwise the file pointer will be set to far for the next read, also we
            $chunksize = $RANGE[2] + 1 - $position;
            // might read past file end
        }
        $timers->start("timeout");
        // reset the connection timer (we want to timeout only on idle connections,
        // eg. video paused, not on active ones, eg. video playing)
        if ($chunksize > 0) {
            $timers->start("fread");
            $data = fread($video, $chunksize);
            // read the data
            $timers->stop("fread");
            $position += $chunksize;
            // update the current position
            $bytes += $chunksize;
            $chunks += 1;
            // increment the chunk counter
            //logg("chunk ".($chunks).": ".($position - $chunksize)."-".($position - 1)." [".(strlen($data))." bytes] (size: ".($chunksize).")");
            // send the data to the client
            print $data;
            $timers->start("flush");
            flush();
            $timers->stop("flush");
            $timers->start("obflush");
            ob_flush();
            $timers->stop("obflush");
        }
        $timers->start("sleep");
        usleep($usleep);
        // wait before sending the next chunk of data
        $timers->stop("sleep");
    }
    $timers->stop("total");
    logg("connection status: " . connection_status());
    logg("sent:              " . $bytes . " bytes in " . $chunks . " chunks [avg chunksize: " . $bytes / $chunks . " bytes, rate: " . $bytes / $timers->total("sleep") . " B/s, real rate: " . $bytes / $timers->total("total") . " B/s]");
    logg("bench:             " . $timers->show("fread") . ", " . $timers->show("flush") . ", " . $timers->show("obflush") . ", " . $timers->show("sleep") . ", " . $timers->show("total"));
    logg("-------------------");
    fclose($video);
    // close the file
    exit(0);
    // exit the script
}
Ejemplo n.º 2
0
function loadfile($filelocation, $rate)
{
    $log = isset($_GET['logfile']);
    //should we log or not?
    if ($log) {
        $logfile = '../logfiles/' . basename($_GET['logfile']);
        //only allow writing to ../logfiles/
        $lp = fopen($logfile, 'a');
        //pointer to write to log file
        if (!$lp) {
            die('Cannot open the log file: ' . $logfile);
        }
        //bail if you can't open the log file
    }
    $fp = fopen("{$filelocation}", "rb");
    //pointer to read from file
    if (!$fp) {
        $msg = "Cannot open file: {$filelocation}\n";
        if ($log) {
            fwrite($lp, $msg);
            fclose($lp);
        }
        die($msg);
        //bail if you can't read the file
    }
    if (isset($_GET['size']) && intval($_GET['size']) <= filesize($filelocation) && intval($_GET['size']) != 0) {
        //use the provided 'size' for slicing
        $filesize = intval($_GET['size']);
    } else {
        $filesize = filesize($filelocation);
    }
    if ($filesize == 0) {
        $msg = "'Zero byte file! Aborting download.\n";
        if ($log) {
            fwrite($lp, $msg);
            fclose($lp);
        }
        fclose($fp);
        die($msg);
        //bail if filesize = 0
    }
    if ($rate == 0) {
        $rate = $filesize;
    }
    //rate=0 means no throtteling
    if (isset($_GET['contenttype'])) {
        $content_type = $_GET['contenttype'];
    } else {
        $content_type = getcontenttype($filelocation);
        //try to detect the content-type
        if (!$content_type) {
            $content_type = 'application/octet-stream';
        }
        //default content-type
    }
    $range1 = 0;
    $range2 = $filesize - 1;
    $use_range = isset($_SERVER['HTTP_RANGE']);
    $content_range_offset = isset($_GET['contentrangeoffset']) ? intval($_GET['contentrangeoffset']) : 0;
    if (isset($_GET['if-range']) && isset($_SERVER['HTTP_IF_RANGE'])) {
        switch ($_GET['if-range']) {
            case '200':
                // pretend that the If-Range check failed and return 200 OK
                $use_range = false;
                break;
            case '400':
                header('HTTP/1.1 400 Bad Request');
                $msg = 'Sorry, requested range (bytes=' . $range1 . '-' . $range2 . ') is not satisfiable!';
                if ($log) {
                    fwrite($lp, $msg);
                    fwrite($lp, "\n");
                    fclose($lp);
                }
                fclose($fp);
                die($msg);
                break;
        }
    }
    if ($use_range) {
        preg_match('/bytes=(\\d+)-(\\d+)?/', $_SERVER['HTTP_RANGE'], $matches);
        $range1 = intval($matches[1]);
        $range2 = intval($matches[2]);
        if ($range2 == 0) {
            $range2 = $filesize - 1;
        }
        $length = $range2 - $range1 + 1;
        if (isset($_GET['etag'])) {
            $content = floor(mktime() / 30) * 30;
            $etag = md5($content);
            header('ETag: ' . $etag);
        }
        if (isset($_GET['cachecontrol'])) {
            header('Cache-Control: ' . $_GET['cachecontrol']);
        }
        if (isset($_GET['expires'])) {
            header('Expires: ' . gmdate('D, j M Y H:i:s T', time() + 200));
            header('Cache-Control: max-age=200, must-revalidate');
        }
        if (isset($_GET['lastmodified'])) {
            $last_modified_time = filemtime($filelocation);
            header("Last-Modified: " . httpdate($last_modified_time) . " GMT");
        }
        if (isset($_GET['date'])) {
            // set the Date to 1 second ago emulate some network lag
            header("Date: " . httpdate(time() - 1) . " GMT");
        }
        if (isset($_GET['status'])) {
            header('HTTP/1.1 ' . $_GET['status']);
        } else {
            header('HTTP/1.1 206 Partial Content');
        }
        header('Content-Type: ' . $content_type);
        if (!isset($_GET['chunked'])) {
            header('Content-Length: ' . $length);
        }
        if (!isset($_GET['contentrange'])) {
            header('Content-Range: bytes ' . $range1 . '-' . ($range2 + $content_range_offset) . '/' . $filesize);
        }
        if (!isset($_GET['acceptranges'])) {
            header('Accept-Ranges: bytes');
        } else {
            header('Accept-Ranges: none');
        }
        if ($log) {
            fwrite($lp, "\n");
            fwrite($lp, $_SERVER['HTTP_RANGE']);
            fwrite($lp, "\t\t\t");
        }
    } else {
        header('HTTP/1.1 200 OK');
        header('Content-Type: ' . $content_type);
        if (!isset($_GET['chunked'])) {
            header('Content-Length: ' . $filesize);
        }
        if (isset($_GET['etag'])) {
            $content = floor(mktime() / 30) * 30;
            $etag = md5($content);
            header('ETag: ' . $etag);
        }
    }
    if ($range1 > $range2 || $range2 >= $filesize) {
        header('HTTP/1.1 416 Requested Range Not Satisfiable');
        $msg = 'Sorry, requested range (bytes=' . $range1 . '-' . $range2 . ') is not satisfiable!';
        if ($log) {
            fwrite($lp, $msg);
            fwrite($lp, "\n");
            fclose($lp);
        }
        fclose($fp);
        die($msg);
    }
    fseek($fp, $range1);
    $tempfp = $range1;
    $count = 0;
    while (!feof($fp) and connection_status() == 0) {
        set_time_limit(0);
        ignore_user_abort(true);
        if ($tempfp > $range2) {
            break;
        }
        print fread($fp, $rate);
        $tempfp = $rate + $tempfp;
        $count++;
        flush();
        ob_flush();
        sleep($GLOBALS['sleep']);
    }
    if ($log) {
        fwrite($lp, $count * $rate);
        fwrite($lp, "\n");
        fclose($lp);
    }
    fclose($fp);
}