From 967edd2b2ab613e502693fe22b968c6552f6f5c0 Mon Sep 17 00:00:00 2001 From: SteveSandersonMS Date: Tue, 7 Jun 2016 16:49:40 +0100 Subject: [PATCH] Support streamed response from HttpNodeInstance --- .../Controllers/ResizeImage.cs | 7 +-- .../NodeServicesExamples/Node/resizeImage.js | 6 +-- .../Content/Node/entrypoint-http.js | 13 ++++++ .../HostingModels/HttpNodeInstance.cs | 46 +++++++++++++------ 4 files changed, 52 insertions(+), 20 deletions(-) diff --git a/samples/misc/NodeServicesExamples/Controllers/ResizeImage.cs b/samples/misc/NodeServicesExamples/Controllers/ResizeImage.cs index 1606a0d..c6b08b2 100644 --- a/samples/misc/NodeServicesExamples/Controllers/ResizeImage.cs +++ b/samples/misc/NodeServicesExamples/Controllers/ResizeImage.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; @@ -36,10 +37,10 @@ namespace NodeServicesExamples.Controllers return NotFound(); } - // Invoke Node and convert the base64 result back to bytes + // Invoke Node and pipe the result to the response var mimeType = GetContentType(imagePath); - var resizedImage = await _nodeServices.Invoke("./Node/resizeImage", fileInfo.PhysicalPath, mimeType, maxWidth, maxHeight); - return File(Convert.FromBase64String(resizedImage.Base64), mimeType); + var imageStream = await _nodeServices.Invoke("./Node/resizeImage", fileInfo.PhysicalPath, mimeType, maxWidth, maxHeight); + return File(imageStream, mimeType); } private string GetContentType(string path) diff --git a/samples/misc/NodeServicesExamples/Node/resizeImage.js b/samples/misc/NodeServicesExamples/Node/resizeImage.js index 9f3d9bf..77fdb1b 100644 --- a/samples/misc/NodeServicesExamples/Node/resizeImage.js +++ b/samples/misc/NodeServicesExamples/Node/resizeImage.js @@ -1,9 +1,7 @@ var sharp = require('sharp'); -module.exports = function(cb, physicalPath, mimeType, maxWidth, maxHeight) { +module.exports = function(result, physicalPath, mimeType, maxWidth, maxHeight) { sharp(physicalPath) .resize(maxWidth > 0 ? maxWidth : null, maxHeight > 0 ? maxHeight : null) - .toBuffer(function (err, buffer) { - cb(err, { base64: buffer && buffer.toString('base64') }); - }); + .pipe(result.stream); } diff --git a/src/Microsoft.AspNetCore.NodeServices/Content/Node/entrypoint-http.js b/src/Microsoft.AspNetCore.NodeServices/Content/Node/entrypoint-http.js index abd9540..35075e7 100644 --- a/src/Microsoft.AspNetCore.NodeServices/Content/Node/entrypoint-http.js +++ b/src/Microsoft.AspNetCore.NodeServices/Content/Node/entrypoint-http.js @@ -42,6 +42,19 @@ var server = http.createServer(function(req, res) { } }; + // Support streamed responses + Object.defineProperty(callback, 'stream', { + enumerable: true, + get: function() { + if (!hasSentResult) { + hasSentResult = true; + res.setHeader('Content-Type', 'application/octet-stream'); + } + + return res; + } + }); + try { func.apply(null, [callback].concat(bodyJson.args)); } catch (synchronousException) { diff --git a/src/Microsoft.AspNetCore.NodeServices/HostingModels/HttpNodeInstance.cs b/src/Microsoft.AspNetCore.NodeServices/HostingModels/HttpNodeInstance.cs index eb4f1f0..a2aeaf1 100644 --- a/src/Microsoft.AspNetCore.NodeServices/HostingModels/HttpNodeInstance.cs +++ b/src/Microsoft.AspNetCore.NodeServices/HostingModels/HttpNodeInstance.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Net.Http; using System.Text; using System.Text.RegularExpressions; @@ -52,27 +53,46 @@ namespace Microsoft.AspNetCore.NodeServices var payloadJson = JsonConvert.SerializeObject(invocationInfo, JsonSerializerSettings); var payload = new StringContent(payloadJson, Encoding.UTF8, "application/json"); var response = await _client.PostAsync("http://localhost:" + _portNumber, payload); - var responseString = await response.Content.ReadAsStringAsync(); if (!response.IsSuccessStatusCode) { - throw new Exception("Call to Node module failed with error: " + responseString); + var responseErrorString = await response.Content.ReadAsStringAsync(); + throw new Exception("Call to Node module failed with error: " + responseErrorString); } - var responseIsJson = response.Content.Headers.ContentType.MediaType == "application/json"; - if (responseIsJson) + var responseContentType = response.Content.Headers.ContentType; + switch (responseContentType.MediaType) { - return JsonConvert.DeserializeObject(responseString); - } + case "text/plain": + // String responses can skip JSON encoding/decoding + if (typeof(T) != typeof(string)) + { + throw new ArgumentException( + "Node module responded with non-JSON string. This cannot be converted to the requested generic type: " + + typeof(T).FullName); + } - if (typeof(T) != typeof(string)) - { - throw new ArgumentException( - "Node module responded with non-JSON string. This cannot be converted to the requested generic type: " + - typeof(T).FullName); - } + var responseString = await response.Content.ReadAsStringAsync(); + return (T)(object)responseString; - return (T)(object)responseString; + case "application/json": + var responseJson = await response.Content.ReadAsStringAsync(); + return JsonConvert.DeserializeObject(responseJson); + + case "application/octet-stream": + // Streamed responses have to be received as System.IO.Stream instances + if (typeof(T) != typeof(Stream)) + { + throw new ArgumentException( + "Node module responded with binary stream. This cannot be converted to the requested generic type: " + + typeof(T).FullName + ". Instead you must use the generic type System.IO.Stream."); + } + + return (T)(object)(await response.Content.ReadAsStreamAsync()); + + default: + throw new InvalidOperationException("Unexpected response content type: " + responseContentType.MediaType); + } } protected override void OnOutputDataReceived(string outputData)