Request/Response

The HyperText Transfer Protocol (HTTP) is a great example of a simple, effective request/response model used on the Web to request, and respond to requests for HTML and other content. Example 2-4 shows a typical HTTP request/response pairing.

Example 2-4. A typical HTTP request/reponse

GET /home.html HTTP/1.0

HTTP/1.1 200 OK
Date: Mon, 11 Jun 2001 13:43:13 GMT
Server: Apache/1.3.12 (Unix) mod_perl/1.24
Last-Modified: Fri, 09 Jun 2000 13:47:56 GMT
ETag: "8a69-6a-3940f58c"
Accept-Ranges: bytes
Content-Length: 306
Connection: close
Content-Type: text/html

<html>
<head>
...

It shows us the request verb GET, and the specification of what to retrieve (the /home.html document), and it shows us what is returned in response.

While we've already seen that the Jabber protocol is asynchronous in nature, there is a similar request/response model available too, which tends to the synchronous (i.e., first request, then response) although unlike HTTP, the response is not guaranteed to immediately follow the request. Other unrelated Jabber fragments may be received in the stream in the time between request and response. This request/response model is called Info/Query, or IQ for short; the Jabber element employed in the implementation of this model is <iq/>.

This IQ model has many uses in providing Jabber's basic IM features. Figure 2-1 shows the element traffic in a typical IQ-based conversation.

Figure 2-1. Element traffic in an IQ-based conversation


         +----------+              +----------+
         | entity 1 | IQ-get  ---> | entity 2 |
         |          |              |          |
         |          | <---  IQ-res |          |
         |          |              |          |
         |          |              |          |
         |          |              |          |
         |          | IQ-set  ---> |          |
         |          |              |          |
         |          | <---  IQ-res |          |
         |          |              |          |
         +----------+              +----------+

Example 2-5, Example 2-6 and Example 2-7 show typical uses of the <iq/> element to effect the IQ request/response model.

Example 2-5. A simple client version query via the IQ model

SEND: <iq type='get' from='jim@company-a.com/home'
          to='mark@company-a.com/Laptop-7' id='v1'>
        <query xmlns='jabber:iq:version'/>
      </iq>

RECV: <iq type='result' to='jim@company-a.com/home'
          from='mark@company-a.com/Laptop-7' id='v1'>
        <query xmlns='jabber:iq:version'>
          <name>Jabber Instant Messenger</name>
          <version>1.7.0.14</version>
          <os>95 4.10</os>
        </query>
      </iq>

In Example 2-5 we see that the equivalent of HTTP's GET verb is the type='get' attribute of the <iq/> element, and that the equivalent of what to get (/home.html) is our namespace specification in the <query/> tag.

Example 2-6 shows a two-stage request/response model. First, a query is made, using the <iq/> 'get' type with an id of 'reg1', to discover which fields are required for user registration. Then, the actual user registration attempt is made with the <iq/> 'set' (id 'reg2'). In this second stage, we can see a link with HTTP's POST verb - whereby data is sent along with the request. [1]

Example 2-6. A multiple-phase IQ to register a user

SEND: <iq type='get' id='reg1'>
        <query xmlns='jabber:iq:register'/>
      </iq>

RECV: <iq type='result' id='reg1'>
        <query xmlns='jabber:iq:register'>
          <instructions>Choose a name and pass to register.</instructions>
          <username/>
          <password/>
        </query>
      </iq>

SEND: <iq type='set' id='reg2'>
        <query xmlns='jabber:iq:register'>
          <username>helen</username>
          <password>tr0y</password>
        </query>
      </iq>

RECV: <iq type='result' id='reg2'/>

While HTTP is primarily a client-server protocol (although these words have less and less meaning in a P2P (peer-to-peer) environment where each participating entity can be both client and server), Example 2-5 shows that the IQ model can be used in a client-to-client (or server-to-server, or server-to-client, for that matter) context as well.

Example 2-7 shows how an IQ-based conversation can extend beyond the get ... result or set ... result model. It shows how the IQ model can be used to deliver results of a search request in stages.

Example 2-7. Staggered response from IQ-based search request

The search request:

SEND: <iq type='set' to='ldap.company-a.com' id='801'>
        <query xmlns='jabber:iq:search'>
          <group>Support</group>
        </query>
      </iq>

The first part of the response—the first record found:

RECV: <iq type='set' from='ldap.company-a.com'
          to='jim@company-a.com/home' id='801'>
        <query xmlns='jabber:iq:search'>
          <name>John Aston</name>
          <phone>4701</phone>
          <group>Support</group>
        </query>
      </iq>

The second record found:

RECV: <iq type='set' from='ldap.company-a.com'
          to='jim@company-a.com/home' id='801'>
        <query xmlns='jabber:iq:search'>
          <name>Katie Smith</name>
          <phone>4711</phone>
          <group>Support</group>
        </query>
      </iq>

The last record found:

RECV: <iq type='set' from='ldap.company-a.com'
          to='jim@company-a.com/home' id='801'>
        <query xmlns='jabber:iq:search'>
          <name>Jeremy Taylor</name>
          <phone>4702</phone>
          <group>Support</group>
        </query>
      </iq>

No more records have been found and no more results are sent; instead, an "end marker", signified by the type='result' attribute, is sent.

RECV: <iq type='result' from='ldap.company-a.com'
          to='jim@company-a.com/home' id='801'>
        <query xmlns='jabber:iq:search'/>
      </iq>

Because of the inherently asynchronous nature of Jabber, we need some way of matching the responses received with the original request. After all, if we were to fire off two requests almost simultaneously to ask for the local time on our server and on a client in New Zealand:

SEND: <iq type='get' from='jim@company-a.com/home' 
          to='piers@company-a.com/emacs'>
        <query xmlns='jabber:iq:time'/>
      <iq/>

SEND: <iq type='get' from='jim@company-a.com/home' 
          to='company-a.com'>
        <query xmlns='jabber:iq:time'/>
      <iq/>

we might find that the responses come back out of sequence (in our view), because of the comparative network distances over which the two conversations must travel. First this:

RECV: <iq type='result' to='jim@company-a.com/home'
          from='company-a.com'>
        <query xmlns='jabber:iq:time'/>
          <utc>20010611T17:59:13</utc>
          <tz>CET</tz>
          <display>Mon Jun 11 19:59:13 2001</display>
        </query>
      </iq>

and then this:

RECV: <iq type='result' to='jim@company-a.com/home'
          from='piers@company-a.com/emacs'>
        <query xmlns='jabber:iq:time'/>
          <utc>20010611T17:59:14</utc>
          <tz>UTC+1200</tz>
          <display>Tue Jun 12 06:59:14 2001</display>
        </query>
      </iq>

That's the reason for the id attribute in the examples earlier in this section. Between a request and a response, any id attribute value in the <iq/> element is preserved, allowing the requester to match up <iq/> request / response pairs. Using the id attribute, we can piece together related fragments of individual conversations, which in this case were a pair of client time queries.

Notes

[1]

Okay, data can also be sent with an HTTP GET request, but let's just not go there, for the sake of the comparison. ;-)