I noticed using Drupal in an internal network that file_create_url() function always returns absolute URL. This is annoying in some case, using an Apache proxy in front of a Drupal, to access it from the outside web, the URL won't be correct.
function file_create_url($path) {
// Strip file_directory_path from $path. We only include relative paths in urls.
if (strpos($path, file_directory_path() . '/') === 0) {
$path = trim(substr($path, strlen(file_directory_path())), '\\/');
}
switch (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC)) {
case FILE_DOWNLOADS_PUBLIC:
return $GLOBALS['base_url'] .'/'. file_directory_path() .'/'. str_replace('\\', '/', $path);
case FILE_DOWNLOADS_PRIVATE:
return url('system/files/'. $path, NULL, NULL, TRUE);
}
}
If we look at this, we see the hard-coded $GLOBALS['base_url']
When looking at the url() function, we see the optional $absolute parameter, which default is FALSE. url() function always make relative to pathes to the server's document root if we don't set this parameter when we call it.
Would it be better to have the same behavior as the url() function ? In some cases, this would be better to let the module developer the choice of using relative URL.
I join to this post how could be a patch for this.
If always return an absolute URL for files is really a feature, I'd like to know why this seems to be necessary for you.
Comment | File | Size | Author |
---|---|---|---|
#29 | 278770.patch | 2.74 KB | grendzy |
#29 | 278770_test.patch.txt | 5.62 KB | grendzy |
#27 | 278770.patch | 8.59 KB | grendzy |
#26 | 278770_0.patch.txt | 7.39 KB | grendzy |
#22 | 278770.patch | 7.39 KB | grendzy |
Comments
Comment #1
pounardI forgot to say what is really annoying about this for my use. Image and ImageField modules uses this method a lot, and I don't need absolute URL for images.
In the majority of cases, where files are somewhere in the docroot, I don't see the need of absolute URL at all. May be the files dir can be outside of the base_path(), case in which my patch seems to be broken, in this case another way to construct the URL is needed.
But the fact is, I need relative URL for images.
Errata:
When looking to the File System administration page, we can see: A file system path where the files will be stored. This directory has to exist and be writable by Drupal. If the download method is set to public this directory has to be relative to the Drupal installation directory, and be accessible over the web..
This makes absolutely obsolete to force absolute URL for public files.
Second errata:
I saw that $base_url is written from the HTTP_HOST user imput, which permits any proxy to rewrite this input in one way (from the user to Apache), but in some case with weird DNS handling (In my company we don't have admin rights on DNS entries, so we can't tell Apache to have an external DNS virtual host, for security reasons), absolute URL break all the proxies pass-through process.
Comment #2
c960657 CreditAttribution: c960657 commentedSee also #207310: Allow rewriting of file URL's using custom_url_rewrite_outbound(). This will allow you to make the generated URLs relative by setting $options['absolute'] to FALSE in your custom custom_url_rewrite_outbound().
Comment #3
pounardThanks.
Comment #4
Xen CreditAttribution: Xen commentedWhile the custom_url_rewrite_outbound() solution works for getting things done, it doesn't fix the real problem.
That this function returns a relative or absolute path depends on whether you're using public or private downloads doesn't make any sense, and either breaks code or forces developers to take it into account in their modules.
I suggest that the function gets changed to return relative paths for private downloads:
return url('system/files/'. $path);
Comment #5
Damien Tournoud CreditAttribution: Damien Tournoud commentedWell, just set the correct $base_url if Drupal can't guess the correct one.
Comment #6
Xen CreditAttribution: Xen commentedBumping up to current version.
Comment #7
Xen CreditAttribution: Xen commentedDamien:
The problem isn't the $base_url, the URLs are quite all right, the problem is that they're absolute (starts with 'http://', not relative (starts with '/system/files').
This not only breaks locale.module (#250451), possibly color.module (I suspect), proxys but also makes the life difficult for those using private downloads in general.
Using public downloads isn't an option in our case, as we're using private downloads because the file directory is set to a PHP stream handler that stores the files on Amazon S3. Apart from putting storage outside, it also means that we're able to run a cluster of Drupal installations without having to NFS mount file directories everywhere (which is a blessing when the servers is hosted different places).
It seems that the 'private is absolute' problem goes back to at least 4.7, but I haven't been able to find a reason for it. And it seems to break existing modules (even in core!) because they've only been tested with public downloads. So fixing file_create_url would actually *fix* multiple modules.
Comment #8
naxoc CreditAttribution: naxoc commentedsubscribing
Comment #9
pounard+1
Comment #10
Xen CreditAttribution: Xen commentedAfter some fiddling about I've found that the proper way to get a 'local' URL is:
While this is pretty strait forward, it might be wise to make this part of file_create_url, or creating a file_create_relative_url helper function, or documenting it better.
Comment #11
Murzsubscribe. Xen.dk, thanks for solution!
Comment #12
neochief CreditAttribution: neochief commentedsubscribing
Comment #13
grendzy CreditAttribution: grendzy commentedMoving to 7.x. A new patch is attached. I also changed the default value to $absolute = FALSE, which is more logical and consistent with previous behavior, IMO (since relative is the default in l() and url(), and relative URLs generally seem to be favored in Drupal).
BTW there's some other work on file_create_url over here: http://drupal.org/node/238299
Comment #14
drewish CreditAttribution: drewish commentedsubscribing
Comment #15
webchickYay! I like this patch very much. :) I ran into this headache with theme_image() (see #238681: theme_image() not evaluating properly?.
Is there a reason that's in parentheses?
Also... we need some tests. :)
Comment #16
webchickHm. Further, this is not giving me the output I expect. What I want out of file_create_url() is system/files/cat.jpg. What I'm getting instead is /drupal/system/files/cat.jpg. (Drupal is installed in a subdirectory, "drupal.") Mind you, this is much better than http://localhost/drupal/system/files/cat.jpg I was getting before that caused theme_image() to choke.
I confirm that #238681: theme_image() not evaluating properly? solves this issue, but I'd love to fix this in file_create_url() instead, if we can.
Some rough steps to reproduce the behaviour I'm experiencing without this patch:
1. Go to Administer >> Site configuration >> File management
2. Set your File download type to Private, and move your files directory outside of webroot.
3. Stick an image in there, such as cat.jpg
4. In your theme somewhere, put:
5. Notice that there is nothing printed between the div tags.
6. Bang face into desk for 25 minutes.
Comment #17
drewish CreditAttribution: drewish commentedwebchick, i'd be great if we could get #329226: Store file_test.module's values in variables rather than globals committed so that we could then get #238299: file_create_url should return valid URLs which already has some file_create_url() tests written.
Comment #18
grendzy CreditAttribution: grendzy commentedHmm. Well this is sticky -- I see why you would want system/files/cat.jpg -- so that you have something that can be passed into url() and l(). This won't really work either though -- because if you are using public files, and non-clean URLs, what you will get is this:
/index.php?q=sites/default/files/cat.jpg. Which is no good.
It might not be possible to fix this without some bigger changes.
Other ideas?
Comment #19
Xen CreditAttribution: Xen commentedwebchick:
I'd suggest that what you want should be obtainable with another new function. As I see it, file_create_url returns a path that's directly usable in html output, while what you want is something akin to Drupal paths (like "node/3").
Really, I'd expect (from the name) that file_create_path to do something like that, but it has a different purpose.
Comment #20
grendzy CreditAttribution: grendzy commentedOK, here's a new patch, including a unit test. I believe the test covers all the possible permutations of public / private files, and well as files inside and outside the docroot.
Here's the basic idea:
This will probably have a small performance impact for those sites with clean urls disabled. My intuition is that the performance hit will be very small, and also the number of sites running without clean URLs is small.
Comment #22
grendzy CreditAttribution: grendzy commentedComment #24
pounardI saw the system message that says the patch don't patch anymore.
What is the status of this bug? Is there someone that will one day or another, make the choice of using relative url as default behavior for this function ?
I have have a lot of problems with D5 and D6 behind proxies or load balancers, and patching the core each new release which comes out is really anoying, and I think it's the same for a lot of people.
In all cases, Drupal 7 has no stable release today (or no public one has I saw), may be it's the right time to apply it upstream, else it will probably wait for Drupal 8, then 9, then 10... Plus the behavior of url() function is per default to always make relative urls, this is an inconsistency in Drupal API that the file_create_url() has a different behavior.
Comment #25
grendzy CreditAttribution: grendzy commentedpounard, thanks for your feedback. I'm still working on this. Regarding the system message, I'm a little stuck on that. The patch passes all the tests on my system, but not on Drupal's test system. After pondering this a bit more, I plan to try another patch that gets rid the proposed file_check_private and lets url() handle everything, which should also help #238681.
Comment #26
grendzy CreditAttribution: grendzy commentedComment #27
grendzy CreditAttribution: grendzy commentedNew approach:
This patch enhances url() to understand files. This means file_create_url() is no longer needed.
I believe this will fix #207310: Allow rewriting of file URL's using custom_url_rewrite_outbound() and #238681: theme_image() not evaluating properly?.
Comment #29
grendzy CreditAttribution: grendzy commentedKeeping up with head. Also splitting the test into a separate patch, because of a glitch with the test bot.
Comment #31
Wim LeersSubscribing.
Comment #32
grendzy CreditAttribution: grendzy commentedfile_create_url has been reworked with the streamwrapper patch: #227232: Support PHP stream wrappers
Comment #33
_grizly CreditAttribution: _grizly commentedSurely simplest is best:
if needed in a function:
This way, if the drupal installation happens to exist outside the root dir, relative URI's will still work.