domotz-remote-pawn
Version:
Domotz Agent
168 lines (145 loc) • 4.41 kB
JavaScript
"use strict";
var util = require ("util");
var stream = require ("stream");
var fs = require ("fs");
var path = require ("path");
var errors = require ("../../protocol/errors");
var packets = require ("../../protocol/packets");
var Writer = require ("../../protocol/server/writer");
var PutStream = module.exports = function (currFiles, helper, message,
globalOptions, getStream){
//WRQ
if (!helper) return;
stream.Writable.call (this);
this._isWRQ = false;
this._finished = false;
this._writer = null;
this._size = null;
this._continue = false;
this._closed = false;
this._sizeSet = false;
this._currFiles = currFiles;
//Validate the request
try{
message = packets.rrq.deserialize (message);
}catch (error){
return helper.sendErrorAndClose (error);
}
//Check whether the file can be read
if (this._currFiles.put[message.file]){
return helper.sendErrorAndClose (errors.ECURPUT);
}
this._currFiles.get[message.file] = true;
getStream.method = "GET";
getStream.file = message.file;
var me = this;
this.on ("unpipe", function (){
//After a finish event the readable stream unpipes the writable stream
if (me._finished) return;
//The user has called manually to unpipe()
//Abort file transfer
if (me._writer) me._writer.abort ();
});
this.on ("finish", function (){
//The finish event is emitted before unpipe
//This handler is the first that is called when the finish event is
//emitted
me._finished = true;
});
//Link the streams each other. The put stream is only used to send data to the
//client but the "connection" and all its related events occur in the get
//stream
this._gs = getStream;
getStream._ps = this;
this._createWriter (helper, message, globalOptions);
};
util.inherits (PutStream, stream.Writable);
PutStream.prototype._abort = function (error){
this._writer.abort (error);
};
PutStream.prototype._close = function (){
this._writer.close ();
};
PutStream.prototype._createWriter = function (helper, message, globalOptions){
var me = this;
this._writer = new Writer ({
helper: helper,
message: message,
globalOptions: globalOptions
});
//The events are emitted using the get stream
this._writer.onError = function (error){
delete me._currFiles.get[me._gs.file];
me._gs.emit ("close");
me._gs.emit ("error", error);
};
this._writer.onAbort = function (){
delete me._currFiles.get[me._gs.file];
me._gs.emit ("close");
me._gs.emit ("abort");
};
this._writer.onClose = function (){
delete me._currFiles.get[me._gs.file];
me._closed = true;
me._gs.emit ("close");
};
this._writer.onStats = function (stats){
me._gs.stats = stats;
me.onReady ();
};
this._writer.onContinue = function (){
me._continue = true;
};
};
PutStream.prototype._write = function (chunk, encoding, cb){
if (this._continue){
this._writer.send (chunk, cb);
}else{
//Wait until the writer is ready to send data
var me = this;
this._writer.onContinue = function (){
me._continue = true;
me._writer.send (chunk, cb);
};
}
};
PutStream.prototype.setSize = function (size){
if (this._isWRQ) throw new Error ("Only GET requests can set the size");
//Sanity check
if (this._gs._aborted) return;
if (this._sizeSet) throw new Error ("The size was previously set");
this._sizeSet = true;
if (size === 0){
//Empty file
//The _write() function is never called so the get request is never
//answered
var end = this.end;
var me = this;
this.end = function (){
if (me._continue){
//Send an empty buffer
me._writer.send (new Buffer (0), function (){
end.call (me);
});
}else{
//Wait until the writer is ready to end
this._writer.onContinue = function (){
//Send an empty buffer
me._writer.send (new Buffer (0), function (){
end.call (me);
});
};
}
};
}
//The request was "paused" when the request listener was emitted. The call to
//setSize() resumes it in the case of GET requests
this._writer.continueRequest (size);
};
PutStream.prototype.setUserExtensions = function (userExtensions){
if (this._isWRQ){
this._gs._reader._responseUserExtensions = userExtensions;
}else{
this._writer._responseUserExtensions = userExtensions;
}
};