Simulating static file via headers & filestream

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

Post Reply
arkady
Forum Newbie
Posts: 23
Joined: Sun Sep 17, 2006 9:34 pm

Simulating static file via headers & filestream

Post by arkady »

Gday Guys

Hoping I can explain exactly what i'm trying to do, without overshadowing the issue at hand. My aim is to provide, using apache mod_rewrite and an activex (i know i know but it's the only viewer available for this filetype) module, a preview of a file (type NWD) which is dynamically retrieved. The script that i'm creating needs to dump the output of the file in such a way as to trick the <object> into believing it is retrieving a static file. (ie. http://some.url.com/blah.nwd). Using mod_rewrite, i can obviously get the browser to believe a static url is a file. My problem lies in the way that I post my header information and the file contents. I can confirm that my object code (html) works perfectly with a static file, and a packet sniff indicates that i'm having problems with the way in which i'm delivering the streamed file.

I've attempted several methods of streaming the file in order to trick the retriever into believing it is a static file, and i'm confident that the header area isn't causing any problems. This is the current code that i'm attempting to use:

Code: Select all

else if ($to_be_zipped == 'stream')
                  {
                         if ((date('H')-8) < 10) $tmp = 0 . (date('H')-8);
                         else $tmp = (date('H')-8);
                         /*header("Content-Disposition:filename=\"navi.nwd\"");*/
                         header("Last-Modified: " . date("D, d M Y ") . $tmp . date(":i:s \G\M\T"));
                         header("ETag: \"test\"");
                         header("Accept-Ranges: bytes");
                         header("Content-Length: ".filesize($path_to_file));
                         /*header('Content-Transfer-Encoding: binary');*/
                         header("Connection: Keep-Alive");
                         header("Content-Type: text/plain");
                         ob_flush();
                         flush();
                         readfile_chunked($path_to_file);
                         /*$file = file_get_contents($path_to_file);
                         echo $file;
                         ob_flush();
						 flush();*/
                         /*$dataFile = fopen( $path_to_file, "r");
                         if ($dataFile)
                         {
                           while (!feof($dataFile))
                           {
                             $buffer = fgets($dataFile, 4096);
                             echo $buffer;
                             ob_flush();
                             flush();
                           }
                           fclose($dataFile);
                         }
                         else
                         {
                           die ("fopen couldn't open file");
                         } */
                  }

function readfile_chunked ($filename) {
  $chunksize = 1*(1504); // how many bytes per chunk
  $buffer = '';
  $handle = fopen($filename, 'rb');
  if ($handle === false)
  {
   return false;
  }
  while (!feof($handle)) {
   $buffer = fread($handle, $chunksize);
   print $buffer;
   flush();
   ob_flush();
   usleep(1000);
  }
  return fclose($handle);
}
Note the several commented out areas to give you an idea of what i've tried. A packet sniff reveals that the calling object is retrieving a portion of the file, usually around 1.5mb. Previous methods have resulted in TCP Window full packets being sent. The current method using the readfile_chunked method (yoinked & modified from a tutorial site) have not resulted in TCP Window Full packets, however the "simulated download" is still stopping around this 1.5mb mark.

I can also confirm that no extraneous data or whitespace is causing problems, and a simple modification of the above to force a header download results in a 100% working file.

As far as I can tell, i am 100% simulating a static file on a server, but having some sort of a problem with the way that the file is being delivered to the sender

*phew* ok, any ideas? :)

** edit: fixed typo in original code **
Last edited by arkady on Mon Sep 18, 2006 2:36 am, edited 1 time in total.
arkady
Forum Newbie
Posts: 23
Joined: Sun Sep 17, 2006 9:34 pm

Post by arkady »

Further investigation has revealed that the amount of data transmitted, varies according to the size of the chunk that i specify. If it's larger, i'll tend to get TCP Window Full packets and a larger amount of data (not all) is transmitted. Smaller packet size results in no TCP Window Full packets but less (say 1/4) data transmitted...

