Not logged in. · Lost password · Register
Forum: MatriX RSS
Avatar
ssteiner #1
Member since Jan 2016 · 26 posts · Location: Switzerland
Group memberships: Members
Show profile · Link to this post
Subject: wait for connection
Hi

I don't suppose there's a way to do something like

  1. ConnectionResult res = await xmppClient.ConnectAsync(login, server, password).ConfigureAwait()

that would actually wait for the bind, and only return upon success or if an error has occurred? I'm currently facing the issue that I'm too fast in reconnection.. the xmpp channel may not be fully up yet so when I start subscribing again, the system on the other end isn't ready for me and turns down my requests with 400 responses.
This post was edited on 2016-03-04, 22:04 by Alex.
Avatar
Alex #2
Member since Feb 2003 · 4322 posts · Location: Germany
Group memberships: Administrators, Members
Show profile · Link to this post
you can write a simple wrapper yourself which gives you just a wrapper  from APM/event pattern to TAP.
Such an extension is on our TODO list for one of the next releases.


See also:
https://msdn.microsoft.com/en-us/library/dd997423(v=vs.110…

Alex
This post was edited on 2016-03-04, 22:10 by Alex.
Avatar
ssteiner #3
Member since Jan 2016 · 26 posts · Location: Switzerland
Group memberships: Members
Show profile · Link to this post
Well, but we're in a bit a different situation, aren't we? There's no begin and end method for the connect either. So basically you'd have to use a Taskcompletionsource that you can wait on, and then wait for all potential On** callbacks to determine the result of the connection attempt.

And I don't even know what kind of On* callbacks could be returned... there's of course OnBind, OnError, but what else is there? OnAuthError, OnBindError? Is that it.. and couldn't you concievably get another On* event in the meantime (imagine calling an Open and immediately trying to close again).. and of course you'd then also want a taskcompletionsource to wait on for closing the connection, so you'd have to determine just exactly which task completionsource you now have to touch on all the On* callbacks.

Or am I missing something.. are there traditional BeginConnect and EndConnect methods that I could wrap in a task?
Avatar
Alex #4
Member since Feb 2003 · 4322 posts · Location: Germany
Group memberships: Administrators, Members
Show profile · Link to this post
Quote by ssteiner:
Well, but we're in a bit a different situation, aren't we? There's no begin and end method for the connect either. So basically you'd have to use a Taskcompletionsource that you can wait on, and then wait for all potential On** callbacks to determine the result of the connection attempt.

you can write wrappers fro the APM (BeginX EndX) and event based pattern. There are examples for both of them in the MSDN

Quote by ssteiner:
And I don't even know what kind of On* callbacks could be returned... there's of course OnBind, OnError, but what else is there? OnAuthError, OnBindError? Is that it.. and couldn't you concievably get another On* event in the meantime (imagine calling an Open and immediately trying to close again).. and of course you'd then also want a taskcompletionsource to wait on for closing the connection, so you'd have to determine just exactly which task completionsource you now have to touch on all the On* callbacks.

