@yetech/estra7ah 3.3.1 → 3.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/estra7ah.js +77 -8
- package/package.json +1 -1
package/dist/estra7ah.js
CHANGED
|
@@ -51,17 +51,46 @@ function downloadRange(url, fd, start, end, progressBar, attempt = 0) {
|
|
|
51
51
|
.then(
|
|
52
52
|
(response) =>
|
|
53
53
|
new Promise((resolve, reject) => {
|
|
54
|
+
// A 200 means the server (or a proxy on this plain-HTTP route) ignored
|
|
55
|
+
// the Range header and is sending the whole file: writing that at six
|
|
56
|
+
// different offsets corrupts the binary. Bail out to single-stream.
|
|
57
|
+
if (response.status !== 206) {
|
|
58
|
+
response.data.destroy();
|
|
59
|
+
return reject({ noRangeSupport: true });
|
|
60
|
+
}
|
|
54
61
|
let position = start;
|
|
62
|
+
// With timeout: 0 a dead connection would hang the update forever
|
|
63
|
+
// (progress stuck at 0%). Kill the stream if no data arrives for 30s
|
|
64
|
+
// so the retry/fallback path can take over.
|
|
65
|
+
let watchdog;
|
|
66
|
+
const resetWatchdog = () => {
|
|
67
|
+
clearTimeout(watchdog);
|
|
68
|
+
watchdog = setTimeout(() => {
|
|
69
|
+
response.data.destroy(new Error("download stalled: no data for 30s"));
|
|
70
|
+
}, 30000);
|
|
71
|
+
};
|
|
72
|
+
resetWatchdog();
|
|
73
|
+
response.data.on("close", () => clearTimeout(watchdog));
|
|
55
74
|
response.data.on("data", (chunk) => {
|
|
75
|
+
resetWatchdog();
|
|
56
76
|
fs.writeSync(fd, chunk, 0, chunk.length, position);
|
|
57
77
|
position += chunk.length;
|
|
58
78
|
progressBar.tick(chunk.length);
|
|
59
79
|
});
|
|
60
|
-
response.data.on("end", () =>
|
|
80
|
+
response.data.on("end", () => {
|
|
81
|
+
// "end" also fires on some premature disconnects; an incomplete
|
|
82
|
+
// chunk must be retried, never treated as success.
|
|
83
|
+
if (position !== end + 1) {
|
|
84
|
+
return reject({ err: new Error(`chunk ended at ${position}, expected ${end + 1}`), position });
|
|
85
|
+
}
|
|
86
|
+
resolve(position);
|
|
87
|
+
});
|
|
88
|
+
response.data.on("aborted", () => reject({ err: new Error("connection aborted"), position }));
|
|
61
89
|
response.data.on("error", (err) => reject({ err, position }));
|
|
62
90
|
})
|
|
63
91
|
)
|
|
64
92
|
.catch((failure) => {
|
|
93
|
+
if (failure && failure.noRangeSupport) throw failure;
|
|
65
94
|
const resumeAt = failure.position || start;
|
|
66
95
|
if (attempt < 2) return downloadRange(url, fd, resumeAt, end, progressBar, attempt + 1);
|
|
67
96
|
throw failure.err || failure;
|
|
@@ -103,10 +132,17 @@ async function downloadFileFast(url, destPath) {
|
|
|
103
132
|
await Promise.all(jobs);
|
|
104
133
|
} catch (error) {
|
|
105
134
|
fs.closeSync(fd);
|
|
106
|
-
|
|
135
|
+
try {
|
|
136
|
+
fs.unlinkSync(tmpPath);
|
|
137
|
+
} catch (e) {}
|
|
138
|
+
if (error && error.noRangeSupport) return downloadFileStream(url, destPath);
|
|
107
139
|
throw error;
|
|
108
140
|
}
|
|
109
141
|
fs.closeSync(fd);
|
|
142
|
+
if (fs.statSync(tmpPath).size !== totalLength) {
|
|
143
|
+
fs.unlinkSync(tmpPath);
|
|
144
|
+
throw new Error("downloaded size does not match content-length");
|
|
145
|
+
}
|
|
110
146
|
fs.renameSync(tmpPath, destPath);
|
|
111
147
|
}
|
|
112
148
|
|
|
@@ -121,12 +157,40 @@ function downloadFileStream(url, destPath) {
|
|
|
121
157
|
total: totalLength,
|
|
122
158
|
})
|
|
123
159
|
: null;
|
|
124
|
-
const
|
|
160
|
+
const tmpPath = `${destPath}.download`;
|
|
161
|
+
const writer = fs.createWriteStream(tmpPath);
|
|
125
162
|
return new Promise((resolve, reject) => {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
163
|
+
let watchdog;
|
|
164
|
+
const fail = (err) => {
|
|
165
|
+
clearTimeout(watchdog);
|
|
166
|
+
writer.destroy();
|
|
167
|
+
try {
|
|
168
|
+
fs.unlinkSync(tmpPath);
|
|
169
|
+
} catch (e) {}
|
|
170
|
+
reject(err);
|
|
171
|
+
};
|
|
172
|
+
const resetWatchdog = () => {
|
|
173
|
+
clearTimeout(watchdog);
|
|
174
|
+
watchdog = setTimeout(() => {
|
|
175
|
+
response.data.destroy(new Error("download stalled: no data for 30s"));
|
|
176
|
+
}, 30000);
|
|
177
|
+
};
|
|
178
|
+
resetWatchdog();
|
|
179
|
+
response.data.on("data", (chunk) => {
|
|
180
|
+
resetWatchdog();
|
|
181
|
+
if (progressBar) progressBar.tick(chunk.length);
|
|
182
|
+
});
|
|
183
|
+
writer.on("error", fail);
|
|
184
|
+
writer.on("finish", () => {
|
|
185
|
+
clearTimeout(watchdog);
|
|
186
|
+
if (totalLength && fs.statSync(tmpPath).size !== totalLength) {
|
|
187
|
+
return fail(new Error("downloaded size does not match content-length"));
|
|
188
|
+
}
|
|
189
|
+
fs.renameSync(tmpPath, destPath);
|
|
190
|
+
resolve();
|
|
191
|
+
});
|
|
192
|
+
response.data.on("aborted", () => fail(new Error("connection aborted")));
|
|
193
|
+
response.data.on("error", fail);
|
|
130
194
|
response.data.pipe(writer);
|
|
131
195
|
});
|
|
132
196
|
});
|
|
@@ -156,7 +220,12 @@ async function downloadRunner() {
|
|
|
156
220
|
const runnerPath = `${DIR_NAME}/${getRunner()}`;
|
|
157
221
|
|
|
158
222
|
try {
|
|
159
|
-
|
|
223
|
+
try {
|
|
224
|
+
await downloadFileFast(serverFile, runnerPath);
|
|
225
|
+
} catch (error) {
|
|
226
|
+
console.error("Parallel download failed, retrying with a single stream:", error.message);
|
|
227
|
+
await downloadFileStream(serverFile, runnerPath);
|
|
228
|
+
}
|
|
160
229
|
fs.writeFileSync(versionFilePath, version);
|
|
161
230
|
console.log("** Finished DOWNLOADING NEW RUNNER VERSION **");
|
|
162
231
|
} catch (error) {
|