hyperdrive icon indicating copy to clipboard operation
hyperdrive copied to clipboard

Occasional "Download was canceled" error when start/end range is specified.

Open ikreymer opened this issue 6 years ago • 8 comments

The following snippet occasionally results in this error:

var ram = require('random-access-memory');
var hyperdrive = require('hyperdrive');
var discovery = require('hyperdiscovery');

var opts = {"latest": false,
            "sparse": true}

var drive = hyperdrive(ram, '778f8d955175c92e4ced5e4f5563f69bfec0c86cc6f670352c457943666fe639');

drive.on("ready", function() {
  var sw = discovery(drive);

  var readOpts = {"start": 13, "end": 36}

  var stream = drive.createReadStream("dat.json", readOpts);

  stream.pipe(process.stdout);

  stream.on("error", function(err) {
    console.log(err);
  });

  stream.on("end", function() {
    process.stdout.write("\ndone\n");
  });

});

Sometimes this works correctly, and the output should be:

"Dat command line demo",
done

but occasionally this results in an error like this:

Error: Download was cancelled
    at Feed.undownload (http-range-dat/node_modules/hypercore/index.js:332:30)
    at onend (http-range-dat/node_modules/hyperdrive/index.js:498:24)
    at done (http-range-dat/node_modules/hypercore/index.js:561:5)
    at http-range-dat/node_modules/hypercore/index.js:529:40
    at onroot (http-range-dat/node_modules/hypercore/index.js:575:30)
    at onleftchild (http-range-dat/node_modules/hypercore/index.js:594:7)
    at Storage.getNode (http-range-dat/node_modules/hypercore/lib/storage.js:112:24)
    at onroot (http-range-dat/node_modules/hypercore/index.js:583:19)
    at onleftchild (http-range-dat/node_modules/hypercore/index.js:591:7)
    at Storage.getNode (http-range-dat/node_modules/hypercore/lib/storage.js:112:24)

I'm using this in the context of an http request (https://github.com/ikreymer/http-range-dat/) where this seems to happen more frequently. Perhaps I'm not using this correctly?

ikreymer avatar Apr 15 '18 22:04 ikreymer

I may be experiencing this issue as well, using dat-node. "Download was cancelled" error, and I am specifying a start/end range.

magland avatar May 24 '18 18:05 magland

Some more details... I am doing:

var stream = dat.archive.createReadStream(file_path, {start:start,end:end-1}); ...

The first time I do it, no problem. The second time (once the file already partially exists on local machine), that's when I get the "Download was cancelled" error.

magland avatar May 24 '18 18:05 magland

I managed to do a workaround, by wrapping my createReadStream code in

setTimeout(function() { ..... },0);

not sure why that's needed, but it certainly is in my case.

Update: Actually, that only works sometimes. As the original post indicates... this is an unpredictable issue.

magland avatar May 24 '18 18:05 magland

I solved the problem by commenting out the following code in hyperdrive/index.js:

if (length > -1 && length < stat.size) {
            self.content.seek(byteOffset + length, {start: start, end: end}, onend)
          }

Not sure exactly what this line does, but without it my app works beautifully. Do you want me to create a pull request @mafintosh ?

magland avatar May 24 '18 19:05 magland

@magland open a PR, we'll discuss it there. If you add a test that'd be amazing. otherwise we'll manage :)

mafintosh avatar May 29 '18 14:05 mafintosh

Will do this afternoon. Thx.

magland avatar May 29 '18 14:05 magland

Thanks @magland I can also confirm that by commenting out those lines, I am also no longer seeing the "Download was canceled" error either! Nice find! Always interesting when commenting out code fixes things, but I suppose if download already takes a range, the seek may be unnecessary? Look forward to seeing this fixed.

ikreymer avatar Jun 03 '18 02:06 ikreymer

I believe I have found and fixed the problem. I will open a PR.

Simply removing the lines discussed above does not fix the issue; it just makes createReadStream ignore the fact that opts.end was specified. The issue is that createReadStream was not accounting for opts.start correctly when seeking.

The code in question:

if (length > -1 && length < stat.size) {
      self.content.seek(byteOffset + length, {start: start, end: end}, onend)
}

should be:

if (length > -1 && length < stat.size) {
      self.content.seek(byteOffset + length + (opts.start ? opts.start : 0), {start: start, end: end}, onend)
}

Also, onend needed to be changed from this:

range = self.content.download({start: start, end: index, linear: true}, ondownload)

To this:

range = self.content.download({start: start, end: index + 1, linear: true}, ondownload)

The change to index + 1 accounts for the fact that the index returned from seek is the index the data is contained in and download takes a non-inclusive end option.

eu1444 avatar Aug 30 '18 22:08 eu1444