In the 'default' case in drupal_get_js appends a cache-busting query string to URLs in an attempt to get browsers to reload pages when page caches are updated. This is fine, except for the fact that URLs added by modules may already have query-strings as part of the URI.

For instance, when using the Gallery module and navigating to the Gallery plugin management page, this URI is added via drupal_add_js():

/index.php?q=gallery&g2_view=core.CombinedJavascript&g2_key=cd60d0c257ddd99cc07f4efafd8004b0

drupal_get_js() appends "?1207482595". Since the URI above already has a query-string, the ? should be replaced with a &.

In some circumstances, this usage of ? instead of & causes actual breakage because the webserver chooses to disregard all of the query parameters after the first ? and only acknowledges the ones after the second ?.

Attached is a patch which will test the URL before appending the query string and determine which parameter should be used.

Comments

Signe’s picture

StatusFileSize
new3.44 KB

Apply the same logic to drupal_get_css(). I haven't seen an issue here, yet, but it's begging for it to occur.

sun’s picture

Title: drupal_get_js appends timestamp querystring without any regard to whether the request already has a query-string » drupal_get_js appended querystring breaks certain JS
Version: 6.1 » 7.x-dev
Priority: Normal » Critical

+1

Arbitrary query strings can also break certain JavaScripts, for example TinyMCE, as mentioned in http://drupal.org/node/218296#comment-810963

The workaround for now is to set the $cache parameter to FALSE -- however, this results in browsers not caching the JavaScript at all.

So, in addition to those patches here, we badly need a way to force no query string at all for certain drupal_add_js() calls.

Marking as critical.

wretched sinner - saved by grace’s picture

I also came across this issue in Drupal 6 - see #242875: No way to disable 'cachebuster' query-string appended to CSS file links.. Have marked that as a duplicate of this one.

jcsiegrist’s picture

Version: 7.x-dev » 6.x-dev

This issue also breaks language pack loading for the tinyMCE module. The Language packs javascript url returns a drupal 404 error, thus the javascript is never loaded. This should be patched for drupal 6.x as well.

sun’s picture

Version: 6.x-dev » 7.x-dev

Bugs are fixed in HEAD first.

lilou’s picture

StatusFileSize
new4.06 KB

Reroll for D7.

sun’s picture

Title: drupal_get_js appended querystring breaks certain JS » Query string appended drupal_add_js breaks certain scripts
Status: Needs review » Needs work

For D7, I'd suggest to allow developers to pass $options = array('query' => FALSE) in the new $options array to disable the query string for certain JS files only.

Also, this issue unfortunately seems to deal with two issues:

a) Original poster: Drupal's query string is not properly added to an existing query string, i.e. myfile.js?arg1=value?B. There are two ? in the resulting URL. The original poster needs a way to properly assign a query string, f.e.

drupal_add_js($filename, array('query' => 'arg1=value'));

b) Drupal's query string breaks the execution of certain scripts, i.e. myfile.js?B, is sufficient to break the execution of certain scripts (perhaps in specific browsers only, dunno). Since setting the 'cache' option to FALSE results in yet another query string ($info['cache'] ? $query_string : '?' . REQUEST_TIME), there should be an option for developers to disable Drupal's query cache string for certain scripts only, f.e.

drupal_add_js($filename, array('query cache' => FALSE));
sodafox’s picture

Version: 7.x-dev » 6.6
Category: bug » support
StatusFileSize
new187.7 KB

I seem to have the same problem. I'm new to drupal and all the lingo. Here's a screen shot that explains what I get when I try to upload an image using Tinymce and the wysiwygAPI. I also had the same result when trying to get Image assist to work without the error messages displayed.
Also I've managed to get simple patches working before but I'm having trouble understanding which lines to add or remove on this one.

sun’s picture

Version: 6.6 » 7.x-dev
Category: support » bug

Bugs are fixed in the current development branch first.

@sodafox: Also, please do not change issue classification. The bug you are experiencing seems to be limited to using Wysiwyg API and Image Assist. If you were experiencing the bug of *this* issue, neither TinyMCE nor Image Assist would show up at all. Please file a new issue in Image Assist's queue (but search for existing issues first). Thanks.

sun’s picture

Title: Query string appended drupal_add_js breaks certain scripts » JavaScript requiring a query string cannot be loaded

Better title. Point 2) of my last summary was already fixed elsewhere.

mcarbone’s picture

Status: Needs work » Needs review
StatusFileSize
new2.48 KB

Re-rolled to keep up with HEAD.

creslin’s picture

I concur - this is not useful at all.
First it's obfuscated, most developers who

* write file.css
* add file.css to theme.info

Expect file.css to be called from the browser - not file.css?[a-z]

Secondly this has silently bust our mod_deflate in Apache for all CSS's served - meaning 10x the bandwidth and latency implicit in downloading a larger file.

Third, content switching above Drupal hosts to pull css/media from a.another server is now bust - all content is being served from our dynamic host. I simply cannot configure our Layer5 Load balancers to manage a ? in the GET to determine which server to get the data from.

How do I switch this madness off.

