The function image_style_url() is setting a cache entry which will later be checked by image_style_generate() to grant access. However, if the file already exists then the cache entry is not set which results in an access denied message if this happens on a page that got the cache cleared.

CommentFileSizeAuthor
#1 image_style_url_d7_827206.patch1.13 KBjurgenhaas

Comments

jurgenhaas’s picture

Status: Active » Needs review
StatusFileSize
new1.13 KB

Applied patch should resolve this.

dawehner’s picture

This looks fine for me. But we might should be able to test this.

function image_style_url($style_name, $path) {
  $destination = image_style_path($style_name, $path);

  // If the image already exists use that rather than regenerating it.

  if (file_exists($destination)) {
    return file_create_url($destination);
  }

This code stops to run and never set the cache when $destination does exists.

andypost’s picture

Status: Needs review » Closed (works as designed)

I think current implementation is right because logic a bit different.

image_style_url() sets cache and URL to 'image/generate/' (causing to call image_style_generate()) and disables page cache only for first time when derivated image will be generated.

So cache entry exists a small amount of time when URL points to image_style_generate() ONLY when derivated image should be regenerated or created.

But when image already exists the image_style_generate() should never be executed!

jurgenhaas’s picture

Status: Closed (works as designed) » Active

Well, I'm not quite sure I could agree with this, unless there are different functions for something similar elsewhere.

Calling image_style_url() returns a URL to an image which can be included as [img src="image/generate/.../.../name.jpg"] in the data somewhere and this will display the right image regardless whether this exists already or not. You can be sure it will be generated if it didn't exist at the time you needed it.

But that's where the problem begins: you have that URL "inage/generate/..." in your html data and that calls image_style_generate() which returns an access_denied() even if the required image does exist.

This scenario doesn't make sense to me and I wonder if I'm using this in a wrong way and hope to get some suggestions from you guys on how to do this in a better way.

andypost’s picture

But that's where the problem begins: you have that URL "inage/generate/..." in your html data and that calls image_style_generate() which returns an access_denied() even if the required image does exist.

@jurgenhaas could you explain a case when image_style_generate() will return access_denied() ? Could you write a test for this case?

As I said above: 'image/generate/...' used ONLY when no image exists and this html is not cached!

So image_style_url() used to return URL and this links could be DIRECT and 'image/generate/...' IN second case this sets cache causing image_style_generate() to generate image so all other calls to image_style_url() will return DIRECT links to file

jurgenhaas’s picture

The test case is simply a node which contains

   <img src="http://www.example.com/image/generate/[style]/[scheme]/folder/name.jpg">

You can see an example here: http://www.sorina-nwachukwu.de where all images are coded that way.

This is *always* throwing an access denied because of the line

  if (!$style || !cache_get('access:' . $style_name . ':' . $path_hash, 'cache_image')) {

in image_style_generate(). This is why I call image_style_url() for each image before delivering the page, but that only works if the cache_set() is called earlier as described above.

catch’s picture

Title: image_style_url() is setting cache too late » image_style_url() should not be used to generate HTML output
Priority: Critical » Normal

I think the issue here is you should not be embedding those links in HTML. It looks like re-using the code from theme_image_style() would both ensure the preset is generated if it doesn't exist, and also give you a url which will always work.

http://api.drupal.org/api/function/theme_image_style/7

Downgrading this since I'm sure this works fine otherwise, but we should document that the function should never be used directly to generate HTML output.

jurgenhaas’s picture

OK, that's what I thought. It was quite handy to use that function but I expected that to be the wrong way.

catch’s picture

Title: image_style_url() should not be used to generate HTML output » image_style_url() only prevents page caching
Priority: Normal » Critical

I looked at this a bit closer, and I'm putting it back to critical.

image_style_url() prevents stale links from being created via drupal_page_is_cacheable(FALSE), however that's not the only situation where HTML might be cached:

* block caching
* render caching
* panels / views caching
* something like http://drupal.org/project/inline or http://drupal.org/project/filefield_insert where the filter output might be generated with an image/generate path but then cached in the filter module or by text module.

In all of those cases, it'd be possible to cache an image/generate path in the HTML - in fact it's quite likely that you have cache miss for HTML at the same time as a miss for the image style.

I can't think of many ways around this except for adding something like drupal_html_is_cacheable(), which all HTML caching methods could then check before trying to cache_set(), and image.module could use when generating images.

moshe weitzman’s picture

I don't understand image_style_url(). Why do we have different URLs for the case when we need to generate versus the case when we don't. Isn't the whole point to generate an image iff we got a miss? If we can unify on a single URL, then it won't matter if it gets cached.

eojthebrave’s picture

I believe the reason for having two URLs is so that once a derivative image has been generated we can just link to it directly and let Apache deal with serving the image rather than getting Drupal involved for every image request on the page every single time.

Anonymous’s picture

i'm with moshe in not understanding this.

imagecache in D6 relied on our mod_rewrite rules in .htaccess, which will only pass the request to Drupal if the file doesn't exist.

so, the url for a style would always be the same. if it the path points to a file that doesn't exist, imagecache generates the image and serves it, but all other requests are just served directly. if that's not the case now in core, something is very wrong.

tstoeckler’s picture

So instead of what we have now at http://api.drupal.org/api/function/image_style_url/7 we would be doing something like (hypothetical):

function image_style_url($style_name, $path) {
  $destination = image_style_path($style_name, $path);

  // If the image already exists use that rather than regenerating it.
  if (file_exists($destination)) {
    return file_create_url($destination);
  }

  // Disable page cache for this request. This prevents anonymous users from
  // needlessly hitting the image generation URL when the image already exists.
  drupal_page_is_cacheable(FALSE);

  // Prepare the $style and $source variables
  [...]

  // Generate the image.
  $destination = image_style_create_derivative($style, $source, $destination);

  return file_create_url($destination);

???

tstoeckler’s picture

Actually that might not be so good in case you are viewing many images (gallery) and have just flushed a preset, as you would be recreating every single derivative on one page view. Hmm...

Anonymous’s picture

ok, i had a look at the code here, and, well, it just looks wrong now we have a locking framework. Damien informed me that image.module went in before we had a locking framework, but now we have one, i think we should move to something like the way imagecache does it.

that is, image.module should claim any paths its needs to via image_menu(), so that there's no difference between the url that generates a derivative and serves it from disk.

i'll try to ping dries and webchick to see if there's any chance we can fix this for D7.

moshe weitzman’s picture

MW: Skip this comment. It's just wrong and confusing

I don't think we can keep the current approach for D7. We have lost one of the main advantages of imagecache and thats that derivitave building can no lo lober be off-loaded to a separate web server like media.example.com. With the current code, we force all webservers to generate derivitaves. Once again, the fix is to generate derivitaves during the image request not the original HTML request.

catch’s picture

As far as I can see we're still creating derivatives during the image request - the issue is that the url generated for the Only local images are allowed. tag differs depending on whether the file is found or not.

moshe weitzman’s picture

oops. ignore my dumb-ass comment there. thanks catch.

this is still a mess tho.

Anonymous’s picture

spoke to quicksketch about this in #drupal. we generate at one url, and serve the image at another because we can't make image.module depend on mod_rewrite.

that's a huge bummer, but we're stuck with it.

so, we're back to solving the side effects caused by this.

Anonymous’s picture

Status: Active » Postponed

postponing this issue in favour of #851878: serve image derivatives from the same url they are generated from. we may just be able to kill this issue once the other one goes in.

damien tournoud’s picture

Status: Postponed » Closed (duplicate)
mstrelan’s picture

This is closed a duplicate of itself?