|
|
@ -50,101 +50,53 @@ util.inherits(GrpcClientStream, Duplex); |
|
|
|
function GrpcClientStream(call, options) { |
|
|
|
function GrpcClientStream(call, options) { |
|
|
|
Duplex.call(this, options); |
|
|
|
Duplex.call(this, options); |
|
|
|
var self = this; |
|
|
|
var self = this; |
|
|
|
// Indicates that we can start reading and have not received a null read
|
|
|
|
var finished = false; |
|
|
|
var can_read = false; |
|
|
|
|
|
|
|
// Indicates that a read is currently pending
|
|
|
|
// Indicates that a read is currently pending
|
|
|
|
var reading = false; |
|
|
|
var reading = false; |
|
|
|
// Indicates that we can call startWrite
|
|
|
|
|
|
|
|
var can_write = false; |
|
|
|
|
|
|
|
// Indicates that a write is currently pending
|
|
|
|
// Indicates that a write is currently pending
|
|
|
|
var writing = false; |
|
|
|
var writing = false; |
|
|
|
this._call = call; |
|
|
|
this._call = call; |
|
|
|
/** |
|
|
|
/** |
|
|
|
* Callback to handle receiving a READ event. Pushes the data from that event |
|
|
|
* Callback to be called when a READ event is received. Pushes the data onto |
|
|
|
* onto the read queue and starts reading again if applicable. |
|
|
|
* the read queue and starts reading again if applicable |
|
|
|
* @param {grpc.Event} event The READ event object |
|
|
|
* @param {grpc.Event} event READ event object |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
function readCallback(event) { |
|
|
|
function readCallback(event) { |
|
|
|
|
|
|
|
if (finished) { |
|
|
|
|
|
|
|
self.push(null); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
var data = event.data; |
|
|
|
var data = event.data; |
|
|
|
if (self.push(data)) { |
|
|
|
if (self.push(data) && data != null) { |
|
|
|
if (data == null) { |
|
|
|
self._call.startRead(readCallback); |
|
|
|
// Disable starting to read after null read was received
|
|
|
|
|
|
|
|
can_read = false; |
|
|
|
|
|
|
|
reading = false; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
call.startRead(readCallback); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
// Indicate that reading can be resumed by calling startReading
|
|
|
|
|
|
|
|
reading = false; |
|
|
|
reading = false; |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Initiate a read, which continues until self.push returns false (indicating |
|
|
|
|
|
|
|
* that reading should be paused) or data is null (indicating that there is no |
|
|
|
|
|
|
|
* more data to read). |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
function startReading() { |
|
|
|
|
|
|
|
call.startRead(readCallback); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// TODO(mlumish): possibly change queue implementation due to shift slowness
|
|
|
|
|
|
|
|
var write_queue = []; |
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Write the next chunk of data in the write queue if there is one. Otherwise |
|
|
|
|
|
|
|
* indicate that there is no pending write. When the write succeeds, this |
|
|
|
|
|
|
|
* function is called again. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
function writeNext() { |
|
|
|
|
|
|
|
if (write_queue.length > 0) { |
|
|
|
|
|
|
|
writing = true; |
|
|
|
|
|
|
|
var next = write_queue.shift(); |
|
|
|
|
|
|
|
var writeCallback = function(event) { |
|
|
|
|
|
|
|
next.callback(); |
|
|
|
|
|
|
|
writeNext(); |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
call.startWrite(next.chunk, writeCallback, 0); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
writing = false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
call.startInvoke(function(event) { |
|
|
|
call.invoke(function(event) { |
|
|
|
can_read = true; |
|
|
|
|
|
|
|
can_write = true; |
|
|
|
|
|
|
|
startReading(); |
|
|
|
|
|
|
|
writeNext(); |
|
|
|
|
|
|
|
}, function(event) { |
|
|
|
|
|
|
|
self.emit('metadata', event.data); |
|
|
|
self.emit('metadata', event.data); |
|
|
|
}, function(event) { |
|
|
|
}, function(event) { |
|
|
|
|
|
|
|
finished = true; |
|
|
|
self.emit('status', event.data); |
|
|
|
self.emit('status', event.data); |
|
|
|
}, 0); |
|
|
|
}, 0); |
|
|
|
this.on('finish', function() { |
|
|
|
this.on('finish', function() { |
|
|
|
call.writesDone(function() {}); |
|
|
|
call.writesDone(function() {}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
/** |
|
|
|
/** |
|
|
|
* Indicate that reads should start, and start them if the INVOKE_ACCEPTED |
|
|
|
* Start reading if there is not already a pending read. Reading will |
|
|
|
* event has been received. |
|
|
|
* continue until self.push returns false (indicating reads should slow |
|
|
|
|
|
|
|
* down) or the read data is null (indicating that there is no more data). |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
this._enableRead = function() { |
|
|
|
this.startReading = function() { |
|
|
|
if (!reading) { |
|
|
|
if (finished) { |
|
|
|
reading = true; |
|
|
|
self.push(null); |
|
|
|
if (can_read) { |
|
|
|
} else { |
|
|
|
startReading(); |
|
|
|
if (!reading) { |
|
|
|
|
|
|
|
reading = true; |
|
|
|
|
|
|
|
self._call.startRead(readCallback); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
/** |
|
|
|
|
|
|
|
* Push the chunk onto the write queue, and write from the write queue if |
|
|
|
|
|
|
|
* there is not a pending write |
|
|
|
|
|
|
|
* @param {Buffer} chunk The chunk of data to write |
|
|
|
|
|
|
|
* @param {function(Error=)} callback The callback to call when the write |
|
|
|
|
|
|
|
* completes |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
this._tryWrite = function(chunk, callback) { |
|
|
|
|
|
|
|
write_queue.push({chunk: chunk, callback: callback}); |
|
|
|
|
|
|
|
if (can_write && !writing) { |
|
|
|
|
|
|
|
writeNext(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
@ -153,7 +105,7 @@ function GrpcClientStream(call, options) { |
|
|
|
* @param {number} size Ignored |
|
|
|
* @param {number} size Ignored |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
GrpcClientStream.prototype._read = function(size) { |
|
|
|
GrpcClientStream.prototype._read = function(size) { |
|
|
|
this._enableRead(); |
|
|
|
this.startReading(); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
@ -164,7 +116,10 @@ GrpcClientStream.prototype._read = function(size) { |
|
|
|
* @param {function(Error=)} callback Ignored |
|
|
|
* @param {function(Error=)} callback Ignored |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
GrpcClientStream.prototype._write = function(chunk, encoding, callback) { |
|
|
|
GrpcClientStream.prototype._write = function(chunk, encoding, callback) { |
|
|
|
this._tryWrite(chunk, callback); |
|
|
|
var self = this; |
|
|
|
|
|
|
|
self._call.startWrite(chunk, function(event) { |
|
|
|
|
|
|
|
callback(); |
|
|
|
|
|
|
|
}, 0); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|