Chrome/Firefox sending two POSTs under the hood ex

2019-06-28 04:05发布

问题:

NOTE: This isn't the preflight OPTIONS, and it isn't the favicon or anything else like that. It's actually 2 POSTS. There is a screenshot below to show this more clearly.

My specs/versions:

  • macOS Sierra Version 10.12.3
  • Chrome Version 61.0.3128.0 (Official Build) dev (64-bit)
  • Node v8.0.0

I have a server that uses setTimeout to wait 10 seconds before responding. I have a frontend that makes a single POST to the server. Using wireshark I'm able to see that the browser actually makes 2 POSTs, a second one at t+5s. The server sees both these POSTs and responds to both. The frontend only gets the result of the second POST.

In the example below you will see that the fetch actually takes 15 seconds to complete, rather than the 10 seconds you'd expect from the timeout in the server.

The call is CORS, and only happens when the server takes longer than 5 seconds to respond.

What is going on here and how can I stop it?

Here is a minimal reproduction:

server.js (run this with node)

var http = require("http");
// A counter for the requests
var counter = 0;

var server = http.createServer(function(request, response) {
  // Closure to prevent the request number from changing with the counter.
  (function(requestNumber) {
    console.log('Incoming:', request.method, "assigning number", requestNumber);

    // CORS headers
    response.writeHead(200, {
      "Content-Type": "application/json",
      "Access-Control-Allow-Methods": "GET,HEAD,PUT,POST,DELETE",
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Headers": "authorization,content-type,expect",
    });

    // Respond to options immediately.
    if (request.method === "OPTIONS") {
      response.end()
    } else {
      // Otherwise simulate a long running process (db write whatever)
      setTimeout(function() {
        console.log('Timeout done, responding to request number', requestNumber);
        response.write(JSON.stringify({ number: requestNumber }));
        response.end();
      }, 10000);
    }
  }(counter++));
});

server.listen(8080);
console.log("Server is listening");

Fetch call on the frontend:

var start = new Date();
console.log('making call. Wait ~15 seconds for the result to come back');
fetch('http://localhost:8080', {
  headers: {
    Accept: 'application/json',
    'Content-Type': 'application/json',
  },
  method: 'post',
  mode: 'cors',
  body: JSON.stringify({ some: 'content' }),
}).then(result => result.json()).then(json => {
  console.log('result', json);
  console.log('call took:', (new Date() - start), 'seconds');
}).catch(err => {
  console.log('err', err);
});

Here is a screenshot of the server, the frontend console and the wireshark traffic:

And here is the network tab. You can see the request is 'stalled' for 5 seconds.

Some additional notes from testing I've done:

This happens in chrome and firefox, but not safari. (I am on a mac, chrome Version 60.0.3112.20 (Official Build) dev (64-bit)) It's possible that this only happens with CORS, but haven't been able to confirm with a minimal test case. I tried it with jQuery's ajax to see if it was maybe fetch related but it still happened. (I will check the jQuery source to see if it's still using xhr under the hood because I'm not actually sure that it hasn't moved over to fetch.)

回答1:

So it turns out this was a bug with Node 8.0.0 and has been fixed by 8.1.1.

I haven't nailed down the exact bug but it could be one of these:

https://github.com/nodejs/node/pull/13549

https://github.com/nodejs/node/commit/2cb6f2b281