pixl-server-storage 3.0.11 → 3.0.12

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 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
 
@@ -249,6 +249,13 @@ module.exports = Class.create({
249
249
  }
250
250
 
251
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
+ }
252
259
  if ((start < 0) || (start >= buf.length) || (end < start) || (end >= buf.length)) {
253
260
  download.destroy();
254
261
  callback( new Error("Invalid byte range (" + start + '-' + end + ") for key: " + key + " (len: " + buf.length + ")"), null );
@@ -481,6 +481,13 @@ module.exports = Class.create({
481
481
  }
482
482
 
483
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
+ }
484
491
  if ((start < 0) || (start >= stats.size) || (end < start) || (end >= stats.size)) {
485
492
  download.destroy();
486
493
  callback( new Error("Invalid byte range (" + start + '-' + end + ") for key: " + key + " (len: " + stats.size + ")"), null );
package/engines/Redis.js CHANGED
@@ -253,6 +253,13 @@ module.exports = Class.create({
253
253
  }
254
254
 
255
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
+ }
256
263
  if ((start < 0) || (start >= buf.length) || (end < start) || (end >= buf.length)) {
257
264
  download.destroy();
258
265
  callback( new Error("Invalid byte range (" + start + '-' + end + ") for key: " + key + " (len: " + buf.length + ")"), null );
@@ -270,6 +270,13 @@ module.exports = Class.create({
270
270
  }
271
271
 
272
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
+ }
273
280
  if ((start < 0) || (start >= buf.length) || (end < start) || (end >= buf.length)) {
274
281
  download.destroy();
275
282
  callback( new Error("Invalid byte range (" + start + '-' + end + ") for key: " + key + " (len: " + buf.length + ")"), null );
package/engines/S3.js CHANGED
@@ -366,6 +366,13 @@ module.exports = Class.create({
366
366
  }
367
367
 
368
368
  // validate byte range, now that we have the head info
369
+ if (isNaN(start) && !isNaN(end)) {
370
+ start = data.ContentLength - end;
371
+ end = data.ContentLength ? data.ContentLength - 1 : 0;
372
+ }
373
+ else if (!isNaN(start) && isNaN(end)) {
374
+ end = data.ContentLength ? data.ContentLength - 1 : 0;
375
+ }
369
376
  if ((start < 0) || (start >= data.ContentLength) || (end < start) || (end >= data.ContentLength)) {
370
377
  download.destroy();
371
378
  callback( new Error("Invalid byte range (" + start + '-' + end + ") for key: " + key + " (len: " + data.ContentLength + ")"), null );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixl-server-storage",
3
- "version": "3.0.11",
3
+ "version": "3.0.12",
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",
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);