Skip to content

Commit b96c2f9

Browse files
authored
Merge pull request #101 from JeringTech/improve-robustness
Improve robustness
2 parents 3d17497 + 7e31ac8 commit b96c2f9

26 files changed

+469
-298
lines changed

Changelog.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,15 @@ 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/6.0.0-beta.1...HEAD)
6+
## [Unreleased](https://github.com/JeringTech/Javascript.NodeJS/compare/6.0.0-beta.2...HEAD)
7+
8+
## [6.0.0-beta.2](https://github.com/JeringTech/Javascript.NodeJS/compare/6.0.0-beta.1...6.0.0-beta.2) - Feb 24, 2021
9+
### Additions
10+
- Added `OutOfProcessNodeJSServiceOptions.NumProcessRetries` option. ([#101](https://github.com/JeringTech/Javascript.NodeJS/pull/101).
11+
### Changes
12+
- `HttpNodeJSService` now logs endpoint on connect. Logged at `information` level. ([#101](https://github.com/JeringTech/Javascript.NodeJS/pull/101)).
13+
- **Breaking**: Renamed `OutOfProcessNodeJSServiceOptions.WatchGracefulShutdown` to `GracefulProcessShutdown`. Option now affects
14+
process shutdowns on-file-change *and* when retrying invocations. ([#101](https://github.com/JeringTech/Javascript.NodeJS/pull/101)).
715

816
## [6.0.0-beta.1](https://github.com/JeringTech/Javascript.NodeJS/compare/6.0.0-beta.0...6.0.0-beta.1) - Feb 22, 2021
917
### Fixes

perf/NodeJS/ConcurrencyBenchmarks.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class ConcurrencyBenchmarks
1313
{
1414
private const string DUMMY_WARMUP_MODULE = "module.exports = (callback) => callback()";
1515
private const string DUMMY_CONCURRENCY_MODULE_FILE = "dummyConcurrencyModule.js";
16-
private static readonly string PROJECT_PATH = Path.Combine(Directory.GetCurrentDirectory(), "../../../../../../../Javascript"); // BenchmarkDotNet creates a project nested deep in bin
16+
private static readonly string _projectPath = Path.Combine(Directory.GetCurrentDirectory(), "../../../../../../../Javascript"); // BenchmarkDotNet creates a project nested deep in bin
1717

1818
private ServiceProvider _serviceProvider;
1919
private INodeJSService _nodeJSService;
@@ -25,7 +25,7 @@ public void INodeJSService_Concurrency_MultiProcess_Setup()
2525
{
2626
var services = new ServiceCollection();
2727
services.AddNodeJS();
28-
services.Configure<NodeJSProcessOptions>(options => options.ProjectPath = PROJECT_PATH);
28+
services.Configure<NodeJSProcessOptions>(options => options.ProjectPath = _projectPath);
2929
services.Configure<OutOfProcessNodeJSServiceOptions>(options => options.Concurrency = Concurrency.MultiProcess);
3030
_serviceProvider = services.BuildServiceProvider();
3131
_nodeJSService = _serviceProvider.GetRequiredService<INodeJSService>();
@@ -57,7 +57,7 @@ public void INodeJSService_Concurrency_None_Setup()
5757
{
5858
var services = new ServiceCollection();
5959
services.AddNodeJS();
60-
services.Configure<NodeJSProcessOptions>(options => options.ProjectPath = PROJECT_PATH);
60+
services.Configure<NodeJSProcessOptions>(options => options.ProjectPath = _projectPath);
6161
_serviceProvider = services.BuildServiceProvider();
6262
_nodeJSService = _serviceProvider.GetRequiredService<INodeJSService>();
6363

@@ -86,7 +86,7 @@ public void INodeServices_Concurrency_Setup()
8686
var services = new ServiceCollection();
8787
services.AddNodeServices(options =>
8888
{
89-
options.ProjectPath = PROJECT_PATH;
89+
options.ProjectPath = _projectPath;
9090
options.WatchFileExtensions = null;
9191
});
9292
_serviceProvider = services.BuildServiceProvider();

perf/NodeJS/FileWatchingBenchmarks.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public void HttpNodeJSService_FileWatching_GracefulShutdownDisabled_MoveToNewPro
4040
services.Configure<OutOfProcessNodeJSServiceOptions>(options =>
4141
{
4242
options.EnableFileWatching = true;
43-
options.WatchGracefulShutdown = false;
43+
options.GracefulProcessShutdown = false;
4444
});
4545
_serviceProvider = services.BuildServiceProvider();
4646
_httpNodeJSService = _serviceProvider.GetRequiredService<INodeJSService>() as HttpNodeJSService;

perf/NodeJS/LatencyBenchmarks.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class LatencyBenchmarks
1414
private const string DUMMY_WARMUP_MODULE = "module.exports = (callback) => callback()";
1515
private const string DUMMY_LATENCY_MODULE_FILE = "dummyLatencyModule.js";
1616
private const string DUMMY_MODULE_IDENTIFIER = "dummyLatencyModuleIdentifier";
17-
private static readonly string PROJECT_PATH = Path.Combine(Directory.GetCurrentDirectory(), "../../../../../../../Javascript"); // BenchmarkDotNet creates a project nested deep in bin
17+
private static readonly string _projectPath = Path.Combine(Directory.GetCurrentDirectory(), "../../../../../../../Javascript"); // BenchmarkDotNet creates a project nested deep in bin
1818

1919
private ServiceProvider _serviceProvider;
2020
private int _counter;
@@ -27,7 +27,7 @@ public void INodeJSService_Latency_InvokeFromFile_Setup()
2727
{
2828
var services = new ServiceCollection();
2929
services.AddNodeJS();
30-
services.Configure<NodeJSProcessOptions>(options => options.ProjectPath = PROJECT_PATH);
30+
services.Configure<NodeJSProcessOptions>(options => options.ProjectPath = _projectPath);
3131
_serviceProvider = services.BuildServiceProvider();
3232
_nodeJSService = _serviceProvider.GetRequiredService<INodeJSService>();
3333
_counter = 0;
@@ -50,7 +50,7 @@ public void INodeJSService_Latency_InvokeFromFile_GracefulShutdownEnabled_Setup(
5050
{
5151
var services = new ServiceCollection();
5252
services.AddNodeJS();
53-
services.Configure<NodeJSProcessOptions>(options => options.ProjectPath = PROJECT_PATH);
53+
services.Configure<NodeJSProcessOptions>(options => options.ProjectPath = _projectPath);
5454
services.Configure<OutOfProcessNodeJSServiceOptions>(options => options.EnableFileWatching = true);
5555
_serviceProvider = services.BuildServiceProvider();
5656
_nodeJSService = _serviceProvider.GetRequiredService<INodeJSService>();
@@ -87,7 +87,7 @@ public async Task<DummyResult> INodeJSService_Latency_InvokeFromCache()
8787

8888
private string DummyModuleFactory()
8989
{
90-
return File.ReadAllText(Path.Combine(PROJECT_PATH, DUMMY_LATENCY_MODULE_FILE));
90+
return File.ReadAllText(Path.Combine(_projectPath, DUMMY_LATENCY_MODULE_FILE));
9191
}
9292

9393
[Obsolete]
@@ -97,7 +97,7 @@ public void INodeServices_Latency_Setup()
9797
var services = new ServiceCollection();
9898
services.AddNodeServices(options =>
9999
{
100-
options.ProjectPath = PROJECT_PATH;
100+
options.ProjectPath = _projectPath;
101101
options.WatchFileExtensions = null;
102102
});
103103
_serviceProvider = services.BuildServiceProvider();

perf/NodeJS/RealWorkloadBenchmarks.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public static void Main(string[] args)
2222
Console.WriteLine(""Hello world {0}!"");
2323
}}
2424
}}";
25-
private static readonly string PROJECT_PATH = Path.Combine(Directory.GetCurrentDirectory(), "../../../../../../../Javascript"); // BenchmarkDotNet creates a project nested deep in bin
25+
private static readonly string _projectPath = Path.Combine(Directory.GetCurrentDirectory(), "../../../../../../../Javascript"); // BenchmarkDotNet creates a project nested deep in bin
2626

2727
private int _counter;
2828
private ServiceProvider _serviceProvider;
@@ -35,7 +35,7 @@ public void INodeJSService_RealWorkload_Setup()
3535
{
3636
var services = new ServiceCollection();
3737
services.AddNodeJS();
38-
services.Configure<NodeJSProcessOptions>(options => options.ProjectPath = PROJECT_PATH); // Module loads prismjs from node_modules
38+
services.Configure<NodeJSProcessOptions>(options => options.ProjectPath = _projectPath); // Module loads prismjs from node_modules
3939
services.Configure<OutOfProcessNodeJSServiceOptions>(options => options.Concurrency = Concurrency.MultiProcess);
4040
_serviceProvider = services.BuildServiceProvider();
4141
_nodeJSService = _serviceProvider.GetRequiredService<INodeJSService>();
@@ -66,7 +66,7 @@ public async Task<string[]> INodeJSService_RealWorkload()
6666

6767
private string DummyModuleFactory()
6868
{
69-
return File.ReadAllText(Path.Combine(PROJECT_PATH, DUMMY_REAL_WORKLOAD_MODULE_FILE));
69+
return File.ReadAllText(Path.Combine(_projectPath, DUMMY_REAL_WORKLOAD_MODULE_FILE));
7070
}
7171

7272
[Obsolete]
@@ -76,7 +76,7 @@ public void INodeServices_RealWorkload_Setup()
7676
var services = new ServiceCollection();
7777
services.AddNodeServices(options =>
7878
{
79-
options.ProjectPath = PROJECT_PATH;
79+
options.ProjectPath = _projectPath;
8080
options.WatchFileExtensions = null;
8181
});
8282
_serviceProvider = services.BuildServiceProvider();

src/NodeJS/Jering.Javascript.NodeJS.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<PackageId>Jering.Javascript.NodeJS</PackageId>
77
<Authors>JeremyTCD</Authors>
88
<Title>Invoke Javascript in NodeJS, from C#</Title>
9-
<Description>Jering.Javascript.NodeJS enables you to invoke javascript in NodeJS, from C#. With this ability, you can use javascript libraries and scripts from your C# projects.</Description>
9+
<Description>Jering.Javascript.NodeJS enables you to invoke javascript in NodeJS, from C#. With this ability, you can use javascript libraries and scripts from C# projects.</Description>
1010
<Copyright>© 2018-2021 Jering. All rights reserved.</Copyright>
1111
<PackageProjectUrl>https://www.jering.tech/utilities/jering.javascript.nodejs/index</PackageProjectUrl>
1212
<RepositoryUrl>https://github.com/JeringTech/Javascript.NodeJS</RepositoryUrl>

src/NodeJS/NodeJSServiceCollectionExtensions.cs

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using Microsoft.Extensions.Options;
33
using System;
44
using System.Collections.ObjectModel;
5-
using System.Net.Http;
65
using System.Threading;
76

87
namespace Jering.Javascript.NodeJS
@@ -22,10 +21,9 @@ public static IServiceCollection AddNodeJS(this IServiceCollection services)
2221
services.
2322
AddLogging().
2423
AddOptions();
25-
services.AddHttpClient();
2624

2725
// Services defined in this project
28-
return services.
26+
services.
2927
AddSingleton<IConfigureOptions<NodeJSProcessOptions>, ConfigureNodeJSProcessOptions>().
3028
AddSingleton<IHttpContentFactory, InvocationContentFactory>().
3129
AddSingleton<IEmbeddedResourcesService, EmbeddedResourcesService>().
@@ -35,26 +33,16 @@ public static IServiceCollection AddNodeJS(this IServiceCollection services)
3533
AddSingleton<IEnvironmentService, EnvironmentService>().
3634
AddSingleton<IFileWatcherFactory, FileWatcherFactory>().
3735
AddSingleton<IMonitorService, MonitorService>().
38-
AddSingleton<ITaskService, TaskService>().
39-
AddSingleton(IHttpClientServiceFactory);
40-
}
41-
42-
internal static IHttpClientService IHttpClientServiceFactory(IServiceProvider serviceProvider)
43-
{
36+
AddSingleton<ITaskService, TaskService>();
4437
#if NETCOREAPP3_1
4538
// If not called, framework forces HTTP/1.1 so long as origin isn't https
4639
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
4740
#endif
41+
services.
42+
AddHttpClient<IHttpClientService, HttpClientService>().
43+
SetHandlerLifetime(Timeout.InfiniteTimeSpan); // No DNS changes, handler lifetime can be infinite
4844

49-
// Create client
50-
OutOfProcessNodeJSServiceOptions outOfProcessNodeJSServiceOptions = serviceProvider.GetRequiredService<IOptions<OutOfProcessNodeJSServiceOptions>>().Value;
51-
IHttpClientFactory httpClientFactory = serviceProvider.GetRequiredService<IHttpClientFactory>();
52-
HttpClient httpClient = httpClientFactory.CreateClient();
53-
54-
// Configure
55-
httpClient.Timeout = outOfProcessNodeJSServiceOptions.TimeoutMS == -1 ? Timeout.InfiniteTimeSpan : TimeSpan.FromMilliseconds(outOfProcessNodeJSServiceOptions.TimeoutMS + 1000);
56-
57-
return new HttpClientService(httpClient);
45+
return services;
5846
}
5947

6048
internal static INodeJSService INodeJSServiceFactory(IServiceProvider serviceProvider)

src/NodeJS/NodeJSServiceImplementations/OutOfProcess/Http/HttpClientService.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System;
1+
using Microsoft.Extensions.Options;
2+
using System;
23
using System.Net.Http;
34
using System.Threading;
45
using System.Threading.Tasks;
@@ -19,9 +20,16 @@ public class HttpClientService : IHttpClientService
1920
/// Creates a <see cref="HttpClientService"/>.
2021
/// </summary>
2122
/// <param name="httpClient">The <see cref="HttpClient"/> to send HTTP requests with.</param>
22-
public HttpClientService(HttpClient httpClient)
23+
/// <param name="outOfProcessNodeJSServiceOptionsAccessor"></param>
24+
public HttpClientService(HttpClient httpClient,
25+
IOptions<OutOfProcessNodeJSServiceOptions> outOfProcessNodeJSServiceOptionsAccessor)
2326
{
2427
_httpClient = httpClient;
28+
29+
// Configure
30+
OutOfProcessNodeJSServiceOptions outOfProcessNodeJSServiceOptions = outOfProcessNodeJSServiceOptionsAccessor.Value;
31+
httpClient.Timeout = outOfProcessNodeJSServiceOptions.TimeoutMS == -1 ? System.Threading.Timeout.InfiniteTimeSpan :
32+
TimeSpan.FromMilliseconds(outOfProcessNodeJSServiceOptions.TimeoutMS + 1000);
2533
}
2634

2735
/// <inheritdoc />

src/NodeJS/NodeJSServiceImplementations/OutOfProcess/Http/HttpNodeJSService.cs

Lines changed: 6 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public class HttpNodeJSService : OutOfProcessNodeJSService
2222

2323
private readonly IHttpContentFactory _httpContentFactory;
2424
private readonly IJsonService _jsonService;
25+
private readonly ILogger<HttpNodeJSService> _logger;
2526
private readonly IHttpClientService _httpClientService;
2627

2728
private bool _disposed;
@@ -41,7 +42,7 @@ public class HttpNodeJSService : OutOfProcessNodeJSService
4142
/// <param name="httpClientService"></param>
4243
/// <param name="jsonService"></param>
4344
/// <param name="nodeJSProcessFactory"></param>
44-
/// <param name="loggerFactory"></param>
45+
/// <param name="logger"></param>
4546
public HttpNodeJSService(IOptions<OutOfProcessNodeJSServiceOptions> outOfProcessNodeJSServiceOptionsAccessor,
4647
IHttpContentFactory httpContentFactory,
4748
IEmbeddedResourcesService embeddedResourcesService,
@@ -51,9 +52,9 @@ public HttpNodeJSService(IOptions<OutOfProcessNodeJSServiceOptions> outOfProcess
5152
IHttpClientService httpClientService,
5253
IJsonService jsonService,
5354
INodeJSProcessFactory nodeJSProcessFactory,
54-
ILoggerFactory loggerFactory) :
55+
ILogger<HttpNodeJSService> logger) :
5556
base(nodeJSProcessFactory,
56-
loggerFactory.CreateLogger(typeof(HttpNodeJSService)),
57+
logger,
5758
outOfProcessNodeJSServiceOptionsAccessor,
5859
embeddedResourcesService,
5960
fileWatcherFactory,
@@ -64,41 +65,10 @@ public HttpNodeJSService(IOptions<OutOfProcessNodeJSServiceOptions> outOfProcess
6465
{
6566
_httpClientService = httpClientService;
6667
_jsonService = jsonService;
68+
_logger = logger;
6769
_httpContentFactory = httpContentFactory;
6870
}
6971

70-
// DO NOT DELETE - keep for backward compatibility.
71-
/// <summary>
72-
/// <para>Creates a <see cref="HttpNodeJSService"/>.</para>
73-
/// <para>If this constructor is used, file watching is disabled.</para>
74-
/// </summary>
75-
/// <param name="outOfProcessNodeJSServiceOptionsAccessor"></param>
76-
/// <param name="httpContentFactory"></param>
77-
/// <param name="embeddedResourcesService"></param>
78-
/// <param name="httpClientService"></param>
79-
/// <param name="jsonService"></param>
80-
/// <param name="nodeJSProcessFactory"></param>
81-
/// <param name="loggerFactory"></param>
82-
public HttpNodeJSService(IOptions<OutOfProcessNodeJSServiceOptions> outOfProcessNodeJSServiceOptionsAccessor,
83-
IHttpContentFactory httpContentFactory,
84-
IEmbeddedResourcesService embeddedResourcesService,
85-
IHttpClientService httpClientService,
86-
IJsonService jsonService,
87-
INodeJSProcessFactory nodeJSProcessFactory,
88-
ILoggerFactory loggerFactory) :
89-
this(outOfProcessNodeJSServiceOptionsAccessor,
90-
httpContentFactory,
91-
embeddedResourcesService,
92-
null,
93-
null,
94-
null,
95-
httpClientService,
96-
jsonService,
97-
nodeJSProcessFactory,
98-
loggerFactory)
99-
{
100-
}
101-
10272
/// <inheritdoc />
10373
protected override async Task<(bool, T)> TryInvokeAsync<T>(InvocationRequest invocationRequest, CancellationToken cancellationToken)
10474
{
@@ -201,6 +171,7 @@ protected override void OnConnectionEstablishedMessageReceived(string connection
201171
else if (currentChar == ']')
202172
{
203173
_endpoint = new Uri(stringBuilder.ToString());
174+
_logger.LogInformation(string.Format(Strings.LogInformation_HttpEndpoint, _endpoint));
204175
return;
205176
}
206177
else

src/NodeJS/NodeJSServiceImplementations/OutOfProcess/Http/IHttpClientService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public interface IHttpClientService
1313
/// <summary>
1414
/// Gets or sets the timespan to wait before the request times out.
1515
/// </summary>
16-
TimeSpan Timeout {get; set;}
16+
TimeSpan Timeout { get; set; }
1717

1818
/// <summary>
1919
/// Send an HTTP request as an asynchronous operation.

src/NodeJS/NodeJSServiceImplementations/OutOfProcess/Http/InvocationContent.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ namespace Jering.Javascript.NodeJS
1616
public class InvocationContent : HttpContent
1717
{
1818
// Arbitrary boundary
19-
internal static readonly byte[] BOUNDARY_BYTES = Encoding.UTF8.GetBytes("--Uiw6+hXl3k+5ia0cUYGhjA==");
19+
internal static readonly byte[] _boundaryBytes = Encoding.UTF8.GetBytes("--Uiw6+hXl3k+5ia0cUYGhjA==");
2020

21-
private static readonly MediaTypeHeaderValue MULTIPART_CONTENT_TYPE = new MediaTypeHeaderValue("multipart/mixed");
21+
private static readonly MediaTypeHeaderValue _multipartContentType = new MediaTypeHeaderValue("multipart/mixed");
2222
private readonly IJsonService _jsonService;
2323
private readonly InvocationRequest _invocationRequest;
2424

@@ -34,7 +34,7 @@ public InvocationContent(IJsonService jsonService, InvocationRequest invocationR
3434

3535
if (invocationRequest.ModuleSourceType == ModuleSourceType.Stream)
3636
{
37-
Headers.ContentType = MULTIPART_CONTENT_TYPE;
37+
Headers.ContentType = _multipartContentType;
3838
}
3939
}
4040

@@ -50,7 +50,7 @@ protected override async Task SerializeToStreamAsync(Stream stream, TransportCon
5050

5151
if (_invocationRequest.ModuleSourceType == ModuleSourceType.Stream)
5252
{
53-
await stream.WriteAsync(BOUNDARY_BYTES, 0, BOUNDARY_BYTES.Length).ConfigureAwait(false);
53+
await stream.WriteAsync(_boundaryBytes, 0, _boundaryBytes.Length).ConfigureAwait(false);
5454
await _invocationRequest.ModuleStreamSource.CopyToAsync(stream).ConfigureAwait(false);
5555
}
5656
}

0 commit comments

Comments
 (0)