7

I have noticed that Safari 5.0.5 (6533.21.1) seems to be submitting duplicate ajax calls. When I run the following reduced test case:

// jquery 1.6 include
$(document).ready(function() {
    setTimeout(function(e) {
        var req1 = $.getJSON('/api/private/customers.json');
        console.log('req1 sent');
    }, 2000);
    setTimeout(function(e) {
        var req2 = $.getJSON('/api/private/customers.json');
        console.log('req1 sent');
    }, 4000);
});

the Safari Resources panel and the console show two xhr requests going out, but my server log shows three xhr requests coming in:

XX.XX.XX.XXX - - [10/May/2011:16:50:40 -0400] "GET /api/private/customers.json HTTP/1.1" 200 183 "https://sub.mydomain.com/customers" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-us) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1"
XX.XX.XX.XXX - - [10/May/2011:16:50:42 -0400] "GET /api/private/customers.json HTTP/1.1" 200 183 "https://sub.mydomain.com/customers" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-us) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1"
XX.XX.XX.XXX - - [10/May/2011:16:50:42 -0400] "GET /api/private/customers.json HTTP/1.1" 200 183 "https://sub.mycomain.com/customers" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-us) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1"

When I make the same request with the latest version of Firefox, I correctly get two requests:

XX.XX.XX.XXX - - [10/May/2011:16:52:00 -0400] "GET /api/private/customers.json HTTP/1.1" 200 183 "https://sub.mycomain.com/customers" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"
XX.XX.XX.XXX - - [10/May/2011:16:52:02 -0400] "GET /api/private/customers.json HTTP/1.1" 200 183 "https://sub.mycomain.com/customers" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"

This behavior does not seem to happen on the first request, but all subsequent requests are sent in duplicate. Does anyone have any idea what is going on? Efforts to detect the extra requests in the js were futile.

user747594
  • 151
  • 1
  • 7
  • Can you add the next 4 seconds of requests? I'm wondering if the first one is 2 seconds after your first timeout and then it looked like it was showing "duplicates" because it's another 2 seconds from your first timeout's update and 4 seconds since your second timeout was started. This could cause it to look like it's doing duplicates. – Bryce Siedschlaw May 10 '11 at 21:15
  • Thanks Bryce. Sorry if the example is causing confusion. I used timeouts to eliminate the possibility of duplicate click events. They are definitely duplicates. – user747594 May 10 '11 at 22:07
  • I am also having this issue. – Obi Wan Oct 16 '13 at 20:00

3 Answers3

5

I guess we won't know if it's a bug or a feature... Anyway Safari (as it is in 10.0.2 version) still do perform conditionnal request as explained by Dan Manastireanu. The way I found to have a request not duplicated is to set a 'If-Unmodified-Since' header with the current request time. (Alternatively you can also add a timestamp parameter in url, as mentionned again by Dan Manastireanu)

I downvoted the setRequestHeader('Connection', "close") because it is a forbidden header name and throw a Refused to set unsafe header "Connection" error.

So a basic request looks like this:

var get = function(url, callbackSucess, callbackError) {
    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.timeout = 10000;

    var now = new Date().getTime();
    request.setRequestHeader('If-Unmodified-Since', now);

    request.onreadystatechange = function() {
        if (request.readyState !== 4) {
            return;
        }
        if (request.status === 200) {
            callbackSucess(request.status);
        } else {
            callbackError(request.status);
        }
    };
    request.onerror = function (error) {
        callbackError(error);
    };
    request.ontimeout = function () {
        request.abort();
    };
    request.send(null);
}
Community
  • 1
  • 1
maiwenn
  • 51
  • 1
  • 1
  • Thanks a lot! I wasted hours of time not knowing what is going on there. Your solution works and also makes most sense according to the spec. – Fabian Scheidt Oct 16 '18 at 19:12
3

I believe the browser is making a conditional GET request for the second call (also see the 304 response status).

On the first call there is no cached response in the browser, so it does a normal request.
On the second request the browser does first a conditional GET request, and upon seeing that its cached response is out of date it has to repeat the GET request.

As far as I know jQuery has a builtin fix for this (it automatically appends a parameter to the request url, something like _=123456789). I don't know why this is not working here.

You could try to append a request param by hand like this: '/api/private/customers.json?v='+(new Date().getTime())
Or you could try and use jQuery.ajax with cache:false, and dataType:'jsonp'

You can open Safari's Developer Tools (Web Inspector), and check the Network tab. It will tell you more about the request and response.

Dan Manastireanu
  • 1,802
  • 1
  • 15
  • 18
  • I'm not entirely sure Safari has the Network tab, as I don't use it. But Chrome does, and they are both WebKit based and should be similar in this perspective... – Dan Manastireanu May 10 '11 at 21:46
  • Thanks. I tried it with those options and also checked to ensure the expires headers were set in the past (they were). Interestingly, the _ parameter is identical for the duplicate requests (e.g. two _=1305064165485 values). – user747594 May 10 '11 at 21:52
  • It is expected that the last two requests have the same url. jQuery builds just two requests(each should have a different _=timestamp value). But for the second jQuery request the browser intervenes, and makes 2 calls instead(a cond. GET, and a real GET). This is transparent to the javascript side (it looks as just one request). The _ trick is supposed to make the two urls different and trick the browser into thinking the two requests jQuery created are different. Apparently Safari ignores the request params, and compares just the base url... – Dan Manastireanu May 10 '11 at 22:02
  • From the w3c link I posted: *If a 304 response indicates an entity not currently cached, then the cache MUST disregard the response and repeat the request without the conditional.* This is the part about the two requests the browser makes instead of just one, and the reason the urls are identical for the duplicate requests – Dan Manastireanu May 10 '11 at 22:05
  • This happens with POST requests as well (which is how I discovered it - the actual code is creating members in a REST API and I was getting extras). Is conditional GET in play there as well? Is there any way to detect that on the server side? (I have successfully detected the second of the two requests, but ignoring it sometimes causes the request to fail). – user747594 May 10 '11 at 22:12
  • Also, I am setting the response code (in the API) in the Content Length header to 200, which I assume is getting applied to each request? – user747594 May 10 '11 at 22:15
  • A [conditional GET request](http://ruturajv.wordpress.com/2005/12/27/conditional-get-request/) is characterized by having the headers `If-Modified-Since` and/or `If-None-Match` set. You could check for those. – Dan Manastireanu May 10 '11 at 22:59
  • Thanks again Dan. None of the requests (duplicate or otherwise) have either of those headers set. Must be something else going on here. – user747594 May 11 '11 at 01:17
2

Adding a Connection: Close header to the API response fixes the problem.

user747594
  • 151
  • 1
  • 7
  • 1
    Could you explain this a little more? Where did you add this code? – Obi Wan Oct 16 '13 at 20:02
  • Down-voting because this answer is incomplete. Please share more detail! Also, looks like this might be unsafe, according to this: [http://stackoverflow.com/questions/2954958/modifying-jquery-ajax-request-connection-header](http://stackoverflow.com/questions/2954958/modifying-jquery-ajax-request-connection-header) – user3396385 Nov 19 '15 at 19:33