- Cres

mcarbone’s picture

I think you misunderstand this thread. It's not to add the ability to turn off query parameters completely, but to fix a bug when an included file already includes a query parameter. If you want to add an option to disable that functionality completely, I suggest you create a new issue.

sun.core’s picture

#11: 243251_query_string.patch queued for re-testing.

sun.core’s picture

RTBC if bot passes, please.

wretched sinner - saved by grace’s picture

Status: Needs review » Reviewed & tested by the community

Based on @sun above

sun’s picture

Actually, I was mistaken and meant another patch in the queue, but this one looks good, too. ;)

webchick’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs tests

We should get some tests for this. This is the very definition of something we will easily break in the future trying to "optimize" this part of the code and not notice when the regression is introduced.

naxoc’s picture

Status: Needs work » Needs review
StatusFileSize
new6.51 KB

I wrote a small test that checks that css and js files with an appended querystring will display correctly in the inclusions.

When I ran the test I saw that locale got upset with urls that have an appended query string when it is translating js files, so I fixed that.

I also had to do some alterations to the patch because of #228818: IE: Stylesheets ignored after 31 link/style tags. I had to put a check_plain() on the file inclusion on @import css rules. I am not sure that is cool, but it works. I did some research on whether @import should display urls escaped or not. I did not really find anything, but it is considered good practice that link tags escapes urls, so I figured the same was true for @import.

sun’s picture

