diff --git a/src/Microsoft.AspNetCore.NodeServices/HostingModels/HttpNodeInstance.cs b/src/Microsoft.AspNetCore.NodeServices/HostingModels/HttpNodeInstance.cs
index b1b0fe9..8dd896b 100644
--- a/src/Microsoft.AspNetCore.NodeServices/HostingModels/HttpNodeInstance.cs
+++ b/src/Microsoft.AspNetCore.NodeServices/HostingModels/HttpNodeInstance.cs
@@ -21,6 +21,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
///
internal class HttpNodeInstance : OutOfProcessNodeInstance
{
+ private readonly static int streamBufferSize = 16 * 1024;
private static readonly Regex PortMessageRegex =
new Regex(@"^\[Microsoft.AspNetCore.NodeServices.HttpNodeHost:Listening on port (\d+)\]$");
@@ -67,8 +68,10 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
if (!response.IsSuccessStatusCode)
{
// Unfortunately there's no true way to cancel ReadAsStringAsync calls, hence AbandonIfCancelled
- var responseErrorString = await response.Content.ReadAsStringAsync().OrThrowOnCancellation(cancellationToken);
- throw new Exception("Call to Node module failed with error: " + responseErrorString);
+ var responseJson = await response.Content.ReadAsStringAsync().OrThrowOnCancellation(cancellationToken);
+ var responseError = JsonConvert.DeserializeObject(responseJson, jsonSerializerSettings);
+
+ throw new NodeInvocationException(responseError.ErrorMessage, responseError.ErrorDetails);
}
var responseContentType = response.Content.Headers.ContentType;
@@ -136,5 +139,35 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
_disposed = true;
}
}
+
+ private static async Task ReadJsonAsync(Stream stream, CancellationToken cancellationToken)
+ {
+ var json = Encoding.UTF8.GetString(await ReadAllBytesAsync(stream, cancellationToken));
+ return JsonConvert.DeserializeObject(json, jsonSerializerSettings);
+ }
+
+ private static async Task ReadAllBytesAsync(Stream input, CancellationToken cancellationToken)
+ {
+ byte[] buffer = new byte[streamBufferSize];
+
+ using (var ms = new MemoryStream())
+ {
+ int read;
+ while ((read = await input.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0)
+ {
+ ms.Write(buffer, 0, read);
+ }
+
+ return ms.ToArray();
+ }
+ }
+
+#pragma warning disable 649 // These properties are populated via JSON deserialization
+ private class RpcJsonResponse
+ {
+ public string ErrorMessage { get; set; }
+ public string ErrorDetails { get; set; }
+ }
+#pragma warning restore 649
}
}
diff --git a/src/Microsoft.AspNetCore.NodeServices/TypeScript/HttpNodeInstanceEntryPoint.ts b/src/Microsoft.AspNetCore.NodeServices/TypeScript/HttpNodeInstanceEntryPoint.ts
index 9b51bf1..31f6376 100644
--- a/src/Microsoft.AspNetCore.NodeServices/TypeScript/HttpNodeInstanceEntryPoint.ts
+++ b/src/Microsoft.AspNetCore.NodeServices/TypeScript/HttpNodeInstanceEntryPoint.ts
@@ -86,5 +86,8 @@ function readRequestBodyAsJson(request, callback) {
function respondWithError(res: http.ServerResponse, errorValue: any) {
res.statusCode = 500;
- res.end(errorValue.stack || errorValue.toString());
+ res.end(JSON.stringify({
+ errorMessage: errorValue.message || errorValue,
+ errorDetails: errorValue.stack || null
+ }));
}