IE11 caches AJAX requests #26922

Open
butonic opened this Issue Jan 11, 2017 · 42 comments

Projects

None yet

4 participants

@butonic
Member
butonic commented Jan 11, 2017 edited

Steps to reproduce

  1. In php.ini set session.cache_limiter= (emptystring) to disable sending any cache headers, oc sends them itself. if using this option PHP generates Pragma headers which may contradict what the Cache-Control headers used by ownCloud may say.
  2. click on the share link checkbox

Expected behaviour

see the generated link after the request has been received

Actual behaviour

see a spinner ... forever. refreshing the page should show the link in the list ... check the network requests (IE11 has an F12 like chrome or FF) ... see that it caches ajax get requests

Server configuration

Operating system: Win 10 & Win 7

Web server: Apache

Database: Mysql

PHP version: 5.6 & 7.0

ownCloud version: (see ownCloud admin page) 9.1.2 & master

Additional info

see #26922 (comment) for an example of cached requests

@butonic butonic added this to the 10.0 milestone Jan 11, 2017
@PVince81
Collaborator

Broken only in 10 ? In which version did it still work ? (for bisect)

@VicDeo VicDeo was assigned by PVince81 Jan 11, 2017
@PVince81
Collaborator

@VicDeo please have a look

@butonic
Member
butonic commented Jan 11, 2017 edited

ie 11, also broken in stable9.1

@CaptionF CaptionF added green-ticket and removed blue-ticket labels Jan 11, 2017
@VicDeo
Member
VicDeo commented Jan 11, 2017

@butonic win7 IE11. Can't reproduce on master

@PVince81
Collaborator

@VicDeo can you reproduce it on 9.1 ? Maybe something missing in the steps ?

@PVince81
Collaborator

@VicDeo can you switch to "index.php-less" mode https://doc.owncloud.org/server/9.1/admin_manual/configuration_server/index_php_less_urls.html ? From what I see in what @butonic posted it seems to be using that format. Who knows if it's maybe related.

@PVince81
Collaborator

Ah no, never mind, read the wrong link... The response shows https://asylum/oc/core/index.php/s/4iSz1ownieVsPtW

@PVince81
Collaborator

@VicDeo are you using the release tarball ? Maybe try with the enterprise version ?

Maybe there's another app's JS code interfering.

@VicDeo
Member
VicDeo commented Jan 11, 2017

@butonic does the response have proper json headers in your case? (just guessing)

@VicDeo
Member
VicDeo commented Jan 11, 2017

@PVince81 I will retry with 9.1.2 release in a minute

@PVince81
Collaborator

I also couldn't reproduce it on EE on SolidGear's docker.

@butonic anything special in the environment ?
This is with modern.ie's IE11 VM.

@PVince81 PVince81 added the needs info label Jan 11, 2017
@VicDeo
Member
VicDeo commented Jan 11, 2017

@butonic works as expected Win7 IE11, OC 9.1.2 EE complete on a different server

@butonic
Member
butonic commented Jan 11, 2017
Name Protokoll Methode Ergebnis Inhaltstyp Empfangen Zeit Initiator
https://asylum/oc/core/ocs/v2.php/apps/files_sharing/api/v1/shares?format=json HTTPS POST 200 application/json 618 B 154,05 ms XMLHttpRequest

Request headers:

Accept: application/json, text/javascript, */*; q=0.01
Accept-Encoding: gzip, deflate
Accept-Language: de-DE, de; q=0.5
Cache-Control: no-cache
Connection: Keep-Alive
Content-Length: 88
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Cookie: ocudr7mfk9d2=r8oqla99nu8v5a4eclrtk5o7t6; oc_sessionPassphrase=eWQcNyt8Pe5RSH2kEuq%2FpjwnwgPuubAFPCURnDYydZW3ibg2RFsAFCYn85eSdmCSfnJyLvTJ75BsSMuqfHAkmjlPYDFhrkFuZyGC3SuLsimybJqMKQweWDtn%2FYYZfvru
Host: asylum
OCS-APIREQUEST: true
requesttoken: LlAPXCpnLRpjNAkMJjcBdDIDVDMHJBYfZS4hDzItPhM=:tef2z1Ky+MEfsus6vD7IumqT5twmZNtwl/olG4wuSL0=
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko
X-Requested-With: XMLHttpRequest
   expireDate: 
   password: 
   passwordChanged: false
   path: %2FParis.jpg
   permissions: 19
   shareType: 3

response

Connection: Keep-Alive
Content-Length: 618
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; frame-src *; img-src * data: blob:; font-src 'self' data:; media-src *; connect-src *
Content-Type: application/json; charset=utf-8
Date: Wed, 11 Jan 2017 19:20:27 GMT
Keep-Alive: timeout=5, max=100
Server: Apache/2.4.18 (Ubuntu)
X-Content-Type-Options: nosniff
X-Download-Options: noopen
X-Frame-Options: SAMEORIGIN
X-Permitted-Cross-Domain-Policies: none
X-Robots-Tag: none
X-XSS-Protection: 1; mode=block

{"ocs":{"meta":{"status":"ok","statuscode":200,"message":null},"data":{"id":"42","share_type":3,"uid_owner":"admin","displayname_owner":"admin","permissions":1,"stime":1484162305,"parent":null,"expiration":null,"token":"nMKxyX0Y4yxKlDB","uid_file_owner":"admin","displayname_file_owner":"admin","path":"\/Paris.jpg","item_type":"file","mimetype":"image\/jpeg","storage_id":"object::user:admin","storage":5,"item_source":334,"file_source":334,"file_parent":321,"file_target":"\/Paris.jpg","share_with":null,"share_with_displayname":null,"url":"https:\/\/asylum\/oc\/core\/index.php\/s\/nMKxyX0Y4yxKlDB","mail_send":0}}}

after this the spinner just keeps spinning. reloading the page shows the link

hmmmName Protokoll Methode Ergebnis Inhaltstyp Empfangen Zeit Initiator
https://asylum/oc/core/ocs/v2.php/apps/files_sharing/api/v1/shares/43?format=json HTTPS DELETE 200 application/json 74 B 106,92 ms XMLHttpRequest
https://asylum/oc/core/ocs/v2.php/apps/files_sharing/api/v1/shares?format=json&path=%2FParis.jpg&reshares=true HTTPS GET 200 application/json (aus dem Cache) 0 s

IT seems the json requests are cached ... verifying.

When I force loading all requests from the server the reqests go to the server and work correctly.

@PVince81 @VicDeo do you have any caching headers in your requests?

also found this http://stackoverflow.com/questions/32261000/how-to-avoid-ajax-caching-in-internet-explorer-11-when-additional-query-string-p

I cleaned up my PHP install to not send unnecessary headers ... seems this is a problem here?

can someone confirm?

@butonic
Member
butonic commented Jan 11, 2017

Ok ... adding Header set Pragma "no-cache" to the .htaccess seems to fix it. AFAICT Pragma is only respected by IE. Nevertheless, I'd like to only send it for IE. Preferably only for xhr requests ... any other cache busting tricks you know of?

@VicDeo
Member
VicDeo commented Jan 11, 2017 edited

@butonic Pragma: no-cache is intended to be a client-side header, I'm not surprised at all that all browsers except IE ignore it 🤣

@butonic
Member
butonic commented Jan 11, 2017

This seems to work:

BrowserMatch "rv:11.0" ie11
BrowserMatch "MSIE" ie11
Header set Pragma "no-cache" env=ie11
@VicDeo
Member
VicDeo commented Jan 11, 2017 edited

When the no-cache directive is present in a request message, an application SHOULD forward the request toward the origin server even if it has a cached copy of what is being requested. This pragma directive has the same semantics as the no-cache cache-directive (see section 14.9) and is defined here for backward compatibility with HTTP/1.0. Clients SHOULD include both header fields when a no-cache request is sent to a server not known to be HTTP/1.1 compliant.

https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32

'request' and there is nothing about response

@VicDeo
Member
VicDeo commented Jan 11, 2017 edited

In my case both client and server send vaild Cache-Control headers:

Request	POST /ocs/v2.php/apps/files_sharing/api/v1/shares?format=json HTTP/1.1
Accept	application/json, text/javascript, */*; q=0.01
Content-Type	application/x-www-form-urlencoded; charset=UTF-8
requesttoken	PUQnHjwxMUFELj8vIkE1JAwMDRF5OW4BPxBIIAcNeiw=:SpAsLRCjuHnCe+XUftUsHhZkmIgg0D7VbhETaUvCNQU=
OCS-APIREQUEST	true
X-Requested-With	XMLHttpRequest
Accept-Language	ru-RU
Accept-Encoding	gzip, deflate
User-Agent	Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Host	docker.oc.solidgear.es:60956
Content-Length	97
DNT	1
Connection	Keep-Alive
Cache-Control	no-cache
Cookie	ocrek479ogjc=1mmmv42k7vsodd1tri8qomuo96; oc_sessionPassphrase=mW0zsvsIwikWAsATPLAZLaF4yYWZu7kpTkZu7QbVNP29z8Z3Etda5ytMnh0hFg%2FhGPauyUxJSNbI4qA6RUDDoqTY4GHXH%2FAkwERElAOiNbfQ6TcqM0U12lErwhhXIt0H
Response	HTTP/1.1 200 OK
Date	Wed, 11 Jan 2017 21:27:57 GMT
Server	Apache/2.4.7 (Ubuntu)
Expires	Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control	no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma	no-cache
Content-Security-Policy	default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; frame-src *; img-src * data: blob:; font-src 'self' data:; media-src *; connect-src *
X-XSS-Protection	1; mode=block
X-Content-Type-Options	nosniff
X-Frame-Options	Sameorigin
X-Robots-Tag	none
X-Download-Options	noopen
X-Permitted-Cross-Domain-Policies	none
Content-Length	623
Keep-Alive	timeout=5, max=100
Connection	Keep-Alive
Content-Type	application/json; charset=utf-8
@VicDeo
Member
VicDeo commented Jan 11, 2017

