Skip to content

Commit d27c057

Browse files
bugfix: ZENKO-2866 abort request on backend if S3 client disconnects
Call request.abort() on the backend side when an S3 client disconnects, to avoid leaking sockets. Also make sure request.abort() through the stream destroy() call is called asynchronously from the stream creation.
1 parent ff53964 commit d27c057

File tree

1 file changed

+17
-2
lines changed

1 file changed

+17
-2
lines changed

lib/storage/data/external/AwsClient.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,18 @@ class AwsClient {
178178
{ responseHeaders: response.httpResponse.headers,
179179
backendType: this.clientType });
180180
});
181-
const stream = request.createReadStream().on('error', err => {
181+
const stream = request.createReadStream();
182+
183+
// modify the stream destroy() behavior to also abort the HTTP request
184+
const streamDestroy = stream._destroy.bind(stream);
185+
stream._destroy = (err, cb) => {
186+
log.debug('aborting GET request in progress', { objectGetInfo });
187+
request.abort();
188+
if (streamDestroy) {
189+
streamDestroy(err, cb);
190+
}
191+
};
192+
stream.on('error', err => {
182193
let logLevel;
183194
if (err.code === 'NotFound') {
184195
logLevel = 'info';
@@ -189,7 +200,11 @@ class AwsClient {
189200
`error streaming data from ${this.type}`,
190201
err, this._dataStoreName, this.clientType);
191202
});
192-
return callback(null, stream);
203+
// Always call the callback asynchronously: the caller may
204+
// destroy the stream with destroy(), which MUST be
205+
// asynchronous wrt. request.createReadStream() to avoid
206+
// socket leaks, notably because of the request.abort() call.
207+
return process.nextTick(() => callback(null, stream));
193208
}
194209
delete(objectGetInfo, reqUids, callback) {
195210
const { key, dataStoreVersionId, deleteVersion } = objectGetInfo;

0 commit comments

Comments
 (0)