Support streamed response from SocketNodeInstance

This commit is contained in:
SteveSandersonMS
2016-06-07 17:16:01 +01:00
parent 967edd2b2a
commit b19d0dff92
3 changed files with 64 additions and 9 deletions

View File

@@ -78,14 +78,29 @@
var invocation = JSON.parse(line);
var invokedModule = dynamicRequire(path.resolve(process.cwd(), invocation.moduleName));
var invokedFunction = invocation.exportedFunctionName ? invokedModule[invocation.exportedFunctionName] : invokedModule;
// Actually invoke it, passing the callback followed by any supplied args
// Prepare a callback for accepting non-streamed JSON responses
var hasInvokedCallback_1 = false;
var invocationCallback = function (errorValue, successValue) {
if (hasInvokedCallback_1) {
throw new Error('Cannot supply more than one result. The callback has already been invoked,'
+ ' or the result stream has already been accessed');
}
hasInvokedCallback_1 = true;
connection.end(JSON.stringify({
result: successValue,
errorMessage: errorValue && (errorValue.message || errorValue),
errorDetails: errorValue && (errorValue.stack || null)
}));
};
// Also support streamed binary responses
Object.defineProperty(invocationCallback, 'stream', {
enumerable: true,
get: function () {
hasInvokedCallback_1 = true;
return connection;
}
});
// Actually invoke it, passing through any supplied args
invokedFunction.apply(null, [invocationCallback].concat(invocation.args));
}
catch (ex) {

View File

@@ -37,19 +37,41 @@ namespace Microsoft.AspNetCore.NodeServices
await EnsureReady();
var virtualConnectionClient = await GetOrCreateVirtualConnectionClientAsync();
using (var virtualConnection = _currentVirtualConnectionClient.OpenVirtualConnection())
bool shouldDisposeVirtualConnection = true;
Stream virtualConnection = null;
try
{
virtualConnection = _currentVirtualConnectionClient.OpenVirtualConnection();
// Send request
await WriteJsonLineAsync(virtualConnection, invocationInfo);
// Receive response
var response = await ReadJsonAsync<RpcResponse<T>>(virtualConnection);
if (response.ErrorMessage != null)
// Determine what kind of response format is expected
if (typeof(T) == typeof(Stream))
{
throw new NodeInvocationException(response.ErrorMessage, response.ErrorDetails);
// Pass through streamed binary response
// It is up to the consumer to dispose this stream, so don't do so here
shouldDisposeVirtualConnection = false;
return (T)(object)virtualConnection;
}
else
{
// Parse and return non-streamed JSON response
var response = await ReadJsonAsync<RpcJsonResponse<T>>(virtualConnection);
if (response.ErrorMessage != null)
{
throw new NodeInvocationException(response.ErrorMessage, response.ErrorDetails);
}
return response.Result;
return response.Result;
}
}
finally
{
if (shouldDisposeVirtualConnection)
{
virtualConnection.Dispose();
}
}
}
@@ -180,7 +202,7 @@ namespace Microsoft.AspNetCore.NodeServices
}
#pragma warning disable 649 // These properties are populated via JSON deserialization
private class RpcResponse<TResult>
private class RpcJsonResponse<TResult>
{
public TResult Result { get; set; }
public string ErrorMessage { get; set; }

View File

@@ -29,14 +29,32 @@ virtualConnectionServer.createInterface(server).on('connection', (connection: Du
const invokedModule = dynamicRequire(path.resolve(process.cwd(), invocation.moduleName));
const invokedFunction = invocation.exportedFunctionName ? invokedModule[invocation.exportedFunctionName] : invokedModule;
// Actually invoke it, passing the callback followed by any supplied args
// Prepare a callback for accepting non-streamed JSON responses
let hasInvokedCallback = false;
const invocationCallback = (errorValue, successValue) => {
if (hasInvokedCallback) {
throw new Error('Cannot supply more than one result. The callback has already been invoked,'
+ ' or the result stream has already been accessed');
}
hasInvokedCallback = true;
connection.end(JSON.stringify({
result: successValue,
errorMessage: errorValue && (errorValue.message || errorValue),
errorDetails: errorValue && (errorValue.stack || null)
}));
};
// Also support streamed binary responses
Object.defineProperty(invocationCallback, 'stream', {
enumerable: true,
get: (): Duplex => {
hasInvokedCallback = true;
return connection;
}
});
// Actually invoke it, passing through any supplied args
invokedFunction.apply(null, [invocationCallback].concat(invocation.args));
} catch (ex) {
connection.end(JSON.stringify({