[contentbox] corrupted cfcontent jpeg when querystring exists

This is an odd issue, and I can’t see why it would be ColdBox/ContentBox related, except that I can’t reproduce it outside of them, so… shrug

We have an application using a modified version of ContentBox on ColdBox 3.8.1 and the media serving is broken, possibly as a result of being on ColdFusion 11. (It was definitely working on ColdFusion 10, but the code doesn’t run on CF10 anymore so I can’t confirm if that’s a factor. It’s also not possible to run the application without IIS being involved, so I can’t entirely rule that out either.)

The issue is that having a query string is somehow corrupting the response for a cfcontent - I don’t know whether this is being done by the app or by CF/Tomcat/IIS, but instead of a 767KB JPG it is returning a 1320 byte file which starts with a JPG magic number (first four bytes) and then completely different data, causing Firefox to complain:

the image … cannot be displayed because it contains errors.

To re-iterate, using the exact same code, requesting this URL works:

http://localdomain/__media/Jellyfish.jpg

Whilst this returns the invalid data:
http://localdomain/__media/Jellyfish.jpg?nocache=12345

Any non-empty querystring will do it. It occurs with other files and on another server (both with CF11 update 2).

The code serving the image is from ContentBox:
https://github.com/Ortus-Solutions/ContentBox/blob/master/modules/contentbox-ui/handlers/media.cfc
deliverMedia - https://github.com/Ortus-Solutions/ContentBox/blob/master/modules/contentbox/model/media/CFContentMediaProvider.cfc
sendFile - https://github.com/Ortus-Solutions/ContentBox/blob/master/coldbox/system/core/util/FileUtils.cfc

I have tried updating the media handler to just do the following after line 43:
var realPath = mediaService.getCoreMediaRoot( absolute=true ) & “/#prc.mediaPath#”;
cfcontent( reset=true , file=realPath );abort;

The issue still occurs using that code, which is what makes me think CF11 is at fault - there’s no code that should interfere or differentiate between QS/non-QS.

It’s not just the media handler, it seems to also happen for filebrowser’s download handler (which uses fileutils.sendFile), i.e this also gives the corrupt image: http://localdomain/cbFileBrowser/download?path=/Jellyfish.jpg

If I instead use http://localdomain/othershowoff.dev/cbFileBrowser/download/path/%2FJellyfish.jpg it returns the correct file, and likewise if I hardcode the path in the download function and remove the QS then it sends the correct file, so it is having url parameters or a querystring that cause the problem, but I can’t figure it out beyond that.

I’ve copied the FileUtils sendFile function into a standalone file and it works fine there - QS or not the correct file is returned, whether going direct to CF11 or via IIS.

So yeah, if I’m missing something obvious or someone has encountered this before that’d be great to know, but if nothing else this will get indexed by search engines in case someone else has the same problem…

Peter,

That really does sound weird, but there is a few places you don’t make sense. I am getting the impression that if there is no QS it works, but you don’t indicate whether you tried this on the direct path.

But I am assuming you may have.

So a couple of questions, you say you’re using IIS, but you don’t say what rewrite rule extension you’re using either, as it appears from the URL that you are. So with that in mind, can you have a look at the rewrite rule for __media and see if there is an issue with that. It might also be a bug in the handler for this route, it might be setting the info from the QS wrong.

And lastly, you talk about Firefox, but you don’t say if this is related only to Firefox or not!

I am getting the impression that if there is no QS it works,
but you don’t indicate whether you tried this on the direct path.

There is no direct path - the file is stored outside the webroot.

These URLs return the 775,702 byte Jellyfish image, either in the browser viewport or as a download.

http://localdomain/__media/Jellyfish.jpg
http://localdomain/__media/Jellyfish.jpg?
http://localdomain/cbFileBrowser/download/path/%2FJellyfish.jpg

These URLs return a corrupt file 1,320 byte file.

http://localdomain/__media/Jellyfish.jpg?nocache=12345
http://localdomain/__media/Jellyfish.jpg?anything
http://localdomain/cbFileBrowser/download?path=/Jellyfish.jpg

(And again, Jellyfish is just the image I’m testing with; it’s a Windows sample file so no problem with the file, the issue occurs with any image file tried.)

you say you’re using IIS, but you don’t say what rewrite rule extension you’re using either

IIS 7.5.7600.16385 with the official Microsoft URL Rewrite module.

The rewrite rule is this:

The rewriting hasn’t changed since when it was working, so shouldn’t be a factor.

There is no __media rule in IIS, it is routed to the media handler in contentbox-ui/ModuleConfig.cfc (and this hasn’t changed in any relevant way recently).

It might also be a bug in the handler for this route, it might be setting the info from the QS wrong.

I went through putting dumps at every stage - I couldn’t see any difference in what any of the functions were doing with variables when QS was there or not - it has the correct filename / filesize / etc at every stage, and my cfcontent in the media.cfc handler index function should have identified if there was any bug.

(There does seem to be a fair bit of code for just passing a file back, but that’s a separate issue, and it was working before.)

you talk about Firefox, but you don’t say if this is related only to Firefox or not!

Oh, I mentioned the error Firefox gives because that is where I first thought the issue was (and probably the first thing someone else who encounters this will search for).

It’s not a browser issue though - in that it also occurs in Chrome and IE. (Those two don’t give an error message though, just a broken image icon).

Oh, and this isn’t image-specific - I tried with a 70,666 byte PDF file and it returns as an 824 byte file when the QS is added. The corrupt file has the first 380 bytes the same as the original, but the rest different (and is not a valid PDF file).

