-
Notifications
You must be signed in to change notification settings - Fork 62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
IQ result routes #121
IQ result routes #121
Conversation
These are used to quickly match IQ result stanzas and invoke a handler for them. IQ result routes take precendence of normal routes.
eba7e17
to
df57296
Compare
The map is updated from multiple goroutines, so it needs to be locked.
Simplify the API in several ways: - provide the context to the IQ result handler, making it possible to pass in extra context and handle timeouts within the handler. - pass the stanza in as an IQ type, removing the need to always type-cast it in the handler - remove Router.HandleIqResult and Router.HandleFuncIqResult. Since the router is private to Client nobody would ever use these, and they do not really make things simpler anyway.
@mremond This is ready for review now. |
I'm happy with the API, but it turns out to have one major flaw: it is leaking contexts. Back to the drawing board again! |
This makes sending IQ more idiomatic Go, but more importantly it solves a problem with contexts that were not being cancelled correctly with the previous API. As a side-effect of this change `Route.route` must now be invoked in a go-routine to prevent deadlocks. This also allows for stanzas to be processed in parallel, which can result in a nice performance win.
I have reworked the API to use a result channel, which also feels a lot more idiomatic. Usage is pretty simple. iq := stanza.NewIQ(stanza.Attrs{Type: stanza.IQTypeGet, To: "…"})
result, err := client.SendIQ(context.Background(), iq)
reply <- result
fmt.Printf("We received an IQ result: %v", reply) If you want to do full error handling and add a 30 second timeout (which I would highly recommend) the code becomes a bit more verbose, but it does feel like pretty standard Go to me: iq := stanza.NewIQ(stanza.Attrs{Type: stanza.IQTypeGet, To: "…"})
ctx, _ := context.WithTimeout(context.Background(), time.Second * 30)
def ctx.Cancel()
result, err := client.SendIQ(ctx, iq)
if err != nil {
log.Fatalf*("Error sending IQ message: %v", err)
}
select {
case iq := <- result:
fmt.Printf("We received a result: %v", iq)
case <- ctx.Done():
fmt.Printf("No result received in 30 seconds ☹️");
}) One thing to note is that there was a possible deadlock if a stanza handler wanted to send an IQ itself: in that case The tests pass with |
I changed idiotic to idiomatic in previous comment - I hadn't notice autocorrect correcting a typo to something I really did not intend 😬 |
Thanks @wichert It feels right to use channel for that need. I will review the code ASAP. |
This makes it possible to use SendIQ from PostConnect and route handlers.
FWIW I just started using this and it works well for me. I did need to make one last change: add SendIQ to Sender and StreamClient so you can use it from handlers. |
Thanks, I am at a conference, so I may take some time before I can review this, but the plan is to sort the code in the pipeline during the next week. |
This is an attempt to fix #78. The approach I am taking is to introduce an API like this to the router:
The context is there to support cancellation and timeouts. They have become a pretty standard feature in golang webservers, and work well here. With a timeout the example becomes:
To make this a little easier to use I am thinking of adding a method to
Client
to combine sending an IQ and adding a result route. Something like:@mremond Does this look sensible to you?
Remaining TODO items: