Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSendFile won't serve the files in Apache 2.2

I'm using mod_xsendfile (v0.12) to serve static files where Django is controlling access to the files based on users and permissions.

In my conf file, I have:

XSendFile On
XSendFilePath e:/documents/

<Directory e:/Documents>
  Order allow,deny
  Allow from all
</Directory>

In my django code, I set the headers like so:

assert(isinstance(filename, FieldFile))

xsendfile = filename.name
if(platform.system() == 'Windows'):
    xsendfile = xsendfile.replace('\\', '/')

response = HttpResponse()
response['X-Sendfile'] = xsendfile
mimetype = mimetypes.guess_type(xsendfile)[0]
response['Content-Type'] = mimetype
response['Content-Length'] = filename.size

And in my log file I get:

[Fri Oct 22 08:54:22 2010] [error] [client 192.168.20.34] (20023)The given path
was above the root path: xsendfile: unable to find file:
e:/Documents/3/2010-10-20/TestDocument.pdf

In this version of mod_xsendfile,

XSendFileAllowAbove On

generates the error:

Invalid command 'XSendFileAllowAbove', perhaps misspelled or defined by a module
not included in the server configuration

I assumed that was because they have added the XSendFilePath white list. Anyone else got this to work?

like image 786
boatcoder Avatar asked Oct 22 '10 15:10

boatcoder


2 Answers

I've had a whole lot of trouble most of the times I had to configure XSendfile path.

Here is me testing several scenarios on Windows to see what was wrong (jump to the end of this post to see the conclusion ad recommendations):

<?php

/* X-SENDFILE
 * This can be a b*tch to configure. So I'm writing various scenarios here so that I can rely on them in the future.
 * Example use: after re-installing XAMPP, after changing config file, in a new script, after some time without using it...
 * Tested on Windows 7 + XAMPP (Apache/2.4.3, PHP/5.4.7) + mod_xsendfile 1.0-P1 for Apache 2.4.x Win32
 */

/** Environment Debug **/
//echo dirname(__FILE__);   die();
//echo $_SERVER['DOCUMENT_ROOT'];   die();

/** The damn fucking path, with comments **/

// Local file in execution directory.
// Tested with: *no* XSendFilePath inside of the Apache config
// Result: works fine.
//header("X-Sendfile: " . 'localfile.zip' );

// Local file in execution directory + relative path
// Tested with: *no* XSendFilePath inside of the Apache config
// Result: works fine.
//header("X-Sendfile: " . '../xsendfile/localfile.zip' );

// Local file in execution directory + absolute pathS
// Tested with: *no* XSendFilePath inside of the Apache config
// Result: works fine and a lot of flexibility on the slash and letter drive format combinations *BUT* case-sensitive
//header("X-Sendfile: " . 'D:\Dropbox\XAMPP\web\tests\Languages\Apache\xsendfile\localfile.zip' );  // works fine
//header("X-Sendfile: " . '\Dropbox\XAMPP\web\tests\Languages\Apache\xsendfile\localfile.zip' );    // works fine
//header("X-Sendfile: " . 'D:/Dropbox/XAMPP/web/tests/Languages/Apache/xsendfile/localfile.zip' );  // works fine
//header("X-Sendfile: " . '/Dropbox/XAMPP/web/tests/Languages/Apache/xsendfile/localfile.zip' );    // works fine
//header("X-Sendfile: " . '/dropbox/XAMPP/web/tests/Languages/Apache/xsendfile/localfile.zip' );    // FAILS (case-sensitive)

// File in the XSendFilePath directory + Absolute path
// Tested with: XSendFilePath D:\Dropbox\XAMPP\web -- Mind the backward slashes
// Result: FAILS! error.log => [Wed Feb 20 19:08:02.617971 2013] [:error] [pid 15096:tid 1768] (20023)The given path was above the root path: [client ::1:56658] xsendfile: unable to find file: D:\\Dropbox\\XAMPP\\web\\xsfile.zip
//header("X-Sendfile: " . 'D:\Dropbox\XAMPP\web\xsfile.zip' );

// File in the XSendFilePath directory + Absolute path
// Tested with: XSendFilePath D:/Dropbox/XAMPP/web <== mind the forward slashes this time
// Result: WORKS! Conclusion: XSendFilePath needs use forward slashes on Windows AND we don't need any trailing slash in it.
header("X-Sendfile: " . 'D:\Dropbox\XAMPP\web\xsfile.zip' );

/** We might wanna test also:
 * - How does backward slashes in both XSendfilePath and the header combine?
 * - The use of subdirectories.
 * /

/** The rest of the headers (until otherwise stated, nothing special) **/
header("Content-Type: application/zip");
header("Content-Disposition: attachment; filename=\"" . 'blah.zip' . "\"");
header("Content-Transfer-Encoding: binary");
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Content-Description: File Transfer");

/** Tell the script to stop (so the file download may start) **/
die();


?>

So, basically, for X-Sendfile on Windows, make sure that:

  • You use forward slashes in your Apache configuration for XSendfilePath (mandatory);
  • Respect the case in your paths, even though we're on Windows (mandatory);
  • Use absolute paths everywhere (recommended)
  • No trailing slashes for XSendfilePath (recommended)

Hope it helps someone! Fabien

like image 193
Fabien Snauwaert Avatar answered Oct 19 '22 16:10

Fabien Snauwaert


Do not set a Content-Length yourself. This will only confuse handlers such as mod_wsgi in this case. mod_xsendfile will itself set the correct Content-Length.

On Windows you must not only provide the drive letter, the drive letter must be actually in upper-case (IIRC)!

I have a working test configuration like so:

<Directory "E:/">
  XSendFile on
  XSendFilePath E:/localhosts
</Directory>

One of my working test scripts in E:/Apache2.2/htdocs/ looks like this:

<?php
  header('X-SendFile: E:/localhosts/archive.tar.bz2');
  header('Content-type: application/octet-stream');
  header('Content-disposition: attachment; filename="blob"');
?>

XSendFileAllowAbove was removed a while back in favor of XSendFilePath

like image 44
nmaier Avatar answered Oct 19 '22 17:10

nmaier