:(
User avatar
aaronhall
DevNet Resident
Posts: 1040
Joined: Tue Aug 13, 2002 5:10 pm
Location: Back in Phoenix, missing the microbrews
Contact:

Post by aaronhall »

Why does the file need to appear static? Are you talking about static as opposed to streamed?
User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Post by Jenk »

Code: Select all

readfile()
May be of use.
arkady
Forum Newbie
Posts: 23
Joined: Sun Sep 17, 2006 9:34 pm

Post by arkady »

To clarify, i'm trying to simulate the exact same situation as would happen if i were to point the module to a static file

Such as: http://blah.blah.com/myfile.nwd

When you use an address such as this, the module will load the file, no dramas.

I'm attempting to return the contents of some .nwd file in the following manner:

http://blah.blah.com/myscriptresults.ph ... &super=man

Using mod_rewrite, I can fool the script into believing the above url is http://blah.blah.com/myscriptresults/bl ... n/temp.nwd

The code that i've posted above, attempts to simulate the same HTTP header information that is returned when requesting a static file from a webserver. I can confirm that apart from the different URL's, the above two files (one static, one dynamic) will return identical (apart from different address) header information. My problem comes when sending the data to the module, I can see that the contents of the file are cut off abruptly at arbitrary points in the file.

Say I have two identical files.. One i return by placing it into the root of my project directory and use the first url to open. I observe the packets sent.
One i open by using the second url and mod_rewrite to 'trick' the module into believing it's the same situation as the first url. This second option will produce identical HTTP_Header information to the first, however I find that the transmission of the contents of the file will end abruptly at an arbitrary point.

Hope thats clarified things a little better

Cheers
arkady
Forum Newbie
Posts: 23
Joined: Sun Sep 17, 2006 9:34 pm

Post by arkady »

Jenk wrote:

Code: Select all

readfile()
May be of use.
Yep tried that previously, but i find that the entire file doesn't transmit.

Portion of the file transmits, then packet is sent from server [TCP Window Full] [TCP segment of a reassembled PDU]
Analysis: The transmission window is now completely full.
arkady
Forum Newbie
Posts: 23
Joined: Sun Sep 17, 2006 9:34 pm

Post by arkady »

And in keeping with the spirit of the day
The pirate speaks,"T' clarify, i'm tryin' t' simulate t' exact same situation as would happen if i were t' point t' module t' a static file

Such as: http://blah.blah.com/mefile.nwd When you use an address such as this, t' module will load t' file, no dramas.

I'm attemptin' t' return t' contents o' some .nwd file in t' followin' manner:
http://blah.blah.com/mescriptresults.ph ... &super=man

Usin' mod_rewrite, I can fool t' script into believin' t' above url be http://blah.blah.com/mescriptresults/bl ... n/temp.nwd

T' code that i've posted above, attempts t' simulate t' same HTTP header information that be returned when requestin' a static file from a webserver. I can confirm that apart from t' different URL's, t' above two files (one static, one dynamic) will return identical (apart from different address) header information. Me problem comes when sendin' t' data t' t' module, I can see that t' contents o' t' file be cut off abruptly at arbitrary points in t' file.

Say I have two identical files.. One i return by placin' it into t' root o' me project directory and use t' first url t' open. I observe t' packets sent. One i open by usin' t' second url and mod_rewrite t' 'trick' t' module into believin' it's t' same situation as t' first url. This second option will produce identical HTTP_Header information t' t' first, however I find that t' transmission o' t' contents o' t' file will end abruptly at an arbitrary point.

Hope thats clarified thin's a little better Cheers
arkady
Forum Newbie
Posts: 23
Joined: Sun Sep 17, 2006 9:34 pm

Post by arkady »

Resolution: the requesting module was attempting 2 standard GET's and if those failed, attempted a multipart get.

This code produced a working solution for me:

Code: Select all

else if ($to_be_zipped == 'stream')
                  {
                         if ((date('H')-8) < 10) $tmp = 0 . (date('H')-8);
                         else $tmp = (date('H')-8);
                         $range_type = array();
                         $range_value = array();
                         if (isset($_SERVER['HTTP_RANGE']))
                         {
                                  list($range_type, $range_value) = explode("=" , $_SERVER['HTTP_RANGE']);
                                  // range is in format bytes=0-65535 etc.
                                  // now $range_value is 0-65535 (eg)
                                  $range_from = (int)substr($range_value, 0, strpos($range_value, '-'));
                                  $range_to = (int)substr($range_value, (strpos($range_value, '-') + 1));
                                  /*header("X-Misc: " . $range_from . "(-)" . $range_to);*/
                                  $range_size = $range_to - $range_from + 1;
                                  if ($range_to > filesize($path_to_file))
                                  {
                                        $range_to = filesize($path_to_file) -1;
                                        $range_size = $range_to - $range_from + 1;
                                  }
                                  if ($range_to > 0)
                                  {
                                        header('HTTP/1.0 206 Partial Content');
                                        header("Last-Modified: " . date("D, d M Y ") . $tmp . date(":i:s \G\M\T"));
                                        header("ETag: \"blah\"");
                                        header("Accept-Ranges: bytes");
                                        header("Content-Length: " . $range_size);
                                        header("Content-Range: bytes ".$range_from."-".$range_to."/".(string)filesize($path_to_file));
                                        header("Keep-Alive: timeout=15, max=100");
                                        header("Content-Type: text/plain");
                                        /*header("X-Misc: range is ".$range_size);*/
                                        /*ob_flush();
                                        flush();*/
                                        $dataFile = fopen($path_to_file, "rb");
                                        if ($dataFile)
                                        {
                                              fseek($dataFile, $range_from);
                                              print fread($dataFile, $range_size);
                                              ob_flush();
                                              flush();
                                              exit();
                                        }
                                  }
                                  else
                                  {
                                        header("Last-Modified: " . date("D, d M Y") . $tmp . date(":i:s \G\M\T"));
                                        header("ETag: \"super\"");
                                        header("Accept-Ranges: bytes");
                                        header("content-Length: ".(string)filesize($path_to_file));
                                        header("Connection: close");
                                        header("Content-Type: text/plain");
                                        ob_flush();
                                        flush();
                                        readfile_chunked($path_to_file);
                                        exit();
                                  }
                         }
The above code, in addition with previously posted readfile_chunked and mod_rewrite enabled me to 'trick' a calling module into believing it was receiving a static file.

Hope this comes in use for others.
Post Reply