Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Persist DHT nodes #1431

Open
wants to merge 9 commits into
base: master
from

Move DHT persistence logic into lib/ module

Increases cleanliness of index.js.
  • Loading branch information
bookmoons committed Jul 30, 2018
commit a7ed3a26867806759cf2b702cc6be7519a19d9d4
@@ -10,10 +10,8 @@ var debug = require('debug')('webtorrent')
var DHT = require('bittorrent-dht/client') // browser exclude
var EventEmitter = require('events').EventEmitter
var extend = require('xtend')
var fs = require('fs')
var inherits = require('inherits')
var loadIPSet = require('load-ip-set') // browser exclude
var mkdirp = require('mkdirp')
var parallel = require('run-parallel')
var parseTorrent = require('parse-torrent')
var path = require('path')
@@ -22,6 +20,7 @@ var randombytes = require('randombytes')
var speedometer = require('speedometer')
var zeroFill = require('zero-fill')

var dhtPersist = require('./lib/dhtpersist') // browser exclude

This comment has been minimized.

Copy link
@KayleePop

KayleePop Aug 3, 2018

Contributor

It says browser exclude, but it's not actually excluded in the browser field of package.json?

like this

"browser": {
  "./lib/dhtpersist": false
}

This comment has been minimized.

Copy link
@bookmoons

bookmoons Aug 6, 2018

Author

Misunderstood what that was about. I was thinking it was some control message to the bundler. I've updated.

var TCPPool = require('./lib/tcp-pool') // browser exclude
var Torrent = require('./lib/torrent')

@@ -128,90 +127,19 @@ function WebTorrent (opts) {
self._downloadSpeed = speedometer()
self._uploadSpeed = speedometer()

// DHT state save location
var dhtSaveFile

var savingDhtState = false
function saveDhtState () {
if (savingDhtState) return
if (!self.dht) return // Quell after destroy
savingDhtState = true
var dhtState = self.dht.toJSON()
var dhtStateJson = JSON.stringify(dhtState)
mkdirp(
path.dirname(dhtSaveFile),
function handleDhtSaveDirCreated (err) {
if (err) {
savingDhtState = false
return
}
fs.writeFile(
dhtSaveFile,
dhtStateJson,
function handleDhtStateWritten () {
savingDhtState = false
}
)
}
)
}

function readDhtState (file) {
try {
return fs.readFileSync(file)
} catch (e) {
switch (e.code) {
case 'EACCES':
case 'EISDIR':
case 'ENOENT':
case 'EPERM':
return null
default:
throw e
}
}
}

function parseDhtState (dhtStateJson) {
try {
return JSON.parse(dhtStateJson)
} catch (e) {
if (e instanceof SyntaxError) return null
else throw e
}
}

function loadDhtState (file) {
var dhtStateJson = readDhtState(file)
if (!dhtStateJson) return null
var dhtState = parseDhtState(dhtStateJson)
if (!dhtState) return null
return dhtState
}

function loadDhtNodes (file) {
var dhtState = loadDhtState(file)
if (!dhtState) return null
if (!('nodes' in dhtState)) return null
var nodes = dhtState.nodes
if (!Array.isArray(nodes)) return null
if (nodes.length === 0) return null // Don't load an empty nodes list
return nodes
}

if (opts.dht !== false && typeof DHT === 'function' /* browser exclude */) {
var dhtOpts = extend({ nodeId: self.nodeId }, opts.dht)

if (opts.dhtState) {
// Construct state save location
dhtSaveFile =
self.dhtSaveFile =
opts.dhtState === true
? path.join(appDataFolder('webtorrent'), 'dht.json')
: opts.dhtState

if (!('bootstrap' in dhtOpts)) {
// Load persisted state
var nodes = loadDhtNodes(dhtSaveFile)
var nodes = dhtPersist.loadNodes(self.dhtSaveFile)
if (nodes) dhtOpts.bootstrap = nodes
}
}
@@ -222,7 +150,9 @@ function WebTorrent (opts) {
if (opts.dhtState) {
// Persist state periodically
var saveInterval = 15 * 60 * 1000 // 15 minutes
self.saveDhtStateTimer = setInterval(saveDhtState, saveInterval)
self.saveDhtStateTimer = setInterval(function saveDhtState () {
dhtPersist.save(self.dht, self.dhtSaveFile)
}, saveInterval)
}

self.dht.once('error', function (err) {
@@ -499,6 +429,20 @@ WebTorrent.prototype.address = function () {
: { address: '0.0.0.0', family: 'IPv4', port: 0 }
}

/**
* Persist DHT state to disk.
* No effect if DHT is not loaded.
*/
WebTorrent.prototype.saveDhtState = function (cb) {
if (
this.dht !== false &&
this.dhtSaveFile &&
typeof DHT === 'function' /* browser exclude */
) {
dhtPersist.save(this.dht, this.dhtSaveFile, cb)
}
}

/**
* Destroy the client, including all torrents and connections to peers.
* @param {function} cb
@@ -0,0 +1,78 @@
var fs = require('fs')
var mkdirp = require('mkdirp')
var path = require('path')

var savingDhtState = false
function saveDhtState (dht, file, cb) {
if (savingDhtState) return
if (!dht) return // Quell after destroy
savingDhtState = true
var dhtState = dht.toJSON()
var dhtStateJson = JSON.stringify(dhtState)
mkdirp(
path.dirname(file),
function handleDhtSaveDirCreated (err) {
if (err) {
savingDhtState = false
if (cb) cb(err)
return
}
fs.writeFile(
file,
dhtStateJson,
function handleDhtStateWritten () {
savingDhtState = false
if (cb) cb(null)
}
)
}
)
}

function readDhtState (file) {
try {
return fs.readFileSync(file)
} catch (e) {
switch (e.code) {
case 'EACCES':
case 'EISDIR':
case 'ENOENT':
case 'EPERM':
return null
default:
throw e
}
}
}

function parseDhtState (dhtStateJson) {
try {
return JSON.parse(dhtStateJson)
} catch (e) {
if (e instanceof SyntaxError) return null
else throw e
}
}

function loadDhtState (file) {
var dhtStateJson = readDhtState(file)
if (!dhtStateJson) return null
var dhtState = parseDhtState(dhtStateJson)
if (!dhtState) return null
return dhtState
}

function loadDhtNodes (file) {
var dhtState = loadDhtState(file)
if (!dhtState) return null
if (!('nodes' in dhtState)) return null
var nodes = dhtState.nodes
if (!Array.isArray(nodes)) return null
if (nodes.length === 0) return null // Don't load an empty nodes list
return nodes
}

module.exports = {
save: saveDhtState,
loadNodes: loadDhtNodes
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.