UNPKG

makedrive

Version:
302 lines (255 loc) 9.75 kB
var SyncMessage = require('../../lib/syncmessage'); var rsync = require('../../lib/rsync'); var rsyncUtils = rsync.utils; var rsyncOptions = require('../../lib/constants').rsyncDefaults; var serializeDiff = require('../../lib/diff').serialize; var deserializeDiff = require('../../lib/diff').deserialize; var states = require('./sync-states'); var steps = require('./sync-steps'); var dirname = require('../../lib/filer').Path.dirname; var async = require('../../lib/async-lite'); var fsUtils = require('../../lib/fs-utils'); function onError(syncManager, err) { syncManager.session.step = steps.FAILED; syncManager.sync.onError(err); } // Checks if path is in masterPath function hasCommonPath(masterPath, path) { if(masterPath === path) { return true; } if(path === '/') { return false; } return hasCommonPath(masterPath, dirname(path)); } function handleRequest(syncManager, data) { var fs = syncManager.fs; var sync = syncManager.sync; var session = syncManager.session; function handleChecksumRequest() { var srcList = session.srcList = data.content.srcList; session.path = data.content.path; fs.modifiedPath = null; sync.onSyncing(); rsync.checksums(fs, session.path, srcList, rsyncOptions, function(err, checksums) { if (err) { return onError(syncManager, err); } session.step = steps.PATCH; var message = SyncMessage.request.diffs; message.content = {checksums: checksums}; syncManager.send(message.stringify()); }); } function handleDiffRequest() { rsync.diff(fs, session.path, data.content.checksums, rsyncOptions, function(err, diffs) { if(err){ return onError(syncManager, err); } session.step = steps.PATCH; var message = SyncMessage.response.diffs; message.content = {diffs: serializeDiff(diffs)}; syncManager.send(message.stringify()); }); } if(data.is.chksum && session.is.ready && (session.is.synced || session.is.failed)) { // DOWNSTREAM - CHKSUM handleChecksumRequest(); } else if(data.is.diffs && session.is.syncing && session.is.diffs) { // UPSTREAM - DIFFS handleDiffRequest(); } else { onError(syncManager, new Error('Failed to sync with the server. Current step is: ' + session.step + '. Current state is: ' + session.state)); } } function handleResponse(syncManager, data) { var fs = syncManager.fs; var sync = syncManager.sync; var session = syncManager.session; function resendChecksums() { if(!session.srcList) { // Sourcelist was somehow reset, the entire downstream sync // needs to be restarted session.step = steps.FAILED; syncManager.send(SyncMessage.response.reset.stringify()); return onError(syncManager, new Error('Fatal Error: Could not sync filesystem from server...trying again!')); } rsync.checksums(fs, session.path, session.srcList, rsyncOptions, function(err, checksums) { if(err) { syncManager.send(SyncMessage.response.reset.stringify()); return onError(syncManager, err); } var message = SyncMessage.request.diffs; message.content = {checksums: checksums}; syncManager.send(message.stringify()); }); } function handleSrcListResponse() { session.state = states.SYNCING; session.step = steps.INIT; session.path = data.content.path; sync.onSyncing(); rsync.sourceList(fs, session.path, rsyncOptions, function(err, srcList) { if(err){ syncManager.send(SyncMessage.request.reset.stringify()); return onError(syncManager, err); } session.step = steps.DIFFS; var message = SyncMessage.request.chksum; message.content = {srcList: srcList}; syncManager.send(message.stringify()); }); } function handlePatchAckResponse() { var syncedPaths = data.content.syncedPaths; session.state = states.READY; session.step = steps.SYNCED; function stampChecksum(path, callback) { fs.lstat(path, function(err, stats) { if(err) { if(err.code !== 'ENOENT') { return callback(err); } // Non-existent paths (usually due to renames or // deletes that are included in the syncedPaths) // cannot be stamped with a checksum return callback(); } if(!stats.isFile()) { return callback(); } rsyncUtils.getChecksum(fs, path, function(err, checksum) { if(err) { return callback(err); } fsUtils.setChecksum(fs, path, checksum, callback); }); }); } // As soon as an upstream sync happens, the files synced // become the last synced versions and must be stamped // with their checksums to version them async.eachSeries(syncedPaths, stampChecksum, function(err) { if(err) { return onError(syncManager, err); } sync.onCompleted(data.content.syncedPaths); }); } function handlePatchResponse() { var modifiedPath = fs.modifiedPath; fs.modifiedPath = null; // If there was a change to the filesystem that shares a common path with // the path being synced, regenerate the checksums and send them // (even if it is the initial one) if(modifiedPath && hasCommonPath(session.path, modifiedPath)) { return resendChecksums(); } var diffs = data.content.diffs; diffs = deserializeDiff(diffs); rsync.patch(fs, session.path, diffs, rsyncOptions, function(err, paths) { if (err) { var message = SyncMessage.response.reset; syncManager.send(message.stringify()); return onError(syncManager, err); } if(paths.needsUpstream.length) { session.needsUpstream = paths.needsUpstream; } rsyncUtils.generateChecksums(fs, paths.synced, true, function(err, checksums) { if(err) { var message = SyncMessage.response.reset; syncManager.send(message.stringify()); return onError(syncManager, err); } var message = SyncMessage.response.patch; message.content = {checksums: checksums}; syncManager.send(message.stringify()); }); }); } function handleVerificationResponse() { session.srcList = null; session.step = steps.SYNCED; var needsUpstream = session.needsUpstream; delete session.needsUpstream; sync.onCompleted(null, needsUpstream); } function handleUpstreamResetResponse() { var message = SyncMessage.request.sync; message.content = {path: session.path}; syncManager.send(message.stringify()); } if(data.is.sync) { // UPSTREAM - INIT handleSrcListResponse(); } else if(data.is.patch && session.is.syncing && session.is.patch) { // UPSTREAM - PATCH handlePatchAckResponse(); } else if(data.is.diffs && session.is.ready && session.is.patch) { // DOWNSTREAM - PATCH handlePatchResponse(); } else if(data.is.verification && session.is.ready && session.is.patch) { // DOWNSTREAM - PATCH VERIFICATION handleVerificationResponse(); } else if (data.is.reset && session.is.failed) { handleUpstreamResetResponse(); } else { onError(syncManager, new Error('Failed to sync with the server. Current step is: ' + session.step + '. Current state is: ' + session.state)); } } function handleError(syncManager, data) { var sync = syncManager.sync; var session = syncManager.session; var message = SyncMessage.response.reset; // DOWNSTREAM - ERROR if((((data.is.srclist && session.is.synced)) || (data.is.diffs && session.is.patch) && (session.is.ready || session.is.syncing))) { session.state = states.READY; session.step = steps.SYNCED; syncManager.send(message.stringify()); onError(syncManager, new Error('Could not sync filesystem from server... trying again')); } else if(data.is.verification && session.is.patch && session.is.ready) { syncManager.send(message.stringify()); onError(syncManager, new Error('Could not sync filesystem from server... trying again')); } else if(data.is.locked && session.is.ready && session.is.synced) { // UPSTREAM - LOCK onError(syncManager, new Error('Current sync in progress! Try again later!')); } else if(((data.is.chksum && session.is.diffs) || (data.is.patch && session.is.patch)) && session.is.syncing) { // UPSTREAM - ERROR var message = SyncMessage.request.reset; syncManager.send(message.stringify()); onError(syncManager, new Error('Could not sync filesystem from server... trying again')); } else if(data.is.maxsizeExceeded) { // We are only emitting the error since this is can be sync again from the client syncManager.sync.emit('error', new Error('Maximum file size exceeded')); } else if(data.is.interrupted && session.is.syncing) { // SERVER INTERRUPTED SYNC (LOCK RELEASED EARLY) sync.onInterrupted(); } else { onError(syncManager, new Error('Failed to sync with the server. Current step is: ' + session.step + '. Current state is: ' + session.state)); } } function handleMessage(syncManager, data) { try { data = JSON.parse(data); data = SyncMessage.parse(data); } catch(e) { return onError(syncManager, e); } if (data.is.request) { handleRequest(syncManager, data); } else if(data.is.response){ handleResponse(syncManager, data); } else if(data.is.error){ handleError(syncManager, data); } else { onError(syncManager, new Error('Cannot handle message')); } } module.exports = handleMessage;