here is an example. You can add more handlers when required to it.
I usually suggest to wait until you get the first OnPresece event to indicate if your session is fully ready for data. This is why this example is also using the OnPresence event.

  1. /// <summary>
  2. /// Opens the XMPP connection asynchronous.
  3. /// This method can work only when AutoPresence is set to true. Because of this the AutoPresence flag gets changed automatically
  4. /// when set to false prior executing.
  5. /// </summary>
  6. /// <remarks>This works only with compliant XMPP servers. This may not work on some older servers which are not 100% XMPP
  7. /// compliant.</remarks>
  8. /// <param name="timeout">The timeout.</param>
  9. /// <returns>True on success, otherwise false.</returns>
  10. /// <exception cref="Matrix.AuthenticationException">Thrown when authentication fails.</exception>
  11. /// <exception cref="System.TimeoutException">Thrown when operation times out.</exception>
  12. public async Task<bool> OpenAsync(int? timeout)
  13. {
  14.     AutoPresence = true;
  15.  
  16.     Jid myJid = null;
  17.     Exception ex = null;
  18.  
  19.     EventHandler<EventArgs>             closeHandler        = null;
  20.     EventHandler<ExceptionEventArgs>    errorHandler        = null;
  21.     EventHandler<JidEventArgs>          bindHandler         = null;
  22.     EventHandler<PresenceEventArgs>     presenceHandler     = null;
  23.     EventHandler<SaslEventArgs>         authErrorHandler    = null;
  24.     EventHandler<CertificateEventArgs>  certificateHandler  = null;
  25.  
  26.     var resultCompletionSource = new TaskCompletionSource<bool>();
  27.  
  28.     Action removeHanders = () =>
  29.     {
  30.         OnClose                 -= closeHandler;
  31.         OnBind                  -= bindHandler;
  32.         OnPresence              -= presenceHandler;
  33.         OnAuthError             -= authErrorHandler;
  34.         OnError                 -= errorHandler;
  35.         OnValidateCertificate   -= certificateHandler;
  36.     };
  37.  
  38.     closeHandler = (sender, args) =>
  39.     {
  40.         removeHanders();
  41.         if (ex == null)
  42.             resultCompletionSource.SetResult(false);
  43.         else
  44.             resultCompletionSource.SetException(ex);
  45.     };
  46.  
  47.     bindHandler = (sender, args) => myJid = args.Jid;
  48.    
  49.     presenceHandler = (sender, args) =>
  50.     {
  51.         if (myJid != null)
  52.         {
  53.             if (args.Presence.From.Equals(myJid, new FullJidComparer()))
  54.             {
  55.                 removeHanders();
  56.                 resultCompletionSource.SetResult(true);
  57.             }
  58.         }
  59.     };
  60.  
  61.     authErrorHandler = (sender, args) =>
  62.     {
  63.         ex = new AuthenticationException(args.Exception);
  64.         Close();
  65.     };
  66.  
  67.     errorHandler = (sender, args) =>
  68.     {
  69.         removeHanders();
  70.         resultCompletionSource.SetException(args.Exception);
  71.     };
  72.  
  73.     certificateHandler = (sender, args) =>
  74.     {
  75.         args.AcceptCertificate = true;
  76.     };
  77.  
  78.     OnBind      += bindHandler;
  79.     OnPresence  += presenceHandler;
  80.     OnClose     += closeHandler;
  81.     OnAuthError += authErrorHandler;
  82.     OnError     += errorHandler;
  83.  
  84.     Open();
  85.  
  86.     if (timeout.HasValue)
  87.     {
  88.         if (resultCompletionSource.Task ==
  89.             await Task.WhenAny(resultCompletionSource.Task, Task.Delay(timeout.Value)))
  90.             return await resultCompletionSource.Task;
  91.  
  92.         removeHanders();
  93.         resultCompletionSource.SetException(new TimeoutException());
  94.     }
  95.  
  96.     return await resultCompletionSource.Task;
  97. }
Avatar
ssteiner #5
Member since Jan 2016 · 26 posts · Location: Switzerland
Group memberships: Members
Show profile · Link to this post
@Alex: thanks.

