Skip to content

Commit 2eee4fb

Browse files
ironman-machineCloud User
authored andcommitted
merge #367
2 parents 1270412 + 71db931 commit 2eee4fb

File tree

3 files changed

+88
-6
lines changed

3 files changed

+88
-6
lines changed

lib/s3middleware/azureHelpers/SubStreamInterface.js

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
11
const stream = require('stream');
22

3+
class SubStream extends stream.PassThrough {
4+
constructor(options) {
5+
super(options);
6+
7+
this.on('stopStreamingToAzure', function stopStreamingToAzure() {
8+
this._abortStreaming();
9+
});
10+
}
11+
12+
_abortStreaming() {
13+
this.push(null);
14+
this.end();
15+
}
16+
}
17+
318
/**
419
* Interface for streaming subparts.
520
* @class SubStreamInterface
@@ -14,7 +29,8 @@ class SubStreamInterface {
1429
this._totalLengthCounter = 0;
1530
this._lengthCounter = 0;
1631
this._subPartIndex = 0;
17-
this._currentStream = new stream.PassThrough();
32+
this._currentStream = new SubStream();
33+
this._streamingAborted = false;
1834
}
1935

2036
/**
@@ -51,12 +67,11 @@ class SubStreamInterface {
5167
* @return {undefined}
5268
*/
5369
stopStreaming(piper) {
70+
this._streamingAborted = true;
5471
if (piper) {
5572
piper.unpipe();
56-
piper.destroy();
5773
}
58-
this._sourceStream.destroy();
59-
this._currentStream.destroy();
74+
this._currentStream.emit('stopStreamingToAzure');
6075
}
6176

6277
/**
@@ -97,7 +112,7 @@ class SubStreamInterface {
97112
this._totalLengthCounter += this._lengthCounter;
98113
this._lengthCounter = 0;
99114
this._subPartIndex++;
100-
this._currentStream = new stream.PassThrough();
115+
this._currentStream = new SubStream();
101116
this.resumeStreaming();
102117
return {
103118
nextStream: this._currentStream,
@@ -111,6 +126,10 @@ class SubStreamInterface {
111126
* @return {undefined}
112127
*/
113128
write(chunk) {
129+
if (this._streamingAborted) {
130+
// don't write
131+
return;
132+
}
114133
const ready = this._currentStream.write(chunk);
115134

116135
if (!ready) {

lib/s3middleware/azureHelpers/mpuUtils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,9 @@ dataStoreName, log, cb) => {
151151
'putting multiple parts');
152152

153153
resultsCollector.on('error', (err, subPartIndex) => {
154-
streamInterface.stopStreaming(request);
155154
log.error(`Error putting subpart to Azure: ${subPartIndex}`,
156155
{ error: err.message, dataStoreName });
156+
streamInterface.stopStreaming(request);
157157
if (err.code === 'ContainerNotFound') {
158158
return cb(errors.NoSuchBucket);
159159
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
const assert = require('assert');
2+
const stream = require('stream');
3+
const SubStreamInterface =
4+
require('../../../../lib/s3middleware/azureHelpers/SubStreamInterface');
5+
6+
describe('s3middleware SubStreamInterface.stopStreaming()', () => {
7+
const eventsEmitted = {
8+
sourceStreamUnpiped: false,
9+
currentStreamStopStreamingToAzure: false,
10+
currentStreamEnded: false,
11+
};
12+
const expectedSequence = {
13+
sourceStreamUnpiped: 0,
14+
currentStreamStopStreamingToAzure: 1,
15+
currentStreamEnded: 2,
16+
};
17+
const data = Buffer.alloc(100);
18+
let dataMarker = 0;
19+
let eventSequence = 0;
20+
const mockRequest = new stream.Readable({
21+
read: () => {
22+
if (dataMarker >= data.length) {
23+
return mockRequest.push(null);
24+
}
25+
mockRequest.push(data.slice(dataMarker, dataMarker + 1));
26+
dataMarker += 1;
27+
return undefined;
28+
},
29+
});
30+
const sourceStream = new stream.PassThrough();
31+
const subStreamInterface = new SubStreamInterface(sourceStream);
32+
sourceStream.on('unpipe', () => {
33+
eventsEmitted.sourceStreamUnpiped = eventSequence++;
34+
});
35+
subStreamInterface._currentStream.on('stopStreamingToAzure', () => {
36+
eventsEmitted.currentStreamStopStreamingToAzure = eventSequence++;
37+
});
38+
subStreamInterface._currentStream.on('finish', () => {
39+
eventsEmitted.currentStreamEnded = eventSequence++;
40+
});
41+
it('should stop streaming data and end current stream', done => {
42+
sourceStream.on('data', chunk => {
43+
const currentLength = subStreamInterface.getLengthCounter();
44+
if (currentLength === 10) {
45+
Object.keys(eventsEmitted).forEach(key => {
46+
assert.strictEqual(eventsEmitted[key], false);
47+
});
48+
assert.strictEqual(mockRequest._readableState.pipesCount, 1);
49+
return subStreamInterface.stopStreaming(mockRequest);
50+
}
51+
return subStreamInterface.write(chunk);
52+
});
53+
mockRequest.pipe(sourceStream);
54+
setTimeout(() => {
55+
Object.keys(eventsEmitted).forEach(key => {
56+
assert.strictEqual(eventsEmitted[key], expectedSequence[key]);
57+
});
58+
assert.strictEqual(subStreamInterface.getLengthCounter(), 10);
59+
assert.strictEqual(mockRequest._readableState.pipesCount, 0);
60+
return done();
61+
}, 1000);
62+
});
63+
});

0 commit comments

Comments
 (0)