1
1
using System ;
2
- using System . Collections . Concurrent ;
3
- using System . Collections . Generic ;
4
2
using System . IO ;
5
3
using System . Linq ;
6
4
using System . Threading ;
@@ -20,20 +18,95 @@ internal class LogicalConnection : ILogicalConnection
20
18
{
21
19
private readonly MsgPackContext _msgPackContext ;
22
20
21
+ private readonly ClientOptions _clientOptions ;
22
+
23
+ private readonly RequestIdCounter _requestIdCounter ;
24
+
23
25
private readonly INetworkStreamPhysicalConnection _physicalConnection ;
24
26
25
- private long _currentRequestId ;
27
+ private readonly ReaderWriterLockSlim _physicalConnectionLock = new ReaderWriterLockSlim ( ) ;
26
28
27
- private readonly ConcurrentDictionary < RequestId , TaskCompletionSource < MemoryStream > > _pendingRequests =
28
- new ConcurrentDictionary < RequestId , TaskCompletionSource < MemoryStream > > ( ) ;
29
+ private readonly IResponseReader _responseReader ;
29
30
30
31
private readonly ILog _logWriter ;
31
32
32
- public LogicalConnection ( ClientOptions options , INetworkStreamPhysicalConnection physicalConnection )
33
+ private bool _disposed ;
34
+
35
+ public LogicalConnection ( ClientOptions options , RequestIdCounter requestIdCounter )
33
36
{
37
+ _clientOptions = options ;
38
+ _requestIdCounter = requestIdCounter ;
34
39
_msgPackContext = options . MsgPackContext ;
35
40
_logWriter = options . LogWriter ;
36
- _physicalConnection = physicalConnection ;
41
+
42
+ _physicalConnection = new NetworkStreamPhysicalConnection ( ) ;
43
+ _responseReader = new ResponseReader ( _clientOptions , _physicalConnection ) ;
44
+ }
45
+
46
+ public void Dispose ( )
47
+ {
48
+ if ( _disposed )
49
+ {
50
+ return ;
51
+ }
52
+
53
+ _disposed = true ;
54
+
55
+ _responseReader . Dispose ( ) ;
56
+ _physicalConnection . Dispose ( ) ;
57
+ }
58
+
59
+ public async Task Connect ( )
60
+ {
61
+ await _physicalConnection . Connect ( _clientOptions ) ;
62
+
63
+ var greetingsResponseBytes = new byte [ 128 ] ;
64
+ var readCount = await _physicalConnection . ReadAsync ( greetingsResponseBytes , 0 , greetingsResponseBytes . Length ) ;
65
+ if ( readCount != greetingsResponseBytes . Length )
66
+ {
67
+ throw ExceptionHelper . UnexpectedGreetingBytesCount ( readCount ) ;
68
+ }
69
+
70
+ var greetings = new GreetingsResponse ( greetingsResponseBytes ) ;
71
+
72
+ _clientOptions . LogWriter ? . WriteLine ( $ "Greetings received, salt is { Convert . ToBase64String ( greetings . Salt ) } .") ;
73
+
74
+ _responseReader . BeginReading ( ) ;
75
+
76
+ _clientOptions . LogWriter ? . WriteLine ( "Server responses reading started." ) ;
77
+
78
+ await LoginIfNotGuest ( greetings ) ;
79
+ }
80
+
81
+ public bool IsConnected ( )
82
+ {
83
+ if ( _disposed )
84
+ {
85
+ return false ;
86
+ }
87
+
88
+ if ( ! _responseReader . IsConnected ( ) || ! _physicalConnection . IsConnected ( ) )
89
+ {
90
+ return false ;
91
+ }
92
+
93
+ return true ;
94
+ }
95
+
96
+ private async Task LoginIfNotGuest ( GreetingsResponse greetings )
97
+ {
98
+ var singleNode = _clientOptions . ConnectionOptions . Nodes . Single ( ) ;
99
+
100
+ if ( string . IsNullOrEmpty ( singleNode . Uri . UserName ) )
101
+ {
102
+ _clientOptions . LogWriter ? . WriteLine ( "Guest mode, no authentication attempt." ) ;
103
+ return ;
104
+ }
105
+
106
+ var authenticateRequest = AuthenticationRequest . Create ( greetings , singleNode . Uri ) ;
107
+
108
+ await SendRequestWithEmptyResponse ( authenticateRequest ) ;
109
+ _clientOptions . LogWriter ? . WriteLine ( $ "Authentication request send: { authenticateRequest } ") ;
37
110
}
38
111
39
112
public async Task SendRequestWithEmptyResponse < TRequest > ( TRequest request )
@@ -48,15 +121,6 @@ public async Task<DataResponse<TResponse[]>> SendRequest<TRequest, TResponse>(TR
48
121
return await SendRequestImpl < TRequest , DataResponse < TResponse [ ] > > ( request ) ;
49
122
}
50
123
51
- public TaskCompletionSource < MemoryStream > PopResponseCompletionSource ( RequestId requestId , MemoryStream resultStream )
52
- {
53
- TaskCompletionSource < MemoryStream > request ;
54
-
55
- return _pendingRequests . TryRemove ( requestId , out request )
56
- ? request
57
- : null ;
58
- }
59
-
60
124
public static byte [ ] ReadFully ( Stream input )
61
125
{
62
126
input . Position = 0 ;
@@ -72,32 +136,43 @@ public static byte[] ReadFully(Stream input)
72
136
}
73
137
}
74
138
75
- public IEnumerable < TaskCompletionSource < MemoryStream > > PopAllResponseCompletionSources ( )
76
- {
77
- var result = _pendingRequests . Values . ToArray ( ) ;
78
- _pendingRequests . Clear ( ) ;
79
- return result ;
80
- }
81
-
82
139
private async Task < TResponse > SendRequestImpl < TRequest , TResponse > ( TRequest request )
83
140
where TRequest : IRequest
84
141
{
142
+ if ( _disposed )
143
+ {
144
+ throw new ObjectDisposedException ( nameof ( LogicalConnection ) ) ;
145
+ }
146
+
85
147
var bodyBuffer = MsgPackSerializer . Serialize ( request , _msgPackContext ) ;
86
148
87
- var requestId = GetRequestId ( ) ;
88
- var responseTask = GetResponseTask ( requestId ) ;
149
+ var requestId = _requestIdCounter . GetRequestId ( ) ;
150
+ var responseTask = _responseReader . GetResponseTask ( requestId ) ;
89
151
90
152
long headerLength ;
91
153
var headerBuffer = CreateAndSerializeBuffer ( request , requestId , bodyBuffer , out headerLength ) ;
92
154
93
- lock ( _physicalConnection )
155
+ try
94
156
{
157
+ _physicalConnectionLock . EnterWriteLock ( ) ;
158
+
95
159
_logWriter ? . WriteLine ( $ "Begin sending request header buffer, requestId: { requestId } , code: { request . Code } , length: { headerBuffer . Length } ") ;
96
- _physicalConnection . Write ( headerBuffer , 0 , Constants . PacketSizeBufferSize + ( int ) headerLength ) ;
160
+ _physicalConnection . Write ( headerBuffer , 0 , Constants . PacketSizeBufferSize + ( int ) headerLength ) ;
97
161
98
162
_logWriter ? . WriteLine ( $ "Begin sending request body buffer, length: { bodyBuffer . Length } ") ;
99
163
_physicalConnection . Write ( bodyBuffer , 0 , bodyBuffer . Length ) ;
100
164
}
165
+ catch ( Exception ex )
166
+ {
167
+ _logWriter ? . WriteLine (
168
+ $ "Request with requestId { requestId } failed, header:\n { ToReadableString ( headerBuffer ) } \n body: \n { ToReadableString ( bodyBuffer ) } ") ;
169
+ Dispose ( ) ;
170
+ throw ;
171
+ }
172
+ finally
173
+ {
174
+ _physicalConnectionLock . ExitWriteLock ( ) ;
175
+ }
101
176
102
177
try
103
178
{
@@ -137,22 +212,5 @@ private byte[] CreateAndSerializeBuffer<TRequest>(
137
212
MsgPackSerializer . Serialize ( packetLength , stream , _msgPackContext ) ;
138
213
return packetSizeBuffer ;
139
214
}
140
-
141
- private RequestId GetRequestId ( )
142
- {
143
- var requestId = Interlocked . Increment ( ref _currentRequestId ) ;
144
- return ( RequestId ) ( ulong ) requestId ;
145
- }
146
-
147
- private Task < MemoryStream > GetResponseTask ( RequestId requestId )
148
- {
149
- var tcs = new TaskCompletionSource < MemoryStream > ( ) ;
150
- if ( ! _pendingRequests . TryAdd ( requestId , tcs ) )
151
- {
152
- throw ExceptionHelper . RequestWithSuchIdAlreadySent ( requestId ) ;
153
- }
154
-
155
- return tcs . Task ;
156
- }
157
215
}
158
216
}
0 commit comments