4
4
#nullable disable
5
5
6
6
using System . Buffers ;
7
+ using System . Diagnostics ;
7
8
using System . Globalization ;
8
9
using System . IO . Pipes ;
9
10
10
11
namespace Microsoft . DotNet . Cli . Commands . Test . IPC ;
11
12
12
13
internal sealed class NamedPipeServer : NamedPipeBase
13
14
{
14
- private readonly Func < IRequest , Task < IResponse > > _callback ;
15
+ private readonly Func < NamedPipeServer , IRequest , Task < IResponse > > _callback ;
15
16
private readonly NamedPipeServerStream _namedPipeServerStream ;
16
17
private readonly CancellationToken _cancellationToken ;
17
18
private readonly MemoryStream _serializationBuffer = new ( ) ;
@@ -24,7 +25,7 @@ internal sealed class NamedPipeServer : NamedPipeBase
24
25
25
26
public NamedPipeServer (
26
27
PipeNameDescription pipeNameDescription ,
27
- Func < IRequest , Task < IResponse > > callback ,
28
+ Func < NamedPipeServer , IRequest , Task < IResponse > > callback ,
28
29
int maxNumberOfServerInstances ,
29
30
CancellationToken cancellationToken ,
30
31
bool skipUnknownMessages )
@@ -55,14 +56,6 @@ public async Task WaitConnectionAsync(CancellationToken cancellationToken)
55
56
// We are being cancelled, so we don't need to wait anymore
56
57
return ;
57
58
}
58
- catch ( Exception ex )
59
- {
60
- // TODO: it might be better idea to let this exception bubble to
61
- // TestApplication. There, we should handle it by printing the exception without failing
62
- // the whole dotnet process, and provide more info about the specific test app that failed.
63
- // Potentially even knowing whether it's test host or test host controller that failed?
64
- Environment . FailFast ( $ "[NamedPipeServer] Unhandled exception:{ Environment . NewLine } { ex } ", ex ) ;
65
- }
66
59
} , cancellationToken ) ;
67
60
}
68
61
@@ -94,6 +87,11 @@ private async Task InternalLoopAsync(CancellationToken cancellationToken)
94
87
if ( currentMessageSize == 0 )
95
88
{
96
89
// We need to read the message size, first 4 bytes
90
+ if ( currentReadBytes < sizeof ( int ) )
91
+ {
92
+ throw new UnreachableException ( CliCommandStrings . DotnetTestPipeIncompleteSize ) ;
93
+ }
94
+
97
95
currentMessageSize = BitConverter . ToInt32 ( _readBuffer , 0 ) ;
98
96
missingBytesToReadOfCurrentChunk = currentReadBytes - sizeof ( int ) ;
99
97
missingBytesToReadOfWholeMessage = currentMessageSize ;
@@ -107,6 +105,11 @@ private async Task InternalLoopAsync(CancellationToken cancellationToken)
107
105
missingBytesToReadOfWholeMessage -= missingBytesToReadOfCurrentChunk ;
108
106
}
109
107
108
+ if ( missingBytesToReadOfWholeMessage < 0 )
109
+ {
110
+ throw new UnreachableException ( CliCommandStrings . DotnetTestPipeOverlapping ) ;
111
+ }
112
+
110
113
// If we have read all the message, we can deserialize it
111
114
if ( missingBytesToReadOfWholeMessage == 0 )
112
115
{
@@ -124,7 +127,7 @@ private async Task InternalLoopAsync(CancellationToken cancellationToken)
124
127
var deserializedObject = ( IRequest ) requestNamedPipeSerializer . Deserialize ( _messageBuffer ) ;
125
128
126
129
// Call the callback
127
- IResponse response = await _callback ( deserializedObject ) ;
130
+ IResponse response = await _callback ( this , deserializedObject ) ;
128
131
129
132
// Write the message size
130
133
_messageBuffer . Position = 0 ;
@@ -205,21 +208,28 @@ public void Dispose()
205
208
return ;
206
209
}
207
210
208
- if ( WasConnected )
211
+ try
209
212
{
210
- // If the loop task is null at this point we have race condition, means that the task didn't start yet and we already dispose.
211
- // This is unexpected and we throw an exception.
212
-
213
- // To close gracefully we need to ensure that the client closed the stream line 103.
214
- if ( ! _loopTask . Wait ( TimeSpan . FromSeconds ( 90 ) ) )
213
+ if ( WasConnected )
215
214
{
216
- throw new InvalidOperationException ( "InternalLoopAsyncDidNotExitSuccessfullyErrorMessage" ) ;
215
+ // If the loop task is null at this point we have race condition, means that the task didn't start yet and we already dispose.
216
+ // This is unexpected and we throw an exception.
217
+
218
+ // To close gracefully we need to ensure that the client closed the stream line 103.
219
+ if ( ! _loopTask . Wait ( TimeSpan . FromSeconds ( 90 ) ) )
220
+ {
221
+ throw new InvalidOperationException ( CliCommandStrings . InternalLoopAsyncDidNotExitSuccessfullyErrorMessage ) ;
222
+ }
217
223
}
218
224
}
225
+ finally
226
+ {
227
+ // Ensure we are still disposing the resouces correctly, even if _loopTask completes with
228
+ // an exception, or if the task doesn't complete within the 90 seconds limit.
229
+ _namedPipeServerStream . Dispose ( ) ;
230
+ PipeName . Dispose ( ) ;
219
231
220
- _namedPipeServerStream . Dispose ( ) ;
221
- PipeName . Dispose ( ) ;
222
-
223
- _disposed = true ;
232
+ _disposed = true ;
233
+ }
224
234
}
225
235
}
0 commit comments