| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,7 +3,13 @@ This project uses [semantic versioning](http://semver.org/spec/v2.0.0.html). Ref | |||
| 3 | 3 | *[Semantic Versioning in Practice](https://www.jering.tech/articles/semantic-versioning-in-practice)* | |
| 4 | 4 | for an overview of semantic versioning. | |
| 5 | 5 | ||
| 6 | - ## [Unreleased](https://github.com/JeringTech/Javascript.NodeJS/compare/6.3.1...HEAD) | ||
| 6 | + ## [Unreleased](https://github.com/JeringTech/Javascript.NodeJS/compare/7.0.0-beta.0...HEAD) | ||
| 7 | + | ||
| 8 | + ## [7.0.0-beta.0](https://github.com/JeringTech/Javascript.NodeJS/compare/6.3.1...7.0.0-beta.0) - Aug 25, 2022 | ||
| 9 | + ### Changes | ||
| 10 | + - **Breaking**: `OutOfProcessNodeJSService.OnConnectionEstablishedMessageReceived` now takes a `System.Text.RegularExpressions.Match` argument instead of a `string`. ([#146](https://github.com/JeringTech/Javascript.NodeJS/pull/146)) | ||
| 11 | + ### Fixes | ||
| 12 | + - Fixed handshake with Node.js not completing when external systems interfere with Node.js's stdout stream. ([#146](https://github.com/JeringTech/Javascript.NodeJS/pull/146)) | ||
| 7 | 13 | ||
| 8 | 14 | ## [6.3.1](https://github.com/JeringTech/Javascript.NodeJS/compare/6.3.0...6.3.1) - May 10, 2022 | |
| 9 | 15 | ### Fixes | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1769,6 +1769,7 @@ Contributions are welcome! | |||
| 1769 | 1769 | - [flcdrg](https://github.com/flcdrg) | |
| 1770 | 1770 | - [samcic](https://github.com/samcic) | |
| 1771 | 1771 | - [johnrom](https://github.com/johnrom) | |
| 1772 | + - [aKzenT](https://github.com/aKzenT) | ||
| 1772 | 1773 | ||
| 1773 | 1774 | ## About | |
| 1774 | 1775 | Follow [@JeringTech](https://twitter.com/JeringTech) for updates and more. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -14,6 +14,3 @@ jobs: | |||
| 14 | 14 | outOfProcessBuildDependencies: ["nodejs"] | |
| 15 | 15 | codecovKey: "e5de9f48-fb06-43c6-8368-44de5cf7e5d4" | |
| 16 | 16 | cacheYarnPackages: true | |
| 17 | - - template: templates/docs/main.yml@templates | ||
| 18 | - parameters: | ||
| 19 | - nugetRestorePats: "$(nugetRestorePats)" | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -48,7 +48,7 @@ | |||
| 48 | 48 | <PrivateAssets>all</PrivateAssets> | |
| 49 | 49 | <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> | |
| 50 | 50 | </PackageReference> | |
| 51 | - <PackageReference Include="Nullable" Version="1.3.0"> | ||
| 51 | + <PackageReference Include="Nullable" Version="1.3.1"> | ||
| 52 | 52 | <PrivateAssets>all</PrivateAssets> | |
| 53 | 53 | <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | |
| 54 | 54 | </PackageReference> | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,6 +6,7 @@ | |||
| 6 | 6 | using System.Net.Http; | |
| 7 | 7 | using System.Reflection; | |
| 8 | 8 | using System.Text; | |
| 9 | + using System.Text.RegularExpressions; | ||
| 9 | 10 | using System.Threading; | |
| 10 | 11 | using System.Threading.Tasks; | |
| 11 | 12 | ||
@@ -17,6 +18,11 @@ namespace Jering.Javascript.NodeJS | |||
| 17 | 18 | /// </summary> | |
| 18 | 19 | public class HttpNodeJSService : OutOfProcessNodeJSService | |
| 19 | 20 | { | |
| 21 | + /// <summary> | ||
| 22 | + /// Regex to match message used to perform a handshake with the NodeJS process. | ||
| 23 | + /// </summary> | ||
| 24 | + private static readonly Regex _sharedConnectionEstablishedMessageRegex = new(@"\[Jering\.Javascript\.NodeJS: HttpVersion - (?<protocol>HTTP\/\d\.\d) Listening on IP - (?<ip>[^ ]+) Port - (?<port>\d+)\]", RegexOptions.Compiled, TimeSpan.FromSeconds(1)); | ||
| 25 | + | ||
| 20 | 26 | internal const string HTTP11_SERVER_SCRIPT_NAME = "Http11Server.js"; | |
| 21 | 27 | internal const string HTTP20_SERVER_SCRIPT_NAME = "Http20Server.js"; | |
| 22 | 28 | ||
@@ -33,6 +39,9 @@ public class HttpNodeJSService : OutOfProcessNodeJSService | |||
| 33 | 39 | // want to use the most recent instance | |
| 34 | 40 | internal volatile Uri? _endpoint; | |
| 35 | 41 | ||
| 42 | + /// <inheritdoc /> | ||
| 43 | + protected override Regex ConnectionEstablishedMessageRegex => _sharedConnectionEstablishedMessageRegex; | ||
| 44 | + | ||
| 36 | 45 | /// <summary> | |
| 37 | 46 | /// Creates an <see cref="HttpNodeJSService"/>. | |
| 38 | 47 | /// </summary> | |
@@ -172,42 +181,18 @@ public HttpNodeJSService(IOptions<OutOfProcessNodeJSServiceOptions> outOfProcess | |||
| 172 | 181 | } | |
| 173 | 182 | ||
| 174 | 183 | /// <inheritdoc /> | |
| 175 | - protected override void OnConnectionEstablishedMessageReceived(string connectionEstablishedMessage) | ||
| 184 | + protected override void OnConnectionEstablishedMessageReceived(Match connectionMessageMatch) | ||
| 176 | 185 | { | |
| 177 | - // Start after message start and "HttpVersion - HTTP/X.X Listening on IP - " | ||
| 178 | - int startIndex = CONNECTION_ESTABLISHED_MESSAGE_START.Length + 41; | ||
| 179 | - var stringBuilder = new StringBuilder("http://"); | ||
| 180 | - | ||
| 181 | - for (int i = startIndex; i < connectionEstablishedMessage.Length; i++) | ||
| 182 | - { | ||
| 183 | - char currentChar = connectionEstablishedMessage[i]; | ||
| 184 | - | ||
| 185 | - if (currentChar == ':') | ||
| 186 | + _endpoint = new UriBuilder | ||
| 186 | 187 | { | |
| 187 | - // ::1 | ||
| 188 | - stringBuilder.Append("[::1]"); | ||
| 189 | - i += 2; | ||
| 190 | - } | ||
| 191 | - else if (currentChar == ' ') | ||
| 192 | - { | ||
| 193 | - stringBuilder.Append(':'); | ||
| 194 | - | ||
| 195 | - // Skip over "Port - " | ||
| 196 | - i += 7; | ||
| 197 | - } | ||
| 198 | - else if (currentChar == ']') | ||
| 199 | - { | ||
| 200 | - _endpoint = new Uri(stringBuilder.ToString()); | ||
| 201 | - _logger.LogInformation(string.Format(Strings.LogInformation_HttpEndpoint, | ||
| 202 | - connectionEstablishedMessage.Substring(41, 8), // Pluck out HTTP version | ||
| 203 | - _endpoint)); | ||
| 204 | - return; | ||
| 205 | - } | ||
| 206 | - else | ||
| 207 | - { | ||
| 208 | - stringBuilder.Append(currentChar); | ||
| 209 | - } | ||
| 210 | - } | ||
| 188 | + Scheme = "http", | ||
| 189 | + Host = connectionMessageMatch.Groups["ip"].Value, | ||
| 190 | + Port = int.Parse(connectionMessageMatch.Groups["port"].Value), | ||
| 191 | + }.Uri; | ||
| 192 | + | ||
| 193 | + _logger.LogInformation(string.Format(Strings.LogInformation_HttpEndpoint, | ||
| 194 | + connectionMessageMatch.Groups["protocol"].Value, // Pluck out HTTP version | ||
| 195 | + _endpoint)); | ||
| 211 | 196 | } | |
| 212 | 197 | ||
| 213 | 198 | /// <inheritdoc /> | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,6 +6,7 @@ | |||
| 6 | 6 | using System.IO; | |
| 7 | 7 | using System.Linq; | |
| 8 | 8 | using System.Reflection; | |
| 9 | + using System.Text.RegularExpressions; | ||
| 9 | 10 | using System.Threading; | |
| 10 | 11 | using System.Threading.Tasks; | |
| 11 | 12 | ||
@@ -21,11 +22,6 @@ namespace Jering.Javascript.NodeJS | |||
| 21 | 22 | /// <seealso cref="INodeJSService" /> | |
| 22 | 23 | public abstract class OutOfProcessNodeJSService : INodeJSService | |
| 23 | 24 | { | |
| 24 | - /// <summary> | ||
| 25 | - /// Start of the message used to perform a handshake with the NodeJS process. | ||
| 26 | - /// </summary> | ||
| 27 | - protected internal const string CONNECTION_ESTABLISHED_MESSAGE_START = "[Jering.Javascript.NodeJS: "; | ||
| 28 | - | ||
| 29 | 25 | /// <summary> | |
| 30 | 26 | /// The logger for the NodeJS process's stdout and stderr streams as well as messages from <see cref="OutOfProcessNodeJSService"/> and its implementations. | |
| 31 | 27 | /// </summary> | |
@@ -57,6 +53,12 @@ public abstract class OutOfProcessNodeJSService : INodeJSService | |||
| 57 | 53 | private volatile INodeJSProcess? _nodeJSProcess; // Volatile since it's used in a double checked lock (we check whether it's null) | |
| 58 | 54 | private IFileWatcher? _fileWatcher; | |
| 59 | 55 | ||
| 56 | + /// <summary> | ||
| 57 | + /// <para>This regex is used to determine successful initialization of the process.</para> | ||
| 58 | + /// <para>All match groups contained in the regex are passed as arguments to the <see cref="OnConnectionEstablishedMessageReceived"/> method.</para> | ||
| 59 | + /// </summary> | ||
| 60 | + protected abstract Regex ConnectionEstablishedMessageRegex { get; } | ||
| 61 | + | ||
| 60 | 62 | /// <summary> | |
| 61 | 63 | /// Creates an <see cref="OutOfProcessNodeJSService"/> instance. | |
| 62 | 64 | /// </summary> | |
@@ -117,8 +119,8 @@ protected OutOfProcessNodeJSService(INodeJSProcessFactory nodeProcessFactory, | |||
| 117 | 119 | /// <para>The message can be used to complete the handshake with the | |
| 118 | 120 | /// NodeJS process, for example by delivering a port and an IP address to use in further communications.</para> | |
| 119 | 121 | /// </summary> | |
| 120 | - /// <param name="connectionEstablishedMessage">The connection established message.</param> | ||
| 121 | - protected abstract void OnConnectionEstablishedMessageReceived(string connectionEstablishedMessage); | ||
| 122 | + /// <param name="connectionMessageMatch">The regex match that can be used to extract additional arguments to complete the handshake.</param> | ||
| 123 | + protected abstract void OnConnectionEstablishedMessageReceived(Match connectionMessageMatch); | ||
| 122 | 124 | ||
| 123 | 125 | /// <inheritdoc /> | |
| 124 | 126 | public virtual async Task<T?> InvokeFromFileAsync<T>(string modulePath, string? exportName = null, object?[]? args = null, CancellationToken cancellationToken = default) | |
@@ -671,9 +673,9 @@ internal void OutputReceivedHandler(string message, EventWaitHandle waitHandle) | |||
| 671 | 673 | // | |
| 672 | 674 | // Note that we should not get a connection message for any process other than the current _nodeJSProcess | |
| 673 | 675 | // because ConnectIfNotConnected is synchronous. | |
| 674 | - if (_nodeJSProcess?.Connected == false && message.StartsWith(CONNECTION_ESTABLISHED_MESSAGE_START)) | ||
| 676 | + if (_nodeJSProcess?.Connected == false && ConnectionEstablishedMessageRegex.Match(message) is { Success: true } match) | ||
| 675 | 677 | { | |
| 676 | - OnConnectionEstablishedMessageReceived(message); | ||
| 678 | + OnConnectionEstablishedMessageReceived(match); | ||
| 677 | 679 | ||
| 678 | 680 | if (_infoLoggingEnabled) | |
| 679 | 681 | { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -69,9 +69,9 @@ | |||
| 69 | 69 | }, | |
| 70 | 70 | "Nullable": { | |
| 71 | 71 | "type": "Direct", | |
| 72 | - "requested": "[1.3.0, )", | ||
| 73 | - "resolved": "1.3.0", | ||
| 74 | - "contentHash": "xHAviTdTY3n+t1nEPN4JPRQR5lI124qRKVw+U9H7dO5sDNPpzoWeo/MQy7dSUmv9eD3k/CJVKokz1tFK+JOzRw==" | ||
| 72 | + "requested": "[1.3.1, )", | ||
| 73 | + "resolved": "1.3.1", | ||
| 74 | + "contentHash": "Mk4ZVDfAORTjvckQprCSehi1XgOAAlk5ez06Va/acRYEloN9t6d6zpzJRn5MEq7+RnagyFIq9r+kbWzLGd+6QA==" | ||
| 75 | 75 | }, | |
| 76 | 76 | "System.Text.Encodings.Web": { | |
| 77 | 77 | "type": "Direct", | |
@@ -255,9 +255,9 @@ | |||
| 255 | 255 | }, | |
| 256 | 256 | "Nullable": { | |
| 257 | 257 | "type": "Direct", | |
| 258 | - "requested": "[1.3.0, )", | ||
| 259 | - "resolved": "1.3.0", | ||
| 260 | - "contentHash": "xHAviTdTY3n+t1nEPN4JPRQR5lI124qRKVw+U9H7dO5sDNPpzoWeo/MQy7dSUmv9eD3k/CJVKokz1tFK+JOzRw==" | ||
| 258 | + "requested": "[1.3.1, )", | ||
| 259 | + "resolved": "1.3.1", | ||
| 260 | + "contentHash": "Mk4ZVDfAORTjvckQprCSehi1XgOAAlk5ez06Va/acRYEloN9t6d6zpzJRn5MEq7+RnagyFIq9r+kbWzLGd+6QA==" | ||
| 261 | 261 | }, | |
| 262 | 262 | "System.Text.Encodings.Web": { | |
| 263 | 263 | "type": "Direct", | |
@@ -509,9 +509,9 @@ | |||
| 509 | 509 | }, | |
| 510 | 510 | "Nullable": { | |
| 511 | 511 | "type": "Direct", | |
| 512 | - "requested": "[1.3.0, )", | ||
| 513 | - "resolved": "1.3.0", | ||
| 514 | - "contentHash": "xHAviTdTY3n+t1nEPN4JPRQR5lI124qRKVw+U9H7dO5sDNPpzoWeo/MQy7dSUmv9eD3k/CJVKokz1tFK+JOzRw==" | ||
| 512 | + "requested": "[1.3.1, )", | ||
| 513 | + "resolved": "1.3.1", | ||
| 514 | + "contentHash": "Mk4ZVDfAORTjvckQprCSehi1XgOAAlk5ez06Va/acRYEloN9t6d6zpzJRn5MEq7+RnagyFIq9r+kbWzLGd+6QA==" | ||
| 515 | 515 | }, | |
| 516 | 516 | "System.Text.Encodings.Web": { | |
| 517 | 517 | "type": "Direct", | |
@@ -750,9 +750,9 @@ | |||
| 750 | 750 | }, | |
| 751 | 751 | "Nullable": { | |
| 752 | 752 | "type": "Direct", | |
| 753 | - "requested": "[1.3.0, )", | ||
| 754 | - "resolved": "1.3.0", | ||
| 755 | - "contentHash": "xHAviTdTY3n+t1nEPN4JPRQR5lI124qRKVw+U9H7dO5sDNPpzoWeo/MQy7dSUmv9eD3k/CJVKokz1tFK+JOzRw==" | ||
| 753 | + "requested": "[1.3.1, )", | ||
| 754 | + "resolved": "1.3.1", | ||
| 755 | + "contentHash": "Mk4ZVDfAORTjvckQprCSehi1XgOAAlk5ez06Va/acRYEloN9t6d6zpzJRn5MEq7+RnagyFIq9r+kbWzLGd+6QA==" | ||
| 756 | 756 | }, | |
| 757 | 757 | "System.Text.Encodings.Web": { | |
| 758 | 758 | "type": "Direct", | |
@@ -918,9 +918,9 @@ | |||
| 918 | 918 | }, | |
| 919 | 919 | "Nullable": { | |
| 920 | 920 | "type": "Direct", | |
| 921 | - "requested": "[1.3.0, )", | ||
| 922 | - "resolved": "1.3.0", | ||
| 923 | - "contentHash": "xHAviTdTY3n+t1nEPN4JPRQR5lI124qRKVw+U9H7dO5sDNPpzoWeo/MQy7dSUmv9eD3k/CJVKokz1tFK+JOzRw==" | ||
| 921 | + "requested": "[1.3.1, )", | ||
| 922 | + "resolved": "1.3.1", | ||
| 923 | + "contentHash": "Mk4ZVDfAORTjvckQprCSehi1XgOAAlk5ez06Va/acRYEloN9t6d6zpzJRn5MEq7+RnagyFIq9r+kbWzLGd+6QA==" | ||
| 924 | 924 | }, | |
| 925 | 925 | "System.Text.Encodings.Web": { | |
| 926 | 926 | "type": "Direct", | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -8,6 +8,7 @@ | |||
| 8 | 8 | using System.Net; | |
| 9 | 9 | using System.Net.Http; | |
| 10 | 10 | using System.Text; | |
| 11 | + using System.Text.RegularExpressions; | ||
| 11 | 12 | using System.Threading; | |
| 12 | 13 | using System.Threading.Tasks; | |
| 13 | 14 | using Xunit; | |
@@ -207,27 +208,28 @@ public async Task TryInvokeAsync_ThrowsInvocationExceptionIfHttpResponseHasAnUne | |||
| 207 | 208 | ||
| 208 | 209 | [Theory] | |
| 209 | 210 | [MemberData(nameof(OnConnectionEstablishedMessageReceived_ExtractsEndPoint_Data))] | |
| 210 | - public void OnConnectionEstablishedMessageReceived_ExtractsEndPoint(string dummyIP, string dummyPort, string expectedResult) | ||
| 211 | + public void OnConnectionEstablishedMessageReceived_ExtractsEndPoint(string dummyHttpVersion, string dummyIP, string dummyPort, string expectedResult) | ||
| 211 | 212 | { | |
| 212 | 213 | // Arrange | |
| 213 | 214 | var loggerStringBuilder = new StringBuilder(); | |
| 214 | - string dummyConnectionEstablishedMessage = $"[Jering.Javascript.NodeJS: HttpVersion - HTTP/1.1 Listening on IP - {dummyIP} Port - {dummyPort}]"; | ||
| 215 | + string dummyConnectionEstablishedMessage = $"[Jering.Javascript.NodeJS: HttpVersion - {dummyHttpVersion} Listening on IP - {dummyIP} Port - {dummyPort}]"; | ||
| 215 | 216 | ExposedHttpNodeJSService testSubject = CreateHttpNodeJSService(loggerStringBuilder: loggerStringBuilder); | |
| 216 | 217 | ||
| 217 | 218 | // Act | |
| 218 | - testSubject.ExposedOnConnectionEstablishedMessageReceived(dummyConnectionEstablishedMessage); | ||
| 219 | + testSubject.ExposedOnConnectionEstablishedMessageReceived(testSubject.ExposedConnectionEstablishedMessageRegex.Match(dummyConnectionEstablishedMessage)); | ||
| 219 | 220 | ||
| 220 | 221 | // Assert | |
| 221 | 222 | Assert.Equal(expectedResult, testSubject._endpoint?.AbsoluteUri); | |
| 222 | - Assert.Contains(string.Format(Strings.LogInformation_HttpEndpoint, "HTTP/1.1", expectedResult), loggerStringBuilder.ToString()); | ||
| 223 | + Assert.Contains(string.Format(Strings.LogInformation_HttpEndpoint, dummyHttpVersion, expectedResult), loggerStringBuilder.ToString()); | ||
| 223 | 224 | } | |
| 224 | 225 | ||
| 225 | 226 | public static IEnumerable<object[]> OnConnectionEstablishedMessageReceived_ExtractsEndPoint_Data() | |
| 226 | 227 | { | |
| 227 | 228 | return new object[][] | |
| 228 | 229 | { | |
| 229 | - new object[]{"127.0.0.1", "12345", "http://127.0.0.1:12345/"}, // IPv4, arbitrary port | ||
| 230 | - new object[]{"::1", "543", "http://[::1]:543/"} // IPv6, arbitrary port | ||
| 230 | + new object[]{ "HTTP/1.1", "127.0.0.1", "12345", "http://127.0.0.1:12345/"}, // Http 1.1, IPv4, arbitrary port | ||
| 231 | + new object[]{ "HTTP/1.1", "::1", "543", "http://[::1]:543/"}, // Http 1.1, IPv6, arbitrary port | ||
| 232 | + new object[]{ "HTTP/2.0", "127.0.0.1", "12345", "http://127.0.0.1:12345/"} // Http 2.0, IPv4, arbitrary port | ||
| 231 | 233 | }; | |
| 232 | 234 | } | |
| 233 | 235 | ||
@@ -326,14 +328,16 @@ public ExposedHttpNodeJSService(IOptions<OutOfProcessNodeJSServiceOptions> outOf | |||
| 326 | 328 | { | |
| 327 | 329 | } | |
| 328 | 330 | ||
| 331 | + public Regex ExposedConnectionEstablishedMessageRegex => ConnectionEstablishedMessageRegex; | ||
| 332 | + | ||
| 329 | 333 | public Task<(bool, T?)> ExposedTryInvokeAsync<T>(InvocationRequest invocationRequest, CancellationToken cancellationToken) | |
| 330 | 334 | { | |
| 331 | 335 | return TryInvokeAsync<T>(invocationRequest, cancellationToken); | |
| 332 | 336 | } | |
| 333 | 337 | ||
| 334 | - public void ExposedOnConnectionEstablishedMessageReceived(string connectionEstablishedMessage) | ||
| 338 | + public void ExposedOnConnectionEstablishedMessageReceived(System.Text.RegularExpressions.Match connectionEstablishedMessageMatch) | ||
| 335 | 339 | { | |
| 336 | - OnConnectionEstablishedMessageReceived(connectionEstablishedMessage); | ||
| 340 | + OnConnectionEstablishedMessageReceived(connectionEstablishedMessageMatch); | ||
| 337 | 341 | } | |
| 338 | 342 | } | |
| 339 | 343 | } | |
| Back | FazBrowse Home | New Git URL |
0 commit comments