What would happen if I were to call another method on the xmppClient while I'm waiting for OpenAsync? Wouldn't that trigger an OnError and so if those operations run concurrently, the OpenAsync would return false because it caught an error that was meant as a result for the other operation (e.g. setting presence while you're not fully connected yet)?

Also, what kind of events do I have to expect on the Close scenario to write a similar async close operation?
Avatar
Alex #6
Member since Feb 2003 · 4322 posts · Location: Germany
Group memberships: Administrators, Members
Show profile · Link to this post
Quote by ssteiner:
What would happen if I were to call another method on the xmppClient while I'm waiting for OpenAsync? Wouldn't that trigger an OnError and so if those operations run concurrently, the OpenAsync would return false because it caught an error that was meant as a result for the other operation (e.g. setting presence while you're not fully connected yet)?

you should not do that. You can open the connection only once. Its not designed for that. If you do so you may see strange errors.

Quote by ssteiner:
Also, what kind of events do I have to expect on the Close scenario to write a similar async close operation?

it should result in an OnClose event.

Alex
Avatar
ssteiner #7
Member since Jan 2016 · 26 posts · Location: Switzerland
Group memberships: Members
Show profile · Link to this post
you should not do that. You can open the connection only once. Its not designed for that. If you do so you may see strange errors.

Hehe.. so my guess was correct, the wrapper approach is a hack... a real async solution would require the xmppclient to have a full state machine to determine if an operation is even permissible. I take it such a redesign is not in the cards, then?

it should result in an OnClose event.
And only that.. shouldn't you catch OnError as well to be on the safe side?
Avatar
Alex #8
Member since Feb 2003 · 4322 posts · Location: Germany
Group memberships: Administrators, Members
Show profile · Link to this post
Quote by ssteiner:
Hehe.. so my guess was correct, the wrapper approach is a hack... a real async solution would require the xmppclient to have a full state machine to determine if an operation is even permissible. I take it such a redesign is not in the cards, then?

no it's not a hack, and the XmppClient has a state machine.
If you try to call Open twice you should get OnError event telling you:

The Open method cannot be called on an active stream.

You still should avoid doing it.

Alex
Avatar
ssteiner #9
Member since Jan 2016 · 26 posts · Location: Switzerland
Group memberships: Members
Show profile · Link to this post
But there's something missing to associate an error to an operation you launched..

Imagine if Open were to take a parameter requestId, that would be returned in an OnError.. so you could associate error with an operation. And that way you could have a filter on OnError.. e.g. in the async connection wrapper so it doesn't catch errors that have nothing to do with the connection attempt.
Avatar
ssteiner #10
Member since Jan 2016 · 26 posts · Location: Switzerland
Group memberships: Members
Show profile · Link to this post
When working with finesse, you may want to change the bindHandler to this

  1. bindHandler = (sender, args) =>
  2. {
  3.     myJid = args.Jid;
  4.     myJid.User = "finesse";
  5.     myJid.Resource = "web_framework";
  6. };

You don't get a presencestate from your user, or at least not at that time (since you have to subscribe with an agent and all). But, you get a presence state from the finesse@server/web_framework that you can go on.
This post was edited 2 times, last on 2016-03-08, 16:57 by Alex.
Avatar
Alex #11
Member since Feb 2003 · 4322 posts · Location: Germany
Group memberships: Administrators, Members
Show profile · Link to this post
In reply to post #9
Quote by ssteiner:
But there's something missing to associate an error to an operation you launched..

Imagine if Open were to take a parameter requestId, that would be returned in an OnError.. so you could associate error with an operation. And that way you could have a filter on OnError.. e.g. in the async connection wrapper so it doesn't catch errors that have nothing to do with the connection attempt.

I see no problem there. The OpenAsync should be also the only operation you call on a XmppClient. There is nothing else you should call concurrent on the same XmppClient instance.
If oyu run into any problems or have suggestion then come back to us and we are happy to make changes for an upcoming release.

Alex
Avatar
ssteiner #12
Member since Jan 2016 · 26 posts · Location: Switzerland
Group memberships: Members
Show profile · Link to this post
I understand where you're coming from, but in the thick of the battle, there's always a scenario that does not conform to what should be. And it would help write good code if there's such a mechanism that allows you to identify where an error comes from. Other APIs I work with do provide this mechanism so I can rest assure my event handling won't trigger anything unwanted. And imagine writing a lib on top of matrix that would then be used by some other entity.. you cannot ensure they're good citizens.

In an API design where operation is decoupled from error return, I believe having a correlator for the two could be helpful. Of course we only ever know for sure if shit hits the fan, but then it's generally too late.
Close Smaller – Larger + Reply to this post:
Verification code: VeriCode Please enter the word from the image into the text field below. (Type the letters only, lower case is okay.)
Smileys: :-) ;-) :-D :-p :blush: :cool: :rolleyes: :huh: :-/ <_< :-( :'( :#: :scared: 8-( :nuts: :-O
Special characters:
Forum: MatriX RSS