blob: 1ebd2781230ad63627cea7df8e86d0888e0f3879 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
var debug = require('debug')('reader:vanadium:service');
var Buffer = require('buffer').Buffer;
var store = require('../pdf-store');
var assert = require('assert');
var format = require('format');
var window = require('global/window');
module.exports = Service;
function Service(client) {
if (!(this instanceof Service)) {
return new Service(client);
}
var service = this;
// NOTE: Attributes prefixed with an underscore are ignored by the Vanadium
// reflection invoker.
service._client = client;
}
// TODO(jasoncampbell): Explore why exceptions are not bubbling up into a
// context that makes sense, for instance serverCall missing here causes
// problems but there is no where to hook in to grab errors since they are only
// logged by the vanadium module.
//
// SEE: https://github.com/vanadium/issues/issues/587
Service.prototype.announce = function(context, serverCall, name, callback) {
var service = this;
// NOTE: It would be ideal if simply putting to the peer list would do all the
// set/connection work automatically.
debug('announce called by: %s', name);
service._client.emit('service:announce', name);
callback(null, 'ACK');
};
Service.prototype.savePDF = function(ctx, serverCall, $stream, meta, callback) {
var service = this;
debug('savePDF called: %o', meta);
// TODO(jasoncampbell): send these errors back to the client via the callback
assert.ok(meta.hash, 'meta.hash required');
assert.ok(meta.name, 'meta.name required');
assert.ok(meta.size, 'meta.size required');
assert.ok(meta.type, 'meta.type required');
// Check if this file is already stored in the local file system.
store.has(meta.hash, function onexists(err, exists) {
if (err) {
// TODO(jasoncampbell): don't send the DB error directly, wrap it or
// create a new error that allows the DB issue to be surfaced on this
// service and send a generic error back to the client.
return callback(err);
}
// Politely cancel the RPC/stream if the file exists.
if (exists) {
var message = format('PDF file "%s" already exists.', meta.hash);
callback(null, message);
return;
}
debug('Buffering PDF: %d', meta.size);
// TODO(jasoncampbell): use eos to handle err/finish on stream.
var buffer = new Buffer(meta.size);
var start = 0;
$stream.on('error', function onerror(err) {
debug('$stream error: %s', err.stack);
callback(err);
});
// Streamed data from WSPR is emitted as Unint8Array objects instead of
// Buffers (which is what the client piped to the RPC stream) this breaks
// compatibility with stream utilities like through, etc. It's possible to
// change this in the vanadium code where the data could be converted to a
// buffer instead of passing a Unint8Array directly (similar to the code
// below) or by defining a VDL for this application it might be possible to
// provide information on the type that should be emitted.
//
// Ideally the $stream here could be piped directly without issue:
//
// $stream.pipe(through(write, flush));
//
$stream.on('data', function(data) {
// create a new buffer from the unint8Array array and then copy into
// (concatenat) the buffer defined above.
var buff = new Buffer(data);
buff.copy(buffer, start);
start += buff.length;
debug('recieved: %d of %d', start, meta.size);
if (start > meta.size) {
var err = new Error('PDF stream exceeded content length');
$stream.emit('error', err);
$stream.pause();
}
});
$stream.on('end', function() {
debug('end PDF stream');
// Create a Blob from the concatenated buffer above (it's not possible to
// create a File object directly).
// TODO(jasoncampbell): store file meta in syncbase separately.
var blob = new window.Blob([ buffer ], {
type: meta.type
});
callback(null, 'Success.');
service._client.emit('service:pdf', meta, blob);
});
});
};