1

I have a server route that is called from a client-side page, like this:

$.get("/feed", {
  feedurl: feedUrl,
  dataType: 'json'
}, function() {
  $feedBody.empty();
}).fail(function(error) {
  console.log('ERROR!!');
}).done(function(data) {
    ...
}

Here is the code of the route:

router.get('/feed', function(req, res) {

  console.log('getFeed (%s)', req.query.feedurl);

  var myreq = request(req.query.feedurl);

  myreq.on('error', function(error) {
    console.log('DNS Error (%s) [%s]',req.query.feedurl, error.message);
    res.send({error:error.message});
    })
    .on('response', function(response) {
      console.log('RESPONSE (%s) [%s]', req.query.feedurl, JSON.stringify(response));

      if (response.statusCode == 200) {

        getFeed(req.query.feedurl, function (err, feedItems, feedTitle, feedLink) {
          if (feedItems) {
            try {
              console.log('Sending (%s) - %s', req.query.feedurl, new Date().getTime());
              res.send({
                feedItems: feedItems,
                feedLink: feedLink,
                feedTitle: feedTitle
              });
            } catch(err) {
              console.log('ERROR in SEND try / catch (%s) (%s)', req.query.feedurl, err);
            }

          } else {
            res.send({error:err});
          }

        });

      }
    });
});

And it works fine, except when it doesn't: Right now, on a particular URL (apparently a gzip-encoded RSS feed, but that is not really the point) the feed is sent back several (3!) times back to the client, triggering the dreaded "Can't set headers after they are sent." error. And now, I can't send the error message back to the client.

Here is the output of the above code when it works:

2019-01-15 14:23: getFeed (http://rss.nytimes.com/services/xml/rss/nyt/World.xml)
2019-01-15 14:23: Sending (http://rss.nytimes.com/services/xml/rss/nyt/World.xml) - 1547558586803

And when it does not:

2019-01-15 14:31: getFeed (https://www.rollingstone.com/music/rss)
2019-01-15 14:31: Sending (https://www.rollingstone.com/music/rss) - 1547559093110
2019-01-15 14:31: Sending (https://www.rollingstone.com/music/rss) - 1547559093110
2019-01-15 14:31: ERROR in SEND try / catch (https://www.rollingstone.com/music/rss) (Error: Can't set headers after they are sent.)
2019-01-15 14:31: Sending (https://www.rollingstone.com/music/rss) - 1547559093111
2019-01-15 14:31: ERROR in SEND try / catch (https://www.rollingstone.com/music/rss) (Error: Can't set headers after they are sent.)

Note the 3 calls, and the two subsequent caught errors.

There in only a single call to this route ; why is it sending stuff back 3 times ?

yPhil
  • 8,049
  • 4
  • 57
  • 83
  • 1
    Some or at least one of those calls is likely a pre-flight request. These are quick calls AJAX requests makes to ensure the incoming request is acceptable. https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request – Zenkylo Jan 15 '19 at 15:41

1 Answers1

0

OK ; From this answer I learnt that there is a res.headersSent boolean available. Also, as per this one, I had to enforce it by return;ing right after each res.send().

Here is my final code:

router.get('/feed', function(req, res) {

  var dnsreq = request(req.query.feedurl);

  dnsreq
    .on('error', function(error) {
      // The only way so far to catch a DNS error
      res.send({error:error.code});
    })
    .on('response', function(response) {

      getFeed(req.query.feedurl, function (err, feedItems, feedTitle, feedLink) {

        if (feedItems && !res.headersSent) {
          res.send({
            feedItems: feedItems,
            feedLink: feedLink,
            feedTitle: feedTitle
          });
          return;

        } else if (!res.headersSent) {
          res.send({error:err});
        }
      });
    });
});
yPhil
  • 8,049
  • 4
  • 57
  • 83