Inconsistent browser retry behaviour for timed out POST requests

17,294

Browsers retry requests (including POSTs) when the connection is closed before receiving a response from the server. This is defined in the HTTP 1.1 Spec Section 8.2.4.

Share:
17,294
Jay Kumar
Author by

Jay Kumar

Cloud & DevOps Specialist github, twitter, linkedin

Updated on June 06, 2022

Comments

  • Jay Kumar
    Jay Kumar almost 2 years

    I am experiencing occasional retries for a POST request, when there is no response from server due to timeout. All modern browsers have retry logic for idempotent requests (GET, HEAD, etc) but I am unable to reason out why it happens for a POST request.

    I am testing this case using a simple node.js server with 3 routes and chrome browser .

    /       : gives a html page with jquery and code snippets to fire ajax requests
    /hi     : gives a text response 'hello'
    /sleep  : request will timeout without any response 
    

    By default, node.js http server times out a request after 2 minutes.

    retry.js

    var http = require('http');
    var server = http.createServer();
    
    server.on('request', function(req, res) {
        console.log(new Date() + ' ' + req.method + ' request on ' + req.url);
    
        if (req.url === '/sleep') {
            console.log('!!! sleeping');
        } else if (req.url === '/') {
            html = "$.post('/hi', {'for':'server'}, function() { console.log(arguments) } ).error(function() { console.log(arguments) })";
            html += "<br><br>";
            html += "$.post('/sleep', {'for':'infinite'}, function() { console.log(arguments) } ).error(function() { console.log(arguments) })";
            html += '<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>';
        
            res.writeHead(200, {'Content-Type': 'text/html'});
            res.end(html);
        } else {
            res.writeHead(200, {'Content-Type': 'text/plain'});
            res.end('hello');
        }
    });
    
    server.listen(2020);
    console.log('server listening on port 2020');
    

    run it

    $ node retry.js 
    server listening on port 2020
    

    1

    Load this page in browser http://localhost:2020
    
    Fri Mar 01 2013 12:21:59 GMT+0530 (IST) GET request on /
    Fri Mar 01 2013 12:21:59 GMT+0530 (IST) GET request on /favicon.ico
    

    2

    From Dev console, fire an ajax POST request to /hi using jquery

    $.post('/hi', {'for':'server'}, function() { console.log(arguments) } ).error(function() { console.log(arguments) })
    

    Fri Mar 01 2013 12:22:05 GMT+0530 (IST) POST request on /hi

    3

    Fire a POST request to /sleep, results in a retry after 2 mins and errors out after 4 mins.

    $.post('/sleep', {'for':'infinite'}, function() { console.log(arguments) } ).error(function() { console.log(arguments) })
    

    server logs shows 2 requests

    Fri Mar 01 2013 12:22:21 GMT+0530 (IST) POST request on /sleep
    !!! sleeping
    Fri Mar 01 2013 12:24:21 GMT+0530 (IST) POST request on /sleep
    !!! sleeping
    

    Firing it again, errors out in 2 mins without any retry.

    Fri Mar 01 2013 12:30:01 GMT+0530 (IST) POST request on /sleep
    !!! sleeping ?
    

    It's not getting retried until we fire a request to /hi (or any other url) that results in a response. And retry happens for just one subsequent request to /sleep.

    In browser, the network tab shows the pattern like

    /hi - success 
    /sleep - cancelled - 4 mins (retry happens)
    /sleep - cancelled - 2 mins (no retry)
    /sleep - cancelled - 2 mins (no retry)
    /hi - success 
    /sleep - cancelled - 4 mins (retry happens)
    /sleep - cancelled - 2 mins (no retry)
    /sleep - cancelled - 2 mins (no retry)
    

    Question

    Though we need to design our web app to tolerate these extra requests (either by browsers or any other intermediaries), this inconsistent browser retries looks weird. I had observed this behaviour in chrome (v16 & v24) & firefox.

    Can someone help me to understand the browser retry logic behind timed out non-idempotent requests ?

    Other relevant stackoverflow questions

    What happens when no response is received for a request? I'm seeing retries

  • Jay Kumar
    Jay Kumar over 10 years
    This answers the scenario. And we made our app resilient to intermediary retries. Thank you.
  • robsonrosa
    robsonrosa about 9 years
    And what can close a connection before receiving a response? I have a process that last about 10 minutes. Testing here, in my machine, it works fine. But in another machine (both with windows/chrome), a POST retry is triggered after 2 or 3 minutes. I dont have any idea about what is happening. I can only think about browser timeout, but why it doesn't happen in my machine?
  • Tyree Jackson
    Tyree Jackson almost 6 years
    @robsonrosa A reverse proxy or a web application firewall in between the client and the server could potentially close the connection depending on how it is configured.