Pizzja internals
This page tries to explain precisely how Pizzja behaves: receiving/forwarding stanzas, etc.
Vocabulary
- A local account, or subscriber, is a JID that subscribed to the component, providing it with the JID and password of the account the component should connect to. In the presentation, this corresponds to random@blah.
- A local resource, or controller, is a connected resource of a subscriber.
- A remote account, or controlled account is the JID that the component will connect as. This is your "important" JID, the ones your friends know. In the presentation, this corresponds to regular@server.
Resource mapping
| Local controller | Remote client connection |
| foo@local/A foo@local/A/x foo@local/A/y | bar@remote/A |
| foo@local/B/w | bar@remote/B |
How Pizzja works
Pizzja stores a list of JID that have subscribed to it and provided it with the JID and password of the account it should control. When Pizzja sees a subscriber online:
- It looks up the "remote" JID it should connect as.
- It determines what resource it should use, based on the subscriber's resource.
- It connects as JID/resource, unless it is already (i.e. there is already a controller online matching that resource).
When a subscriber goes offline:
- It determines what JID and resource the subscriber was controlling.
- It disconnects from that resource, unless there are still other online controllers for it.
The important thing to understand is that Pizzja acts both as a component and as a client. More precisely, it maintains one connection as a component (i.e. server-side) once, and several client connections: one for each account/resource it is asked to control. It will communicate with the controllers through its connection as a component, and with each remote account through the corresponding client connection.
Stanza forwarding
Pizzja's main job is to forward stanzas:
- From a controller onto the right client connection. This is the multiplexing.
- From a client connection back to the right controller(s). This is the demultiplexing, and yes, sometimes it is somewhat tricky to pick the right controller.
We will suppose, as an example, that this instance of Pizzja is bound to the server example, and that it runs on the domain name pizzja.example.
When Pizzja receives an outgoing stanza to forward along a client connection, it looks at the JID in the "to" attribute, which will be something like xxx@…/resource. The part before the '@' is the escaped JID of the intended recipient. All Pizzja has to do is thus to unescape that JID and obtain, say, yyy (JIDs often contain '@', but not always), set yyy/resource as the new "to" attribute of the stanza, and send it along the right client connection.
When Pizzja receives an incoming stanza to forward to one or several controllers, it looks at the JID in the "from" attribute, which will be something like yyy/resource (again, the JID may contain a '@', or not). It escapes it as xxx and suffixes it with '@', its own domain name, and the resource of the originating stanza. The resulting JID, xxx@…/resource, is then set as the new "from" attribute and the stanza is sent to the right controller(s).
Presence
Since there can be several controllers for a given client connection, Pizzja does not reflect transparently the presence information of the controllers (because they can be different from each other). This means that any controller that wants to change the presence status on the remote account will have to send the explicit <presence/> stanza it wishes Pizzja to forward along its client connection.
IQ management
Unlike other stanzas, incoming IQs received on bar@foreign/A are not broadcasted to all controlling resources matching foo@local/A/*. Here are 2 scenarios that illustrate why.
- 1st scenario
- If resource A/x of foo@local sends an IQ request, it will receive the corresponding IQ result or error, which is good, but resource A/y will too, without having asked anything.
- 2nd scenario
- Reciprocally, a foreign contact sends an IQ request to bar@foreign/A and all the resources matching foo@local/A/* receive it, they will all have to send a reply in order to comply with XMPP, so the requester will end up with several replies from bar@foreign/A.
Scenario 1 is the easier to solve: Pizzja can keep track of which local resource sent an IQ request, and forward the result back to that resource only.
As for scenario 2, Pizzja will have to make a choice about which resource will receive the request. It could apply the rules described in RFC 3921, section 11.1, to determine the 'most available' resource, but only between the local resources matching foo@local/A/* (so it's not possible to just forward the request to foo@local and let the server apply the rules itself: we will have to reimplement the rules described in the RFC, only with a smaller set of candidates).
Alternatively, for scenario 2, Pizzja could send the request to all matching resources, then intercept the replies and forward only one of them. So the problem remains: which one? Possible answers are:
- The first one
- The first one that doesn't have a type="error" attribute
- The first one that doesn't reply with a not-implemented error.
- Those from a predefined resource name
- ...?
A possible problem with this last approach is that clients might rely on the fact that their IQ result/error will be taken into account by the recipient, be it the server or a peer, so it might not be desirable to silently discard those.
