Skip to content

Commit 967edd2

Browse files
Support streamed response from HttpNodeInstance
1 parent 3440aa4 commit 967edd2

File tree

4 files changed

+53
-21
lines changed

4 files changed

+53
-21
lines changed

samples/misc/NodeServicesExamples/Controllers/ResizeImage.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.IO;
23
using System.Threading.Tasks;
34
using Microsoft.AspNetCore.Hosting;
45
using Microsoft.AspNetCore.Mvc;
@@ -36,10 +37,10 @@ public async Task<IActionResult> Index(string imagePath, int maxWidth, int maxHe
3637
return NotFound();
3738
}
3839

39-
// Invoke Node and convert the base64 result back to bytes
40+
// Invoke Node and pipe the result to the response
4041
var mimeType = GetContentType(imagePath);
41-
var resizedImage = await _nodeServices.Invoke<ResizeImageResult>("./Node/resizeImage", fileInfo.PhysicalPath, mimeType, maxWidth, maxHeight);
42-
return File(Convert.FromBase64String(resizedImage.Base64), mimeType);
42+
var imageStream = await _nodeServices.Invoke<Stream>("./Node/resizeImage", fileInfo.PhysicalPath, mimeType, maxWidth, maxHeight);
43+
return File(imageStream, mimeType);
4344
}
4445

4546
private string GetContentType(string path)
Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
var sharp = require('sharp');
22

3-
module.exports = function(cb, physicalPath, mimeType, maxWidth, maxHeight) {
3+
module.exports = function(result, physicalPath, mimeType, maxWidth, maxHeight) {
44
sharp(physicalPath)
55
.resize(maxWidth > 0 ? maxWidth : null, maxHeight > 0 ? maxHeight : null)
6-
.toBuffer(function (err, buffer) {
7-
cb(err, { base64: buffer && buffer.toString('base64') });
8-
});
6+
.pipe(result.stream);
97
}

src/Microsoft.AspNetCore.NodeServices/Content/Node/entrypoint-http.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,19 @@ var server = http.createServer(function(req, res) {
4242
}
4343
};
4444

45+
// Support streamed responses
46+
Object.defineProperty(callback, 'stream', {
47+
enumerable: true,
48+
get: function() {
49+
if (!hasSentResult) {
50+
hasSentResult = true;
51+
res.setHeader('Content-Type', 'application/octet-stream');
52+
}
53+
54+
return res;
55+
}
56+
});
57+
4558
try {
4659
func.apply(null, [callback].concat(bodyJson.args));
4760
} catch (synchronousException) {

src/Microsoft.AspNetCore.NodeServices/HostingModels/HttpNodeInstance.cs

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.IO;
23
using System.Net.Http;
34
using System.Text;
45
using System.Text.RegularExpressions;
@@ -52,27 +53,46 @@ public override async Task<T> Invoke<T>(NodeInvocationInfo invocationInfo)
5253
var payloadJson = JsonConvert.SerializeObject(invocationInfo, JsonSerializerSettings);
5354
var payload = new StringContent(payloadJson, Encoding.UTF8, "application/json");
5455
var response = await _client.PostAsync("http://localhost:" + _portNumber, payload);
55-
var responseString = await response.Content.ReadAsStringAsync();
5656

5757
if (!response.IsSuccessStatusCode)
5858
{
59-
throw new Exception("Call to Node module failed with error: " + responseString);
59+
var responseErrorString = await response.Content.ReadAsStringAsync();
60+
throw new Exception("Call to Node module failed with error: " + responseErrorString);
6061
}
6162

62-
var responseIsJson = response.Content.Headers.ContentType.MediaType == "application/json";
63-
if (responseIsJson)
63+
var responseContentType = response.Content.Headers.ContentType;
64+
switch (responseContentType.MediaType)
6465
{
65-
return JsonConvert.DeserializeObject<T>(responseString);
66+
case "text/plain":
67+
// String responses can skip JSON encoding/decoding
68+
if (typeof(T) != typeof(string))
69+
{
70+
throw new ArgumentException(
71+
"Node module responded with non-JSON string. This cannot be converted to the requested generic type: " +
72+
typeof(T).FullName);
73+
}
74+
75+
var responseString = await response.Content.ReadAsStringAsync();
76+
return (T)(object)responseString;
77+
78+
case "application/json":
79+
var responseJson = await response.Content.ReadAsStringAsync();
80+
return JsonConvert.DeserializeObject<T>(responseJson);
81+
82+
case "application/octet-stream":
83+
// Streamed responses have to be received as System.IO.Stream instances
84+
if (typeof(T) != typeof(Stream))
85+
{
86+
throw new ArgumentException(
87+
"Node module responded with binary stream. This cannot be converted to the requested generic type: " +
88+
typeof(T).FullName + ". Instead you must use the generic type System.IO.Stream.");
89+
}
90+
91+
return (T)(object)(await response.Content.ReadAsStreamAsync());
92+
93+
default:
94+
throw new InvalidOperationException("Unexpected response content type: " + responseContentType.MediaType);
6695
}
67-
68-
if (typeof(T) != typeof(string))
69-
{
70-
throw new ArgumentException(
71-
"Node module responded with non-JSON string. This cannot be converted to the requested generic type: " +
72-
typeof(T).FullName);
73-
}
74-
75-
return (T)(object)responseString;
7696
}
7797

7898
protected override void OnOutputDataReceived(string outputData)

0 commit comments

Comments
 (0)