Ok, so I just tried with index.cfm in the URL and it appears it may be IIS/rewrite-related (even though this hasn’t changed, AFAIK).

There are no (visible) differences to the CGI scope when going via index.cfm or the rewritten version, but adding index.cfm does work with a QS attached…

Broken:
http://localdomain/__media/Jellyfish.jpg?dfgfd

Works:
http://localdomain/__media/Jellyfish.jpg
http://localdomain/index.cfm/__media/Jellyfish.jpg
http://localdomain/index.cfm/__media/Jellyfish.jpg?dfgfd

So, any idea where I can check to see if IIS has been updated or any settings changed that are causing this?

Ok, I say this often and I will say it once more. The logger is very good at getting to the bottom of this, in this case it would have indicated that the path is indeed wrong. When I meant the full path, I referred to __media resolved path as this is a url mapping to hide the true location of the media.

Now because of this, your are indeed missing this. (.htaccess)

RewriteCond %{REQUEST_URI} /__media/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.cfm/%{REQUEST_URI} [QSA,L,NS]

Huh?

The existing rewrite rule works, has worked for months, and hasn’t changed.

IIS is passing all relevant requests to CF.

If I put this line of code in:

writeDump(top=3,var=CGI,abort=1);

I get IDENTICAL values for both of these two URLs:

http://localdomain/cbFilebrowser/file/download?fileid=3
http://localdomain/index.cfm/cbFilebrowser/file/download?fileid=3

However, when I put the following cfcontent before the CGI dump, the behaviour differs:

cfcontent(file=FileService.get(rc.FileId).getLocalPath());abort;

The differing behaviour is not a 404 or rewrite failure, it is a corrupt file - but whatever is changing the file is aware of the file type.

When I replaced the above cfcontent with this:

FileWrite(‘C:\tmp#createUuid()#’,FileRead(FileService.get(rc.FileId).getLocalPath()));abort;

I get the file correctly - which means the corruption is occurring in the outgoing Response.

There are no Outbound rewrite rules setup. Maybe there’s some filter hidden in IIS or Tomcat that is malfunctioning.

​If you’re using contentbox, then the __media rewrite rule needs to be setup. It’s rewrite rule resolves to what you have listed above, but it sounds like something else is going on.

If you use the debug tools in Firefox, what is actually being returned, can you see anything that sticks out in the response headers?​

Other than different Content-Length settings, the corrupt one has a “Connection: close” response header that the other doesn’t have. Not sure if that’s relevant or just a side-effect.

HTTP/1.1 200 OK
Cache-Control: no-cache, no-store, must-revalidate, max-age=0
Pragma: no-cache
Content-Length: 592
Content-Type: image/jpeg;charset=UTF-8
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Server: Microsoft-IIS/7.5
Set-Cookie: DEFAULTLOCALE=en%5FGB; Path=/
Connection: close

HTTP/1.1 200 OK
Cache-Control: no-cache, no-store, must-revalidate, max-age=0
Pragma: no-cache
Content-Type: image/jpeg;charset=UTF-8
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Server: Microsoft-IIS/7.5
Set-Cookie: DEFAULTLOCALE=en%5FGB; Path=/
Content-Length: 268309

I think I’m going to try setup nginx to see if that at least narrows it down to being IIS or CF.

That’s certainly an odd issue Peter. The main suggestion I have, it sounds like you’re already doing, which is to eliminate pieces of the puzzle to help narrow it down.

  • Do you have any network appliances that scan web traffic like WAFs or anti-virus kind of stuff. I’ve seen them screw up HTTP responses before
  • Try hitting the URL from the server itself to bypass any other parts of the network
  • Try hitting Tomcat directly on it’s HTTP listener port (8080, etc) to bypass IIS. You’ll need the index.cfm in there.
  • Use a packet sniffer on the web server to verify if the HTTP responses are different when they leave the web server, or being modified somewhere down the line
  • Try cfcontent from a simple ColdBox app to rule out ContentBox

Thanks!

~Brad

ColdBox Platform Evangelist
Ortus Solutions, Corp

E-mail: brad@coldbox.org
ColdBox Platform: http://www.coldbox.org
Blog: http://www.codersrevolution.com

Thanks for the ideas Brad.

Do you have any network appliances that scan web traffic like WAFs or anti-virus kind of stuff.

We do have some, but this is occurring on both staging and my local dev machine.

Guess it could still be local AV doing it, so I’ll check that out.
(I remember when gzipped HTML was screwed up by Norton - we’re not using that junk, but could still be something similar happening.)

Try cfcontent from a simple ColdBox app to rule out ContentBox

Yeah, that’ll be a good test too.

Thanks.

A quick update - I reproduced it without ColdBox in the picture (but still with IIS and rewrite rules) so it’s not a ColdBox/ContentBox issue.

It doesn’t occur with an nginx rewrite+reverse proxy, so I tried IIS with reverse proxy too and I don’t see it there either.

This seems to leave the CF/IIS connector as the culprit - and indeed after I swap isapi_redirect.dll with CF10’s version (and restart both CF/IIS) the issue goes away.

Bug raised: https://bugbase.adobe.com/index.cfm?event=bug&id=3853535

That almost sounds like the connectors are not installed properly with CF11, so the question is do you have one of these as the all? I would suggest removing all connectors for cf10 and cf 11 and install the ones needed for each site individually for each version and see how you go.

Good work nailing that down. This is the second thread this week pointing toward possible bugs in CF11’s IIS connector.

Thanks!

~Brad

ColdBox Platform Evangelist
Ortus Solutions, Corp

E-mail: brad@coldbox.org
ColdBox Platform: http://www.coldbox.org
Blog: http://www.codersrevolution.com