5
5
using System . Collections . Concurrent ;
6
6
using System . Collections . ObjectModel ;
7
7
using Amqp ;
8
- using Amqp . Handler ;
8
+ using Amqp . Framing ;
9
9
using Amqp . Sasl ;
10
+ using Amqp . Types ;
10
11
11
12
namespace RabbitMQ . AMQP . Client . Impl ;
12
13
13
14
/// <summary>
14
15
/// AmqpConnection is the concrete implementation of <see cref="IConnection"/>
15
16
/// It is a wrapper around the AMQP.Net Lite <see cref="Connection"/> class
16
17
/// </summary>
17
- public class AmqpConnection : AbstractLifeCycle , IConnection , IHandler
18
+ public class AmqpConnection : AbstractLifeCycle , IConnection
18
19
{
19
20
private const string ConnectionNotRecoveredCode = "CONNECTION_NOT_RECOVERED" ;
20
21
private const string ConnectionNotRecoveredMessage = "Connection not recovered" ;
@@ -24,6 +25,7 @@ public class AmqpConnection : AbstractLifeCycle, IConnection, IHandler
24
25
25
26
// The native AMQP.Net Lite connection
26
27
private Connection ? _nativeConnection ;
28
+ private ClosedCallback ? _closedCallback ;
27
29
28
30
private readonly AmqpManagement _management ;
29
31
private readonly RecordingTopologyListener _recordingTopologyListener = new ( ) ;
@@ -133,90 +135,6 @@ await _connectionCloseTaskCompletionSource.Task.WaitAsync(TimeSpan.FromSeconds(1
133
135
OnNewStatus ( State . Closed , null ) ;
134
136
}
135
137
136
- public bool CanHandle ( EventId id )
137
- {
138
- Trace . WriteLine ( TraceLevel . Verbose , $ "EventId: { id } ") ;
139
- return id switch
140
- {
141
- EventId . SocketAccept => false ,
142
- EventId . SocketConnect => false ,
143
- EventId . ConnectionLocalOpen => true ,
144
- EventId . ConnectionRemoteOpen => false ,
145
- EventId . ConnectionLocalClose => false ,
146
- EventId . ConnectionRemoteClose => true ,
147
- EventId . SessionLocalOpen => false ,
148
- EventId . SessionRemoteOpen => false ,
149
- EventId . SessionLocalClose => false ,
150
- EventId . SessionRemoteClose => false ,
151
- EventId . LinkLocalOpen => false ,
152
- EventId . LinkRemoteOpen => false ,
153
- EventId . LinkLocalClose => false ,
154
- EventId . LinkRemoteClose => false ,
155
- EventId . SendDelivery => false ,
156
- EventId . ReceiveDelivery => false ,
157
- EventId . SslAuthenticate => false ,
158
- EventId . SslStreamAccept => false ,
159
- EventId . HttpAccept => false ,
160
- EventId . WebSocketAccept => false ,
161
- EventId . ConnectionAccept => false ,
162
- _ => false ,
163
- } ;
164
- }
165
-
166
- public void Handle ( Event protocolEvent )
167
- {
168
- Trace . WriteLine ( TraceLevel . Verbose , $ "protocolEvent: { protocolEvent } ") ;
169
- switch ( protocolEvent . Id )
170
- {
171
- case EventId . ConnectionLocalOpen :
172
- Trace . WriteLine ( TraceLevel . Verbose , $ "{ ToString ( ) } is open") ;
173
- OnNewStatus ( State . Open , null ) ;
174
- break ;
175
- case EventId . ConnectionRemoteOpen :
176
- break ;
177
- case EventId . ConnectionLocalClose :
178
- break ;
179
- case EventId . ConnectionRemoteClose :
180
- // TODO oy vey, what to do about the task?
181
- _ = Task . Run ( ( ) => ReconnectAsync ( ( Amqp . Framing . Close ) protocolEvent . Context ) ) ;
182
- break ;
183
- case EventId . SessionLocalOpen :
184
- break ;
185
- case EventId . SessionRemoteOpen :
186
- break ;
187
- case EventId . SessionLocalClose :
188
- break ;
189
- case EventId . SessionRemoteClose :
190
- break ;
191
- case EventId . LinkLocalOpen :
192
- break ;
193
- case EventId . LinkRemoteOpen :
194
- break ;
195
- case EventId . LinkLocalClose :
196
- break ;
197
- case EventId . LinkRemoteClose :
198
- break ;
199
- case EventId . SendDelivery :
200
- break ;
201
- case EventId . ReceiveDelivery :
202
- break ;
203
- case EventId . SocketConnect :
204
- break ;
205
- case EventId . SslAuthenticate :
206
- break ;
207
- case EventId . SocketAccept :
208
- break ;
209
- case EventId . SslStreamAccept :
210
- break ;
211
- case EventId . HttpAccept :
212
- break ;
213
- case EventId . WebSocketAccept :
214
- break ;
215
- case EventId . ConnectionAccept :
216
- break ;
217
- }
218
- }
219
-
220
138
public override string ToString ( )
221
139
{
222
140
string info = $ "AmqpConnection{{ConnectionSettings='{ _connectionSettings } ', Status='{ State . ToString ( ) } '}}";
@@ -232,6 +150,11 @@ protected override void Dispose(bool disposing)
232
150
{
233
151
if ( disposing )
234
152
{
153
+ if ( _nativeConnection is not null &&
154
+ _closedCallback is not null )
155
+ {
156
+ _nativeConnection . Closed -= _closedCallback ;
157
+ }
235
158
_semaphoreOpen . Dispose ( ) ;
236
159
_semaphoreClose . Dispose ( ) ;
237
160
}
@@ -283,33 +206,26 @@ await _semaphoreOpen.WaitAsync()
283
206
return ;
284
207
}
285
208
286
- /*
287
209
var open = new Open
288
210
{
211
+ // Note: no need to set cf.AMQP.HostName
289
212
HostName = $ "vhost:{ _connectionSettings . VirtualHost } ",
213
+ // Note: no need to set cf.AMQP.ContainerId
290
214
ContainerId = _connectionSettings . ContainerId ,
291
215
Properties = new Fields ( )
292
216
{
293
217
[ new Symbol ( "connection_name" ) ] = _connectionSettings . ContainerId ,
294
218
}
295
219
} ;
296
220
297
- if (_connectionSettings.MaxFrameSize > int .MinValue)
221
+ if ( _connectionSettings . MaxFrameSize > uint . MinValue )
298
222
{
299
223
// Note: when set here, there is no need to set cf.AMQP.MaxFrameSize
300
224
open . MaxFrameSize = _connectionSettings . MaxFrameSize ;
301
225
}
302
- */
303
226
304
227
var cf = new ConnectionFactory ( ) ;
305
228
306
- cf . AMQP . ContainerId = _connectionSettings . ContainerId ;
307
- cf . AMQP . HostName = $ "vhost:{ _connectionSettings . VirtualHost } ";
308
- if ( _connectionSettings . MaxFrameSize > int . MinValue )
309
- {
310
- cf . AMQP . MaxFrameSize = _connectionSettings . MaxFrameSize ;
311
- }
312
-
313
229
if ( _connectionSettings is { UseSsl : true , TlsSettings : not null } )
314
230
{
315
231
cf . SSL . Protocols = _connectionSettings . TlsSettings . Protocols ;
@@ -338,13 +254,11 @@ [new Symbol("connection_name")] = _connectionSettings.ContainerId,
338
254
cf . SASL . Profile = SaslProfile . External ;
339
255
}
340
256
341
- /*
342
257
void OnOpened ( Amqp . IConnection connection , Open open1 )
343
258
{
344
259
Trace . WriteLine ( TraceLevel . Verbose , $ "{ ToString ( ) } is open") ;
345
260
OnNewStatus ( State . Open , null ) ;
346
261
}
347
- */
348
262
349
263
try
350
264
{
@@ -358,8 +272,8 @@ void OnOpened(Amqp.IConnection connection, Open open1)
358
272
{
359
273
connectionSettings = ( ConnectionSettings ) _connectionSettings ;
360
274
Address address = connectionSettings . Address ;
361
- _nativeConnection = await cf . CreateAsync ( address : address , handler : this ) . ConfigureAwait ( false ) ;
362
- // _nativeConnection = await cf.CreateAsync(address: address, open: open, onOpened: OnOpened) .ConfigureAwait(false);
275
+ _nativeConnection = await cf . CreateAsync ( address : address , open : open , onOpened : OnOpened )
276
+ . ConfigureAwait ( false ) ;
363
277
}
364
278
}
365
279
catch ( Exception ex )
@@ -377,8 +291,8 @@ void OnOpened(Amqp.IConnection connection, Open open1)
377
291
await _management . OpenAsync ( )
378
292
. ConfigureAwait ( false ) ;
379
293
380
- // _closedCallback = BuildClosedCallback();
381
- // _nativeConnection.AddClosedCallback(_closedCallback);
294
+ _closedCallback = BuildClosedCallback ( ) ;
295
+ _nativeConnection . AddClosedCallback ( _closedCallback ) ;
382
296
}
383
297
catch ( AmqpException e )
384
298
{
@@ -391,98 +305,6 @@ await _management.OpenAsync()
391
305
}
392
306
}
393
307
394
- private void DoClose ( Amqp . Framing . Error errorFrame , Error ? argError = null )
395
- {
396
- Error ? err = argError ?? Utils . ConvertError ( errorFrame ) ;
397
- Trace . WriteLine ( TraceLevel . Verbose , $ "{ ToString ( ) } is closed") ;
398
- OnNewStatus ( State . Closed , err ) ;
399
- ChangeEntitiesStatus ( State . Closed , err ) ;
400
- _connectionCloseTaskCompletionSource . SetResult ( true ) ;
401
- }
402
-
403
- private async Task ReconnectAsync ( Amqp . Framing . Close closeFrame )
404
- {
405
- Amqp . Framing . Error errorFrame = closeFrame . Error ;
406
-
407
- // change the status for the connection and all the entities
408
- // to reconnecting and all the events are fired
409
- OnNewStatus ( State . Reconnecting , Utils . ConvertError ( errorFrame ) ) ;
410
- ChangeEntitiesStatus ( State . Reconnecting , Utils . ConvertError ( errorFrame ) ) ;
411
-
412
- IBackOffDelayPolicy backOffDelayPolicy = _connectionSettings . Recovery . GetBackOffDelayPolicy ( ) ;
413
- bool connected = false ;
414
- // as first step we try to recover the connection
415
- // so the connected flag is false
416
- while ( false == connected &&
417
- // we have to check if the backoff policy is active
418
- // the user may want to disable the backoff policy or
419
- // the backoff policy is not active due of some condition
420
- // for example: Reaching the maximum number of retries and avoid the forever loop
421
- backOffDelayPolicy . IsActive ( ) &&
422
- // even we set the status to reconnecting up, we need to check if the connection is still in the
423
- // reconnecting status. The user may close the connection in the meanwhile
424
- State == State . Reconnecting )
425
- {
426
- try
427
- {
428
- int nextDelayMs = backOffDelayPolicy . Delay ( ) ;
429
-
430
- Trace . WriteLine ( TraceLevel . Information ,
431
- $ "{ ToString ( ) } is trying Recovering connection in { nextDelayMs } milliseconds, " +
432
- $ "attempt: { _connectionSettings . Recovery . GetBackOffDelayPolicy ( ) . CurrentAttempt } . ") ;
433
-
434
- await Task . Delay ( TimeSpan . FromMilliseconds ( nextDelayMs ) )
435
- . ConfigureAwait ( false ) ;
436
-
437
- await OpenConnectionAsync ( )
438
- . ConfigureAwait ( false ) ;
439
-
440
- connected = true ;
441
- }
442
- catch ( Exception e )
443
- {
444
- // TODO this could / should be more visible to the user, perhaps?
445
- Trace . WriteLine ( TraceLevel . Warning ,
446
- $ "{ ToString ( ) } Error trying to recover connection { e } ") ;
447
- }
448
- }
449
-
450
- backOffDelayPolicy . Reset ( ) ;
451
- string connectionDescription = connected ? "recovered" : "not recovered" ;
452
- Trace . WriteLine ( TraceLevel . Information ,
453
- $ "{ ToString ( ) } is { connectionDescription } ") ;
454
-
455
- if ( false == connected )
456
- {
457
- var notRecoveredError = new Error ( ConnectionNotRecoveredCode ,
458
- $ "{ ConnectionNotRecoveredMessage } ," +
459
- $ "recover status: { _connectionSettings . Recovery } ") ;
460
- DoClose ( errorFrame , notRecoveredError ) ;
461
- return ;
462
- }
463
-
464
- if ( _connectionSettings . Recovery . IsTopologyActive ( ) )
465
- {
466
- Trace . WriteLine ( TraceLevel . Information , $ "{ ToString ( ) } Recovering topology") ;
467
- var visitor = new Visitor ( _management ) ;
468
- await _recordingTopologyListener . Accept ( visitor )
469
- . ConfigureAwait ( false ) ;
470
- }
471
-
472
- OnNewStatus ( State . Open , null ) ;
473
-
474
- // after the connection is recovered we have to reconnect all the publishers and consumers
475
- try
476
- {
477
- await ReconnectEntitiesAsync ( )
478
- . ConfigureAwait ( false ) ;
479
- }
480
- catch ( Exception e )
481
- {
482
- Trace . WriteLine ( TraceLevel . Error , $ "{ ToString ( ) } error trying to reconnect entities { e } ") ;
483
- }
484
- }
485
-
486
308
/// <summary>
487
309
/// Event handler for the connection closed event.
488
310
/// In case the error is null means that the connection is closed by the user.
@@ -494,7 +316,6 @@ await ReconnectEntitiesAsync()
494
316
/// and then kick off a task dedicated to recovery
495
317
/// </summary>
496
318
/// <returns></returns>
497
- /*
498
319
private ClosedCallback BuildClosedCallback ( )
499
320
{
500
321
return async ( sender , error ) =>
@@ -511,6 +332,15 @@ await _semaphoreClose.WaitAsync()
511
332
// close all the sessions, if the connection is closed the sessions are not valid anymore
512
333
_nativePubSubSessions . ClearSessions ( ) ;
513
334
335
+ void DoClose ( Error ? argError = null )
336
+ {
337
+ Error ? err = argError ?? Utils . ConvertError ( error ) ;
338
+ Trace . WriteLine ( TraceLevel . Verbose , $ "{ ToString ( ) } is closed") ;
339
+ OnNewStatus ( State . Closed , err ) ;
340
+ ChangeEntitiesStatus ( State . Closed , err ) ;
341
+ _connectionCloseTaskCompletionSource . SetResult ( true ) ;
342
+ }
343
+
514
344
if ( error is null )
515
345
{
516
346
DoClose ( ) ;
@@ -631,7 +461,6 @@ await ReconnectEntitiesAsync()
631
461
}
632
462
} ;
633
463
}
634
- */
635
464
636
465
private void ChangeEntitiesStatus ( State state , Error ? error )
637
466
{
0 commit comments