From 545493b34a7d4e00ec1418b69a3f17b385d158fa Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Mon, 17 Oct 2016 13:38:19 -0400 Subject: [PATCH] Report an error when HTTP request body is incomplete. When a download aborts prematurely, the status code is often 200 OK, even though we probably should not proceed with any further processing of the downloaded information. This silent failure leads to problems like the dreaded "Error: ENOENT: no such file or directory, open... os.json" (#7806 and others), which were hard to diagnose properly because the failure occurred only later, when extracting a buffer that downloaded incompletely. The getUrlWithResuming helper should be able to retry after this error is thrown, which will result in a more helpful warning, even if in the most common case, i.e. MaxCDN failure, it will never actually succeed. Note that this change will not help until Meteor 1.4.2 is officially released and becomes the implementation used to download later releases. Mitigates #7806. --- History.md | 5 +++++ tools/utils/http-helpers.js | 10 +++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 0b145d7b89..5d63c9dc9d 100644 --- a/History.md +++ b/History.md @@ -20,6 +20,11 @@ * The `meteor ...` syntax will now work for any command installed in `dev_bundle/bin`, except for Meteor's own commands. +* Incomplete package downloads will now fail (and be retried several + times) instead of silently succeeding, which was the cause of the + dreaded `Error: ENOENT: no such file or directory, open... os.json` + error. [#7806](https://github.com/meteor/meteor/issues/7806) + ## v1.4.1.2 * Node has been upgraded to version 4.6.0, a recommended security release: diff --git a/tools/utils/http-helpers.js b/tools/utils/http-helpers.js index 673da536b7..c8c9b49942 100644 --- a/tools/utils/http-helpers.js +++ b/tools/utils/http-helpers.js @@ -299,7 +299,15 @@ _.extend(exports, { // require it until we definitely need it. Console.debug("Doing HTTP request: ", options.method || 'GET', options.url); var request = require('request'); - var req = request(options, callback); + var req = request(options, function (error, response, body) { + const contentLength = Number(response.headers["content-length"]); + if (contentLength > 0 && body.length < contentLength) { + error = new Error( + "Expected " + contentLength + " bytes in request body " + + "but received only " + body.length); + } + return callback.call(this, error, response, body); + }); if (_.isFunction(onRequest)) { onRequest(req);