pixl-server-storage 3.0.9 → 3.0.13
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/docs/API.md +6 -1
- package/engines/Couchbase.js +14 -0
- package/engines/Filesystem.js +16 -1
- package/engines/Redis.js +14 -0
- package/engines/RedisCluster.js +14 -0
- package/engines/S3.js +22 -1
- package/package.json +2 -3
- package/test/test-main.js +95 -0
- package/transaction.js +1 -1
package/docs/API.md
CHANGED
|
@@ -226,7 +226,12 @@ storage.getStreamRange( 'test1.gif', 0, 99, function(err, readStream, info) {
|
|
|
226
226
|
} );
|
|
227
227
|
```
|
|
228
228
|
|
|
229
|
-
As you can see above, your callback is also passed a 3rd argument, which is an object containing the record's full byte length and modification date. These properties match those returned when you call [head()](#head) (i.e. `len` and `mod`). The `len` is not affected by the `start` and `end` range -- it is always the full byte length of the record.
|
|
229
|
+
As you can see in the example above, your callback is also passed a 3rd argument, which is an object containing the record's full byte length and modification date. These properties match those returned when you call [head()](#head) (i.e. `len` and `mod`). The `len` is not affected by the `start` and `end` range -- it is always the full byte length of the record.
|
|
230
|
+
|
|
231
|
+
The API supports two special range cases:
|
|
232
|
+
|
|
233
|
+
- If `start` is valid but `end` is `NaN`, it is assumed you want the entire record starting at `start` offset.
|
|
234
|
+
- If `start` is `NaN` but `end` is valid, it is assumed you want `end` bytes from the end of the record.
|
|
230
235
|
|
|
231
236
|
Please note that as of this writing, the `Couchbase`, `Redis` and `RedisCluster` engines have no native stream API, so the `getStreamRange()` method has to load the entire record into memory.
|
|
232
237
|
|
package/engines/Couchbase.js
CHANGED
|
@@ -248,6 +248,20 @@ module.exports = Class.create({
|
|
|
248
248
|
return callback( err, null );
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
+
// validate byte range, now that we have the head info
|
|
252
|
+
if (isNaN(start) && !isNaN(end)) {
|
|
253
|
+
start = buf.length - end;
|
|
254
|
+
end = buf.length ? buf.length - 1 : 0;
|
|
255
|
+
}
|
|
256
|
+
else if (!isNaN(start) && isNaN(end)) {
|
|
257
|
+
end = buf.length ? buf.length - 1 : 0;
|
|
258
|
+
}
|
|
259
|
+
if (isNaN(start) || isNaN(end) || (start < 0) || (start >= buf.length) || (end < start) || (end >= buf.length)) {
|
|
260
|
+
download.destroy();
|
|
261
|
+
callback( new Error("Invalid byte range (" + start + '-' + end + ") for key: " + key + " (len: " + buf.length + ")"), null );
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
251
265
|
var range = buf.slice(start, end + 1);
|
|
252
266
|
var stream = new BufferStream(range);
|
|
253
267
|
callback(null, stream, { mod: 1, len: buf.length });
|
package/engines/Filesystem.js
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
var path = require('path');
|
|
6
6
|
var fs = require('fs');
|
|
7
7
|
var async = require('async');
|
|
8
|
-
var mkdirp = require('mkdirp');
|
|
9
8
|
var crypto = require('crypto');
|
|
10
9
|
|
|
11
10
|
var Class = require("pixl-class");
|
|
@@ -13,6 +12,8 @@ var Component = require("pixl-server/component");
|
|
|
13
12
|
var Tools = require("pixl-tools");
|
|
14
13
|
var Cache = require("pixl-cache");
|
|
15
14
|
|
|
15
|
+
var mkdirp = Tools.mkdirp;
|
|
16
|
+
|
|
16
17
|
module.exports = Class.create({
|
|
17
18
|
|
|
18
19
|
__name: 'Filesystem',
|
|
@@ -479,6 +480,20 @@ module.exports = Class.create({
|
|
|
479
480
|
return callback( err, null );
|
|
480
481
|
}
|
|
481
482
|
|
|
483
|
+
// validate byte range, now that we have the head info
|
|
484
|
+
if (isNaN(start) && !isNaN(end)) {
|
|
485
|
+
start = stats.size - end;
|
|
486
|
+
end = stats.size ? stats.size - 1 : 0;
|
|
487
|
+
}
|
|
488
|
+
else if (!isNaN(start) && isNaN(end)) {
|
|
489
|
+
end = stats.size ? stats.size - 1 : 0;
|
|
490
|
+
}
|
|
491
|
+
if (isNaN(start) || isNaN(end) || (start < 0) || (start >= stats.size) || (end < start) || (end >= stats.size)) {
|
|
492
|
+
download.destroy();
|
|
493
|
+
callback( new Error("Invalid byte range (" + start + '-' + end + ") for key: " + key + " (len: " + stats.size + ")"), null );
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
|
|
482
497
|
// create read stream
|
|
483
498
|
var inp = fs.createReadStream( file, { start, end } );
|
|
484
499
|
|
package/engines/Redis.js
CHANGED
|
@@ -252,6 +252,20 @@ module.exports = Class.create({
|
|
|
252
252
|
return callback( err, null );
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
+
// validate byte range, now that we have the head info
|
|
256
|
+
if (isNaN(start) && !isNaN(end)) {
|
|
257
|
+
start = buf.length - end;
|
|
258
|
+
end = buf.length ? buf.length - 1 : 0;
|
|
259
|
+
}
|
|
260
|
+
else if (!isNaN(start) && isNaN(end)) {
|
|
261
|
+
end = buf.length ? buf.length - 1 : 0;
|
|
262
|
+
}
|
|
263
|
+
if (isNaN(start) || isNaN(end) || (start < 0) || (start >= buf.length) || (end < start) || (end >= buf.length)) {
|
|
264
|
+
download.destroy();
|
|
265
|
+
callback( new Error("Invalid byte range (" + start + '-' + end + ") for key: " + key + " (len: " + buf.length + ")"), null );
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
255
269
|
var range = buf.slice(start, end + 1);
|
|
256
270
|
var stream = new BufferStream(range);
|
|
257
271
|
callback(null, stream, { mod: 1, len: buf.length });
|
package/engines/RedisCluster.js
CHANGED
|
@@ -269,6 +269,20 @@ module.exports = Class.create({
|
|
|
269
269
|
return callback( err, null );
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
+
// validate byte range, now that we have the head info
|
|
273
|
+
if (isNaN(start) && !isNaN(end)) {
|
|
274
|
+
start = buf.length - end;
|
|
275
|
+
end = buf.length ? buf.length - 1 : 0;
|
|
276
|
+
}
|
|
277
|
+
else if (!isNaN(start) && isNaN(end)) {
|
|
278
|
+
end = buf.length ? buf.length - 1 : 0;
|
|
279
|
+
}
|
|
280
|
+
if (isNaN(start) || isNaN(end) || (start < 0) || (start >= buf.length) || (end < start) || (end >= buf.length)) {
|
|
281
|
+
download.destroy();
|
|
282
|
+
callback( new Error("Invalid byte range (" + start + '-' + end + ") for key: " + key + " (len: " + buf.length + ")"), null );
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
272
286
|
var range = buf.slice(start, end + 1);
|
|
273
287
|
var stream = new BufferStream(range);
|
|
274
288
|
callback(null, stream, { mod: 1, len: buf.length });
|
package/engines/S3.js
CHANGED
|
@@ -338,7 +338,14 @@ module.exports = Class.create({
|
|
|
338
338
|
this.logDebug(9, "Fetching ranged S3 stream: " + key, { start, end });
|
|
339
339
|
|
|
340
340
|
var params = { Key: this.extKey(key, orig_key) };
|
|
341
|
-
|
|
341
|
+
|
|
342
|
+
// convert start/end to HTTP range header string
|
|
343
|
+
var range = "bytes=";
|
|
344
|
+
if (!isNaN(start)) range += start;
|
|
345
|
+
range += '-';
|
|
346
|
+
if (!isNaN(end)) range += end;
|
|
347
|
+
|
|
348
|
+
var download = self.s3.getObject( Tools.mergeHashes(params, { Range: range }) ).createReadStream();
|
|
342
349
|
var proceed = false;
|
|
343
350
|
|
|
344
351
|
download.on('error', function(err) {
|
|
@@ -365,6 +372,20 @@ module.exports = Class.create({
|
|
|
365
372
|
return;
|
|
366
373
|
}
|
|
367
374
|
|
|
375
|
+
// validate byte range, now that we have the head info
|
|
376
|
+
if (isNaN(start) && !isNaN(end)) {
|
|
377
|
+
start = data.ContentLength - end;
|
|
378
|
+
end = data.ContentLength ? data.ContentLength - 1 : 0;
|
|
379
|
+
}
|
|
380
|
+
else if (!isNaN(start) && isNaN(end)) {
|
|
381
|
+
end = data.ContentLength ? data.ContentLength - 1 : 0;
|
|
382
|
+
}
|
|
383
|
+
if (isNaN(start) || isNaN(end) || (start < 0) || (start >= data.ContentLength) || (end < start) || (end >= data.ContentLength)) {
|
|
384
|
+
download.destroy();
|
|
385
|
+
callback( new Error("Invalid byte range (" + start + '-' + end + ") for key: " + key + " (len: " + data.ContentLength + ")"), null );
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
|
|
368
389
|
proceed = true;
|
|
369
390
|
callback( null, download, {
|
|
370
391
|
mod: Math.floor((new Date(data.LastModified)).getTime() / 1000),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pixl-server-storage",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.13",
|
|
4
4
|
"description": "A key/value/list storage component for the pixl-server framework.",
|
|
5
5
|
"author": "Joseph Huckaby <jhuckaby@gmail.com>",
|
|
6
6
|
"homepage": "https://github.com/jhuckaby/pixl-server-storage",
|
|
@@ -27,8 +27,7 @@
|
|
|
27
27
|
"pixl-perf": "^1.0.0",
|
|
28
28
|
"pixl-cache": "^1.0.1",
|
|
29
29
|
"async": "2.6.0",
|
|
30
|
-
"
|
|
31
|
-
"nearley": "2.11.0",
|
|
30
|
+
"nearley": "2.20.1",
|
|
32
31
|
"moo": "0.4.3",
|
|
33
32
|
"porter-stemmer": "0.9.1",
|
|
34
33
|
"unidecode": "0.1.8",
|
package/test/test-main.js
CHANGED
|
@@ -424,6 +424,7 @@ module.exports = {
|
|
|
424
424
|
},
|
|
425
425
|
|
|
426
426
|
function testStreamRange(test) {
|
|
427
|
+
// grab a range from within a stream, with both start and end specified
|
|
427
428
|
test.expect(14);
|
|
428
429
|
var self = this;
|
|
429
430
|
|
|
@@ -469,6 +470,100 @@ module.exports = {
|
|
|
469
470
|
} ); // putStream
|
|
470
471
|
},
|
|
471
472
|
|
|
473
|
+
function testStreamRangeStart(test) {
|
|
474
|
+
// grab a range from within a stream, with end missing
|
|
475
|
+
test.expect(14);
|
|
476
|
+
var self = this;
|
|
477
|
+
|
|
478
|
+
var key = 'spacer-stream.gif';
|
|
479
|
+
var filename = 'spacer.gif';
|
|
480
|
+
var spacerBuf = fs.readFileSync( __dirname + '/' + filename );
|
|
481
|
+
var spacerStream = fs.createReadStream( __dirname + '/' + filename );
|
|
482
|
+
|
|
483
|
+
test.ok( !!spacerBuf, "Got buffer from file" );
|
|
484
|
+
test.ok( typeof(spacerBuf) == 'object', "Buffer is an object" );
|
|
485
|
+
test.ok( spacerBuf.length > 0, "Buffer has size" );
|
|
486
|
+
test.ok( !!spacerStream, "Got read stream" );
|
|
487
|
+
|
|
488
|
+
this.storage.putStream( key, spacerStream, function(err) {
|
|
489
|
+
test.ok( !err, "No error creating stream: " + err );
|
|
490
|
+
|
|
491
|
+
var tempFile = __dirname + '/' + filename + '.streamtemp';
|
|
492
|
+
var outStream = fs.createWriteStream( tempFile );
|
|
493
|
+
|
|
494
|
+
self.storage.getStreamRange( key, 20, NaN, function(err, storageStream, streamInfo) {
|
|
495
|
+
test.ok( !err, "No error fetching stream: " + err );
|
|
496
|
+
test.ok( !!storageStream, "Got storage stream as 2nd arg");
|
|
497
|
+
test.ok( !!storageStream.pipe, "Storage stream has a pipe");
|
|
498
|
+
test.ok( !!streamInfo, "Info was provided as the 3rd arg");
|
|
499
|
+
test.ok( streamInfo.len == 43, "Info has correct data length");
|
|
500
|
+
test.ok( streamInfo.mod > 0, "Info has a non-zero mod date");
|
|
501
|
+
|
|
502
|
+
outStream.on('finish', function() {
|
|
503
|
+
var newSpacerBuf = fs.readFileSync( tempFile );
|
|
504
|
+
test.ok( newSpacerBuf.length == 23, "Stream range length is correct" );
|
|
505
|
+
test.ok( newSpacerBuf.equals( spacerBuf.slice(20) ), "Range buffer content is correct" );
|
|
506
|
+
|
|
507
|
+
self.storage.delete( key, function(err) {
|
|
508
|
+
test.ok( !err, "No error deleting stream key: " + err );
|
|
509
|
+
fs.unlinkSync( tempFile );
|
|
510
|
+
test.done();
|
|
511
|
+
} ); // delete
|
|
512
|
+
} ); // stream finish
|
|
513
|
+
|
|
514
|
+
storageStream.pipe( outStream );
|
|
515
|
+
|
|
516
|
+
} ); // getStream
|
|
517
|
+
} ); // putStream
|
|
518
|
+
},
|
|
519
|
+
|
|
520
|
+
function testStreamRangeEnd(test) {
|
|
521
|
+
// grab a range from within a stream, with start missing
|
|
522
|
+
test.expect(14);
|
|
523
|
+
var self = this;
|
|
524
|
+
|
|
525
|
+
var key = 'spacer-stream.gif';
|
|
526
|
+
var filename = 'spacer.gif';
|
|
527
|
+
var spacerBuf = fs.readFileSync( __dirname + '/' + filename );
|
|
528
|
+
var spacerStream = fs.createReadStream( __dirname + '/' + filename );
|
|
529
|
+
|
|
530
|
+
test.ok( !!spacerBuf, "Got buffer from file" );
|
|
531
|
+
test.ok( typeof(spacerBuf) == 'object', "Buffer is an object" );
|
|
532
|
+
test.ok( spacerBuf.length > 0, "Buffer has size" );
|
|
533
|
+
test.ok( !!spacerStream, "Got read stream" );
|
|
534
|
+
|
|
535
|
+
this.storage.putStream( key, spacerStream, function(err) {
|
|
536
|
+
test.ok( !err, "No error creating stream: " + err );
|
|
537
|
+
|
|
538
|
+
var tempFile = __dirname + '/' + filename + '.streamtemp';
|
|
539
|
+
var outStream = fs.createWriteStream( tempFile );
|
|
540
|
+
|
|
541
|
+
self.storage.getStreamRange( key, NaN, 10, function(err, storageStream, streamInfo) {
|
|
542
|
+
test.ok( !err, "No error fetching stream: " + err );
|
|
543
|
+
test.ok( !!storageStream, "Got storage stream as 2nd arg");
|
|
544
|
+
test.ok( !!storageStream.pipe, "Storage stream has a pipe");
|
|
545
|
+
test.ok( !!streamInfo, "Info was provided as the 3rd arg");
|
|
546
|
+
test.ok( streamInfo.len == 43, "Info has correct data length");
|
|
547
|
+
test.ok( streamInfo.mod > 0, "Info has a non-zero mod date");
|
|
548
|
+
|
|
549
|
+
outStream.on('finish', function() {
|
|
550
|
+
var newSpacerBuf = fs.readFileSync( tempFile );
|
|
551
|
+
test.ok( newSpacerBuf.length == 10, "Stream range length is correct" );
|
|
552
|
+
test.ok( newSpacerBuf.equals( spacerBuf.slice(43 - 10) ), "Range buffer content is correct" );
|
|
553
|
+
|
|
554
|
+
self.storage.delete( key, function(err) {
|
|
555
|
+
test.ok( !err, "No error deleting stream key: " + err );
|
|
556
|
+
fs.unlinkSync( tempFile );
|
|
557
|
+
test.done();
|
|
558
|
+
} ); // delete
|
|
559
|
+
} ); // stream finish
|
|
560
|
+
|
|
561
|
+
storageStream.pipe( outStream );
|
|
562
|
+
|
|
563
|
+
} ); // getStream
|
|
564
|
+
} ); // putStream
|
|
565
|
+
},
|
|
566
|
+
|
|
472
567
|
function testPutMulti(test) {
|
|
473
568
|
// test storing multiple keys at once
|
|
474
569
|
test.expect(1);
|
package/transaction.js
CHANGED
|
@@ -8,9 +8,9 @@ var Path = require("path");
|
|
|
8
8
|
var cp = require("child_process");
|
|
9
9
|
var os = require("os");
|
|
10
10
|
var async = require('async');
|
|
11
|
-
var mkdirp = require('mkdirp');
|
|
12
11
|
var Class = require("pixl-class");
|
|
13
12
|
var Tools = require("pixl-tools");
|
|
13
|
+
var mkdirp = Tools.mkdirp;
|
|
14
14
|
|
|
15
15
|
// Transaction support is implemented as a mixin to Storage
|
|
16
16
|
// Config Keys:
|