hm. Disabled HTTP 1.1 in IE settings, it changed
Cache-Control: no-cache to Pragma: no-cache in its request.

we can add any of them here https://github.com/owncloud/core/blob/08da9a8aea49c4ee6d0adac8d0e0acaf5f11e60e/core/js/shareitemmodel.js#L177

cc @PVince81

@butonic
Member
butonic commented Jan 11, 2017

This is the best I can come up with:

<If "req_novary('User-Agent') =~ /rv:11\.0|MSIE/ && req_novary('X-Requested-With') == 'XMLHttpRequest'">
    Header set Pragma "no-cache"
</If>

This should limit the Pragma to ajax calls ... +1 for using js to make IE sent the Pragma header in ajax requests.

@PVince81
Collaborator

If we want to send Pragma headers, then we need to do it for all requests, not only sharing.
In that case add it as global jquery ajax option in js.js or core/js/oc-requesttoken.js.

And that's not the only place, we also need it in core/js/files/client.js which uses XmlHttpRequest directly (or in js.js' registerXHRForErrorProcessing which wraps some XHR like the ones from files/client.js).

Does it mean that every webapp out there needs to send these headers ? I'm surprised this wasn't discovered before.

I'd rather prefer a server-side solution if possible because not every app developer out there will think of setting the header when not using jQuery (ex: angular)

@butonic
Member
butonic commented Jan 12, 2017

the php. inis session.cache_limiter default is nocache. It adds these cache related headers:

    Expires: Thu, 19 Nov 1981 08:52:00 GMT
    Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
    Pragma: no-cache

Which prevented the avatars from being cached. Also see #26589

The above if block for the .htaccess readds the no-cache header for IE ... as long as it matches the user agent and the X-Requested-With is set to XMLHttpRequest. @PVince81 hm, right angular does not set it IIRC... Here is how to make it send the header and here is the discussion which lead to the removal (CORS problems).

@VicDeo I know Pragma is meant for requests ... but ... MS and standards have a bad track record ...

@VicDeo
Member
VicDeo commented Jan 12, 2017

For jQuery it would be

$.ajaxSetup({ cache: false });
@PVince81
Collaborator

Can you guys summarize the issue again ?

From reading the thread I had the feeling that the caching issue only appears if you explicitly disable HTTP/1.1 in IE11. I wasn't able to reproduce the issue myself.

Ideal would be to have a server-side solution, either something in .htaccess or something in our server-side code for every OCS/Webdav/REST request.

@VicDeo
Member
VicDeo commented Jan 12, 2017 edited

Summary:
IE is caching ajax requests. This issue is here for a long time actually. http://www.dashbay.com/2011/05/internet-explorer-caches-ajax/

I played with browser settings but was unable to trigger that for IE11. It looks like they finally improved IE cache not to cache any ajax request but to do it under some unclear conditions only.
The link above gives the third solution: add Expires: -1 header in response to all requests that have X-Requested-With header set.

Another problem that X-Requested-With is not added by Angular due to CORS issues so any person using angular should init they module like that:

