Skip to content

Commit 6161538

Browse files
authored
Merge pull request #61 from JeringTech/fix-connection-drops
Fixed connection drops.
2 parents 2f7a7b5 + ce10791 commit 6161538

File tree

3 files changed

+62
-5
lines changed

3 files changed

+62
-5
lines changed

Changelog.md

+6-2
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@ This project uses [semantic versioning](http://semver.org/spec/v2.0.0.html). Ref
33
*[Semantic Versioning in Practice](https://www.jering.tech/articles/semantic-versioning-in-practice)*
44
for an overview of semantic versioning.
55

6-
## [Unreleased](https://github.com/JeringTech/Javascript.NodeJS/compare/5.2.0...HEAD)
6+
## [Unreleased](https://github.com/JeringTech/Javascript.NodeJS/compare/5.2.1...HEAD)
77

8-
## [5.2.0](https://github.com/JeringTech/Javascript.NodeJS/compare/5.1.1...5.2.0) - Dec 4, 2019
8+
## [5.2.1](https://github.com/JeringTech/Javascript.NodeJS/compare/5.2.0...5.2.1) - Dec 6, 2019
99
### Fixes
10+
- Improved HTTP connection stability and error logging. ([#61](https://github.com/JeringTech/Javascript.NodeJS/pull/61)).
11+
12+
## [5.2.0](https://github.com/JeringTech/Javascript.NodeJS/compare/5.1.1...5.2.0) - Dec 4, 2019
13+
### Changes
1014
- Expanded API. ([#57](https://github.com/JeringTech/Javascript.NodeJS/pull/57)). Added `INodeJSService` members for invocations without return values and
1115
atomic/simplified caching-invoking:
1216
- `Task InvokeFromFileAsync(string modulePath, string exportName = null, object[] args = null, CancellationToken cancellationToken = default);`

src/NodeJS/Javascript/Servers/OutOfProcess/Http/HttpServer.ts

+27-3
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,19 @@ patchLStat();
2323
// Set by NodeJSProcessFactory
2424
let projectDir = process.cwd();
2525

26+
// Create server
27+
const server = http.createServer(serverOnRequestListener);
28+
29+
// In Node.js v13+ this is the default, however for earlier versions it is 120 seconds.
30+
server.setTimeout(0);
31+
32+
// Send client error details to client for debugging
33+
server.on('clientError', serverOnClientError);
34+
2635
// Start server
27-
const server = http.createServer((req, res) => {
36+
server.listen(parseInt(args.port), 'localhost', serverOnListeningListener);
37+
38+
function serverOnRequestListener(req, res) {
2839
let bodyChunks = [];
2940
req.
3041
on('data', chunk => bodyChunks.push(chunk)).
@@ -158,12 +169,25 @@ const server = http.createServer((req, res) => {
158169
respondWithError(res, error);
159170
}
160171
});
161-
}).listen(parseInt(args.port), 'localhost', function () {
172+
}
173+
174+
// Send error details to client for debugging - https://nodejs.org/api/http.html#http_event_clienterror
175+
function serverOnClientError(error: Error, socket: stream.Duplex) {
176+
let errorJson = JSON.stringify({
177+
errorMessage: error.message,
178+
errorStack: error.stack
179+
});
180+
181+
let httpResponseMessage = `HTTP/1.1 500 Internal Server Error\r\nContent-Length: ${Buffer.byteLength(errorJson, 'utf8')}\r\nContent-Type: text/html\r\n\r\n${errorJson}`;
182+
socket.end(httpResponseMessage);
183+
}
184+
185+
function serverOnListeningListener() {
162186
// Signal to HttpNodeHost which loopback IP address (IPv4 or IPv6) and port it should make its HTTP connections on
163187
// and that we are ready to process invocations.
164188
let info = server.address() as AddressInfo;
165189
console.log(`[Jering.Javascript.NodeJS: Listening on IP - ${info.address} Port - ${info.port}]`);
166-
});
190+
}
167191

168192
function getTempIdentifier(invocationRequest: InvocationRequest): string {
169193
if (invocationRequest.newCacheIdentifier == null) {

test/NodeJS/HttpNodeJSServiceIntegrationTests.cs

+29
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
using System.Diagnostics;
77
using System.IO;
88
using System.Linq;
9+
using System.Net;
10+
using System.Net.Http;
11+
using System.Net.Sockets;
912
using System.Text;
13+
using System.Text.Json;
1014
using System.Threading;
1115
using Xunit;
1216
using Xunit.Abstractions;
@@ -954,6 +958,31 @@ public static IEnumerable<object[]> AllInvokeMethods_ReceiveAndLogStderrOutput_D
954958
};
955959
}
956960

961+
[Fact]
962+
public async void AllInvokeMethods_HandleHttpClientErrorsSuchAsMalformedRequests()
963+
{
964+
// Arrange
965+
HttpNodeJSService testSubject = CreateHttpNodeJSService();
966+
await testSubject.InvokeFromStringAsync("module.exports = callback => callback();").ConfigureAwait(false); // Starts the Node.js process
967+
Uri dummyEndpoint = testSubject.Endpoint;
968+
var dummyJsonService = new JsonService();
969+
970+
// Act
971+
using (var dummyHttpClient = new HttpClient())
972+
// Send a request with an invalid HTTP method. NodeJS drops the connection halfway through and fires the clientError event - https://nodejs.org/api/http.html#http_event_clienterror
973+
using (var dummyHttpRequestMessage = new HttpRequestMessage(new HttpMethod("INVALID"), dummyEndpoint))
974+
using (HttpResponseMessage dummyHttpResponseMessage = await dummyHttpClient.SendAsync(dummyHttpRequestMessage).ConfigureAwait(false))
975+
using (Stream dummyStream = await dummyHttpResponseMessage.Content.ReadAsStreamAsync().ConfigureAwait(false))
976+
{
977+
InvocationError result = await dummyJsonService.DeserializeAsync<InvocationError>(dummyStream).ConfigureAwait(false);
978+
979+
// Assert
980+
Assert.Equal(HttpStatusCode.InternalServerError, dummyHttpResponseMessage.StatusCode);
981+
Assert.False(string.IsNullOrWhiteSpace(result.ErrorMessage));
982+
Assert.False(string.IsNullOrWhiteSpace(result.ErrorStack));
983+
}
984+
}
985+
957986
/// <summary>
958987
/// Specify <paramref name="loggerStringBuilder"/> for access to all logging output.
959988
/// </summary>

0 commit comments

Comments
 (0)