+++ includes/common.inc
@@ -3037,7 +3037,8 @@ function drupal_pre_render_styles($elements) {
+            $qs_sep = (strpos($item['data'], '?') !== FALSE) ? '&' : '?';
+            $import[] = '@import url("' . check_plain(file_create_url($item['data']) . $qs_sep . $query_string) . '");';

@@ -3058,7 +3059,8 @@ function drupal_pre_render_styles($elements) {
+            $qs_sep = (strpos($item['data'], '?') !== FALSE) ? '&' : '?';
+            $element['#attributes']['href'] = check_plain(file_create_url($item['data']) . $qs_sep . $query_string);

@@ -3674,7 +3676,8 @@ function drupal_get_js($scope = 'header', $javascript = NULL) {
+          $qs_sep = (strpos($item['data'], '?') !== FALSE) ? '&' : '?';
+          $js_element['#attributes']['src'] = file_create_url($item['data']) . $qs_sep . ($item['cache'] ? $query_string : REQUEST_TIME);

$qs_sep still needs a proper variable name.

+++ modules/simpletest/tests/common.test
@@ -682,6 +682,16 @@ class CascadingStylesheetsTestCase extends DrupalWebTestCase {
+   * Tests that the query string remains intact when adding css files that have
...
+    $this->assertRaw(drupal_get_path('module', 'node') . '/node.css?arg1=value1&arg2=value2&' . $query_string, t('Query string was appended correctly to css.'));

In comments and messages, CSS should always be uppercase.

+++ modules/simpletest/tests/common.test
@@ -1255,6 +1265,16 @@ class JavaScriptTestCase extends DrupalWebTestCase {
+   * Tests that the query string remains intact when adding js files that have
...
+    $this->assertRaw(drupal_get_path('module', 'node') . '/node.js?arg1=value1&arg2=value2&' . $query_string, t('Query string was appended correctly to js.'));

Ditto for JS. Additionally, we don't abbreviate JavaScript (note the special casing)

+++ modules/simpletest/tests/common_test.module
@@ -183,3 +189,12 @@ function common_test_library() {
+ * Adds a js file and a css file with a query string appended.

Ditto.

+++ modules/simpletest/tests/common_test.module
@@ -183,3 +189,12 @@ function common_test_library() {
+   return 'Drupal is awesome';

Unless I overlooked something, this string is not used by tests, so we should just return an empty string here.

139 critical left. Go review some!

naxoc’s picture

StatusFileSize
new6.61 KB

Reroll with changes from review :)

sun’s picture

+++ includes/common.inc
@@ -3037,7 +3037,8 @@ function drupal_pre_render_styles($elements) {
+            $import[] = '@import url("' . check_plain(file_create_url($item['data']) . $query_string_separator . $query_string) . '");';

@@ -3058,7 +3059,8 @@ function drupal_pre_render_styles($elements) {
+            $element['#attributes']['href'] = check_plain(file_create_url($item['data']) . $query_string_separator . $query_string);

@@ -3674,7 +3676,8 @@ function drupal_get_js($scope = 'header', $javascript = NULL) {
+          $js_element['#attributes']['src'] = file_create_url($item['data']) . $query_string_separator . ($item['cache'] ? $query_string : REQUEST_TIME);

1 of 3 doesn't use check_plain() though - any not-so-obvious reason for that?

139 critical left. Go review some!

naxoc’s picture

You are right, it is not so obvious...
The js one that does not use check_plain is drupal_get_js(). That function uses theme_html_tag, that calls drupal_attributes and that runs check_plain.

I wonder if it is OK that it is the responsibility of the theming layer to use check_plain?

damien tournoud’s picture

More precisely, it seems that this check_plain() is unnecessary, and will lead to double encoding:

$element['#attributes']['href'] = check_plain(file_create_url($item['data']) . $query_string_separator . $query_string);
naxoc’s picture

StatusFileSize
new6.6 KB

That is so true. I removed the checkplain on the link that is going in for theming with theme_html_tag

sun’s picture

Status: Needs review » Reviewed & tested by the community

Looks good to me.

dries’s picture

Committed to CVS HEAD. Thanks.

wretched sinner - saved by grace’s picture

Status: Reviewed & tested by the community » Fixed

Based on Dries' comment above...

pwolanin’s picture

Version: 7.x-dev » 6.x-dev
Status: Fixed » Patch (to be ported)

probably needs to be backported.

david strauss’s picture

@creslin

Secondly this has silently bust our mod_deflate in Apache for all CSS's served - meaning 10x the bandwidth and latency implicit in downloading a larger file.

That's a configuration problem on your end:

[straussd@web3 public_html]$ cat .htaccess 
<FilesMatch "\.css$">
  SetOutputFilter DEFLATE
  Header set X-Ping "Pong"
</FilesMatch>
[straussd@web3 public_html]$ cat icanhasdeflate.css 
Aaaaaaaaaah
http://straussd.fourkitchens.com/icanhasdeflate.css?nocanhas=1

GET /icanhasdeflate.css?nocanhas=1 HTTP/1.1
Host: straussd.fourkitchens.com
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1.8) Gecko/20100202 Firefox/3.5.8
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive

HTTP/1.1 200 OK
Date: Fri, 05 Mar 2010 13:55:58 GMT
Server: Apache/2.2.3 (Red Hat)
Last-Modified: Fri, 05 Mar 2010 13:54:07 GMT
Etag: "be0cd5-c-4810e0c8dfdc0"
Accept-Ranges: bytes
Vary: Accept-Encoding
Content-Encoding: gzip
X-Ping: Pong
Content-Length: 25
Connection: close
Content-Type: text/css
----------------------------------------------------------
Third, content switching above Drupal hosts to pull css/media from a.another server is now bust - all content is being served from our dynamic host. I simply cannot configure our Layer5 Load balancers to manage a ? in the GET to determine which server to get the data from.

This also sounds like a configuration issue.

jimrockford’s picture

This might help someone attempting to use Google Closure in Drupal. I'm still on Drupal 6.14. I was trying to use drupal_add_js to source base.js from Google's closure library. Like all other such calls to include JS, Drupal appended "?s" to the filename (i.e.,
/base.js?s). The result was that none of my goog.require() statements were working. Any code referencing google closure library vars resulted in undefined. Basically, base.js seemed to NOT be downloaded, and so goog namespace was unknown and then all goog.require() calls failed. Only when I added the lines shown below directly in head of page.tpl.php (sans the "?s" appended to the filename), and quit using drupal_add_js() for closure JS files, did closure start working and all the undefined errors went away.

With my page.tpl.php customized like this, my desired google closure code started working:

<head>
...
  <script src="/drupal-6.14/misc/closure/goog/base.js"></script>
  <script>goog.require('goog.json');</script>
  <script>goog.require('goog.dom');</script>
  <script>goog.require('goog.ui.TableSorter');</script>
  <?php print $scripts; ?>
</head>
dpearcefl’s picture

Priority: Critical » Normal
Status: Patch (to be ported) » Postponed (maintainer needs more info)

Is there any interest in this issue? Has this issue been fixed in the latest D6?

sun’s picture

Version: 6.x-dev » 7.0-beta1
Status: Postponed (maintainer needs more info) » Closed (fixed)

I don't think this will be backported.

aaronbauman’s picture

Version: 7.0-beta1 » 7.x-dev
Status: Closed (fixed) » Active

This is still broken for drupal_add_js with type "file".
file_create_url() url encodes the javascript item's "data" property, breaking the url if it includes a query string.
Works fine for "external" javascripts.

The test included in the patch above was simplified to remove the js's query string by #829822-25: Check Drupal 7 core for vulnerabilities in SA-CONTRIB-2010-066, in 2d3af8fe.

  • Dries committed c376055 on 8.3.x
    - Patch #243251 by naxoc, Signe, lilou, mcarbone: JavaScript requiring a...

  • Dries committed c376055 on 8.3.x
    - Patch #243251 by naxoc, Signe, lilou, mcarbone: JavaScript requiring a...

  • Dries committed c376055 on 8.4.x
    - Patch #243251 by naxoc, Signe, lilou, mcarbone: JavaScript requiring a...

  • Dries committed c376055 on 8.4.x
    - Patch #243251 by naxoc, Signe, lilou, mcarbone: JavaScript requiring a...

  • Dries committed c376055 on 9.1.x
    - Patch #243251 by naxoc, Signe, lilou, mcarbone: JavaScript requiring a...

Status: Active » Closed (outdated)

Automatically closed because Drupal 7 security and bugfix support has ended as of 5 January 2025. If the issue verifiably applies to later versions, please reopen with details and update the version.