Summary
This is a simple & short patch against HEAD that adds a new file download method, "Custom", to the admin/setting/file-systems screen. With "Custom" selected, the "file_transfer_handler" configuration variable is expected to contain the name of a function that will handle the actual file transfer, and file_transfer() (in includes/file.inc) is modified to delegate the transfer process to that function.
Description
This was originally going to be a patch specifically for supporting the X-Sendfile feature provided by modern web servers (including Apache2 and Lighttpd). X-Sendfile provides a significant performance benefit for sites that need to offer large files (audio or video, for instance), but also need Drupal's access controls.
Boris advised me to make the patch more generic so that we could, conceivably, in the future support capabilities such as redirecting file transfers to an FTP server or an Amazon S3-based URL.
So, two file transfer handlers are provided as standard:
- file_transfer_php: the standard Drupal code that writes the file out in 1K blocks; this is the default and works as before.
- file_transfer_xsendfile: uses the X-Sendfile HTTP header to let the web server handle the file transfer in an efficient fashion, and without unnecessarily tying up a PHP process for the duration of the transfer.
At this point, the patch is exceedingly simple: two one-line changes to system.module and upload.module, and less than a dozen new lines added to includes/file.inc.
See screenshot of the modified "Download method" selection.
Installation
To try out the patch, you need a web server that supports X-Sendfile. Lighttpd has this support built in, and a mod_xsendfile module exists for Apache2. (Please see the References section for the relevant URLs.)
Installing the Apache module should be a breeze; it took me less than a minute on OS X with DarwinPorts:
wget http://celebnamer.celebworld.ws/stuff/mod_xsendfile/mod_xsendfile.c
sudo /opt/local/apache2/bin/apxs -cia mod_xsendfile.c
sudo /opt/local/apache2/bin/apachectl restart
Be advised that in addition to installing the Apache module, you will need to add the "XSendFile on" directive to your virtual host configuration or the .htaccess file supplied with Drupal. Also be sure to read the limitations of mod_xsendfile: for instance, the file directory must be located under the current directory (i.e. the Drupal web root), implying that you'll likely want to forbid direct HTTP access to the files via Apache config directives.
Once you have X-Sendfile transfer enabled in your web server, enable it for Drupal as by adding the following to your settings.php:
$conf = array('file_transfer_handler' => 'file_transfer_xsendfile');
References
| Comment | File | Size | Author |
|---|---|---|---|
| #15 | file_74472.patch | 1.57 KB | drewish |
| #14 | drupal_file_transfer_handler.patch | 1.1 KB | fago |
| fast_file_transfer.patch | 4.25 KB | Arto |
Comments
Comment #1
moshe weitzman commentedinteresting. i will do some reading on xsendfile. meanwhile, some minor administration:
in the fragment above, i would say 'admin defined handler' instead of user defined handler. And the description text implies that access control is only enforced by PRIVATE, but CUSTOM also enforces it.
Also, I find the settings a bit odd. We have a UI for enabling CUSTOM but not for specifying which function is the handler. Perhaps we should handle this custom_url_rewrite(). Thats a magic function name which is used to rewrite incoming or outgoing urls. Drupal will use it if present. See http://api.drupal.org/api/HEAD/function/drupal_get_path_alias. So in this case, we adjust the code to use fe_transfer_custom() is such exists. If not, use the specified transfer methord. This is roughly analogous to the custom SMTP library as well.
Does anyone know of optimizations which will work on typical web hosts that don't allow xsendfile extension or are apache 1.x?
Comment #2
boris mann commentedFantastic write up, Arto. I'm going to ping the dev list with a pointer to this issue as well.
Comment #3
killes@www.drop.org commentedIt would be nice to have this in core, but I think we should not have file_transfer_xsendfile in core as it is a non-standard feature.
Comment #4
Egon Bianchet commentedInteresting, we should reconsider this for Drupal 6
Comment #5
Steven commentedI don't see much point in the UI option as it is now... this feature requires at least some code to support the download method, and the variable for the handler name anyway.
Ideally, we'd have a mechanism where you can have an x-send-file.module which sets the handler variable on hook_enable / disable. By setting this variable, the download method setting would be grayed out (and this x-send-file module could even form_alter it to add the third option saying 'X-Send-File').
Comment #6
Wesley Tanaka commentedSubscribing (after having read http://www.mysqlperformanceblog.com/2007/02/11/content-delivery-system-d...)
Comment #7
fagosubscribing
Comment #8
nirl commentedGreat Idea!
Two questions, though:
1) what is this patch for?:
2) Why not simplify your patch to work based on a setting.php function (i think this is what Moshe ment, but i am not sure). In this case if a function existed in setting.php (or maybe even a module?) it would send all private files with this function. This way you only add a few lines to file_transfer():
Comment #9
nirl commentedForgot to mention in my last post, this is also very usefull if you are using file wrapper modules such as imagecache and friends.
Comment #10
drewish commentedsubscribing. seems very interesting but i'm not sure it'll make it into 6 this late. i'd be interested in pushing it after some of my other files patches get committed.
Comment #11
ChrisKennedy commentedComment #12
jonathan_hunt commentedSubscribing. We have several clients using Drupal to xfer large files via groups (og). It would be good to see this option available in Drupal core, even if it needs explicit configuration to enable (probably safest option).
Has anyone seen benchmarks comparing x-sendfile to normal Drupal downloads?
While I am here, does anyone know if it is possible to use Partial Content with X-sendfile headers? e.g. http://drupal.org/node/91934
Comment #13
treksler commentedHey,
People want either "Public" or "Private" file transfers, that's it!
Nobody, and I mean NOBODY wants a third option on that screen
The fact that "Public files" are served by Apache
and that currently "Private files" are served by PHP
is an implementation detail that the users should never have to worry about.
On that note,
the code should probably use X-Sendfile if available, and PHP otherwise for "Private" file transfers.
This would simplify the patch to an if statement and one extra line for the X-sendfile header
Now,
If I only knew how to detect sendfile support on-the-fly
how does Drupal detect mod_rewrite for Clean URL support?
Comment #14
fagoHere is new small patch, which also makes fast private file transfers possible. It just allows overriding the default file transfer handling while the default is the current implementation. Now file_transfer() just invokes the configured handler.
X-Sendfile users can activate it by adding this to settings.php:
With this patch one could also implement a small module for activating X-Sendfile file transfers.
Comment #15
drewish commenteddidn't test this patch but tried to clean up some of the comments and documentation.
Comment #16
fagothanks. patch still works as described.
Any testers?
Comment #17
drewish commentedi think it's unreasonable to think this will make it into 6.x at this point. bumping to 7.x.
Comment #18
bdragon commentedSubscribing, +1.
Comment #19
headkit commenteddoublesubscribe +2!
Comment #20
WorldFallz commentedsubscribing, i should be able to test this this week.
Comment #21
c960657 commentedWhat about
in_array('mod_xsendfile',apache_get_modules())?Comment #22
wim leersSubscribing.
Comment #23
aterchin commentedsubscribing
Comment #24
WorldFallz commentedUpdate
I finally got a chance to try this patch-- I couldn't get that patch to apply properly (against the current head), so I did it manually.
However, I was unable to actually try it because as it turns out I'm having trouble getting apache to use mod_xsendfile-- every time I try to load it apache refuses to start.
Comment #25
Susurrus commentedI think someone else mentioned this earlier, but should we even display this option to the user. I would think it would be better if things were either public or private, and if you installed a module that offered a new file serving method, it would just take over until it was uninstalled. They should probably be good about when they're enabled and put some checks for which method is the current one in hook_requirements(), but that should be a contrib issue, not a core issue. I think this has the simplicity needed to make things easy for users (with less options) and still offers all the benefits.
Comment #26
fall_0ut commentedsubscribing...
Comment #27
Arto commentedComment #28
lilou commentedPatch still applied against CVS HEAD.
Comment #29
Phillip Mc commentedsubscribing
Comment #30
drewish commentedit'd be possible to do this totally in contrib now. with the new menu system you could just menu_alter the system/files callback to your own handler, replicate a bit of the code from file_download() and do your own headers.
Comment #31
dave reid+1 for a contrib solution w/ hook_menu_alter. This gets overly complicated with more than public/private.
Comment #32
moshe weitzman commenteddoubt this works now.
Comment #33
dopry commentedCore currently provides enough flexibility for modules to provide this functionality. I say we call it won't fix.
Comment #34
gagagaga commentedCan I use this patch for drupal 6.x?
Comment #35
hypertext200I have created module for x-send file, included small patch against HEAD http://drupal.org/project/xsend.
Welcome your comments!!!
Comment #36
yajnin commentedsubscribe
Comment #37
jp2020 commentedI am a newbie on the site. Just wanted to follow up with regards to the status of this patch.