myModule.config(function($httpProvider) {
  $httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
});
@PVince81
Collaborator

Is this only for GET XHR requests or any methods ?

@VicDeo
Member
VicDeo commented Jan 12, 2017 edited

@PVince81
Here is the real reason: IE doesn’t refresh ajax based content before its expiration date.
I guess if there is no Expiration header response to ANY (GET, POST, HEAD) ajax request will never expire.
So it is better to set expiration instead of disabling cache completely.

@PVince81
Collaborator

One could say that in an ideal REST world there would be Expiration and Etags that could help validate/invalidate results. And also GET requests are read-only.

So we should add the Expiration header in all these code paths:

  • app framework controller
  • OCS base controller(s)
  • ajax/*.php, or rather not, these need to die, but add it at least in share.php then
  • Webdav GET ? Currently we use GET only for files. If in the future we have Webdav nodes where the node body is relevant, I could imagine scenarios where we could use GET.
@PVince81
Collaborator

Would also be good to know what @DeepDiver1975 thinks, especially that it affects our various API formats ^

@CaptionF
CaptionF commented Jan 12, 2017 edited

@PVince81 @VicDeo @butonic sorry I am quit sure, that this issue has been already addressed.

Please stop working on it till further notice.

today we had a websession:

the send link did not work, because the mail.php code had missing ';' that was introduced when the share was displayed for debugging purposes.

@CaptionF CaptionF closed this Jan 12, 2017
@PVince81
Collaborator

Hmm, but it sounded like @butonic was able to reproduce a caching issue in his own local env ?

So maybe there is another unrelated issue with similar symptoms in IE11 that we'll need to address eventually ?

@CaptionF

might be

@CaptionF
@PVince81
Collaborator

In this case I'll leave this up to @butonic to confirm whether there is indeed a bug that he reproduced in his local env or not.

@butonic
Member
butonic commented Jan 12, 2017

@CaptionF there is still a caching issue, which this issue should solve properly. The .htaccess can be modified as a workaround. But I am with @PVince81 that we should add this to the php code. maybe add a Pragma header to https://github.com/owncloud/core/blob/master/lib/public/AppFramework/Http/Response.php#L49 if an IE is used?

I'll reopen but remove the green ticket.

@butonic butonic reopened this Jan 12, 2017
@butonic butonic changed the title from Share via link broken in IE11 to IE11 caches AJAX requests Jan 12, 2017
@butonic
Member
butonic commented Jan 12, 2017

updated the title ind initial description to better reflect the problem. @VicDeo can you reproduce the problem by adding "setting.cache_limiter=" to your config.php? Let us confirm the workaround and agree on a correct solution. For now we have a workaround. We can implement the proper solution later or make this a junior job. @VicDeo Correct me if I am wrong.

@VicDeo
Member
VicDeo commented Jan 12, 2017

@butonic
confirmed that changing
session.cache_limiter=nocache
to
session.cache_limiter=

completely xxxx up ajax requests in IE and not only for sharing via link

E.g.

  1. Browse into trashbin
  2. Navigate to any directory under All files
  3. Delete any item
  4. Browse into trashbin again
  5. See that there is no newly deleted item
@butonic
Member
butonic commented Jan 12, 2017

@VicDeo adding the .htaccess hack fixes it?

@VicDeo
Member
VicDeo commented Jan 12, 2017 edited

Note, that not all environment have mod_headers enabled. solidgear containers are started with disabled mod_headers.

I've done several tests this doesn't work for me:

<If "req_novary('User-Agent') =~ /rv:11\.0|MSIE/ && req_novary('X-Requested-With') == 'XMLHttpRequest'">
    Header set Pragma "no-cache"
</If>

and this does work:

<If "req_novary('User-Agent') =~ /rv:11\.0|MSIE/ && req_novary('X-Requested-With') == 'XMLHttpRequest'">
    Header set Expires "-1"
</If>
@butonic
Member
butonic commented Jan 13, 2017

Great, I think we have found a good enough workaround. @VicDeo s selution can be combined into

<If "req_novary('User-Agent') =~ /rv:11\.0|MSIE/ && req_novary('X-Requested-With') == 'XMLHttpRequest'">
    Header set Pragma "no-cache"
    Header set Expires "-1"
</If>

Lets get back to this when we find more time.

@butonic butonic modified the milestone: backlog, 10.0 Jan 13, 2017
@butonic butonic removed the needs info label Jan 13, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment