📄 x9016.htm
字号:
><P
>However, we know from <A
HREF="c1223.htm"
>Chapter 4</A
> that there are other
entities that connect to Jabber to provide services. These entities are
called <I
CLASS="EMPHASIS"
>components</I
>. You can look upon components as being
philosophically less "transient" than their client-connected brethren;
and also closer to the Jabber server in terms of function and connection.</P
><P
>We know from <A
HREF="x1234.htm#JABTDG-CH-4-SECT-4.1.1"
>the section called <I
><B
CLASS="COMMAND"
>jabberd</B
> and Components</I
> in Chapter 4</A
> that there are
various ways to connect a component:
<I
CLASS="EMPHASIS"
>Library load</I
>, <I
CLASS="EMPHASIS"
>STDIO</I
>, and
<I
CLASS="EMPHASIS"
>TCP sockets</I
>. The first two dictate that the
component be located on the same host as the <B
CLASS="COMMAND"
>jabberd</B
>
backbone to which it connects.
<A
NAME="AEN9134"
HREF="#FTN.AEN9134"
>[3]</A
>
The <I
CLASS="EMPHASIS"
>TCP sockets</I
> connection type, a method using
a socket connection between the component and the <B
CLASS="COMMAND"
>jabberd</B
>
backbone, over which streamed XML documents are exchanged (in the same
way as they are exchanged in a client connection), allows us to run
components on any host, and connect them to a Jabber server running on
another host if we wish. Because of the connection flexibility, this approach
is in many ways the most desirable. But it's not just the flexibility;
because
it abstracts the component away from the Jabber server core libraries,
it leaves it up to us to decide how the component should be written.
All the component has to do to
get the Jabber server to cooperate is to establish the socket connection
as described in the component instance configuration, perform an
authenticating handshake, and correctly exchange XML stream headers.</P
><P
>Let's review how a TCP socket-based component connects. We'll base the
review on what we're actually going to have to do to get our RSS punter
up and running.</P
><P
>First, we have to tell the Jabber server that it is to expect an incoming
socket connection attempt, which it is to <I
CLASS="EMPHASIS"
>accept</I
>.
We do this by defining a component instance definition (or
"description"—see <A
HREF="x1581.htm#JABTDG-CH-4-SECT-4.2.1"
>the section called <I
>Component instances</I
> in Chapter 4</A
>) for
our component. We include this definition in the main Jabber server
configuration file, usually called <TT
CLASS="FILENAME"
>jabber.xml</TT
>.
<A
HREF="x9016.htm#JABTDG-CH-8-EX-8"
>Example 8-8</A
> shows a component instance definition
for our RSS punter mechanism, known as
<TT
CLASS="LITERAL"
>rss.qmacro.dyndns.org</TT
>.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="JABTDG-CH-8-EX-8"
></A
><P
><B
>Example 8-8. A component instance definition for our RSS punter mechanism</B
></P
><P
><PRE
CLASS="SCREEN"
><service id='rss.qmacro.dyndns.org'>
<accept>
<ip>localhost</ip>
<port>5999</port>
<secret>secret</secret>
</accept>
</service> </PRE
></P
></DIV
><P
>The name of the host on which
the main Jabber server is running is <TT
CLASS="LITERAL"
>qmacro.dyndns.org</TT
>;
it just so happens that our plan is to run the RSS punter component on
the same host. We give it a unique name
(<TT
CLASS="LITERAL"
><TT
CLASS="USERINPUT"
><B
>rss</B
></TT
>.qmacro.dyndns.org</TT
>)
to enable the <B
CLASS="COMMAND"
>jabberd</B
> backbone, or hub, to distinguish
it from other components and to be able to route elements to it.
An alternative way of writing the component instance definition is shown
in <A
HREF="x9016.htm#JABTDG-CH-8-EX-9"
>Example 8-9</A
>. The difference is simply in the
way we specify the name. In <A
HREF="x9016.htm#JABTDG-CH-8-EX-8"
>Example 8-8</A
> we
specified an <TT
CLASS="LITERAL"
>id</TT
> in the
<TT
CLASS="LITERAL"
><service/></TT
> tag with the value
"<TT
CLASS="LITERAL"
>rss.qmacro.dyndns.org</TT
>". In the absence of any
<TT
CLASS="LITERAL"
><host/></TT
> tag specification in the definition,
this <TT
CLASS="LITERAL"
>id</TT
> value is used by the <B
CLASS="COMMAND"
>jabberd</B
>
routing logic as the identification for the component when
determining where elements addressed with that destination should be sent.
In <A
HREF="x9016.htm#JABTDG-CH-8-EX-9"
>Example 8-9</A
>, we have an explicit
<TT
CLASS="LITERAL"
><host/></TT
> specification which will be used
instead, and we simply identify the service with an <TT
CLASS="LITERAL"
>id</TT
>
attribute value of "<TT
CLASS="LITERAL"
>rss</TT
>". In this latter case, it doesn't
really matter from an addressability point of view what we specify as
the value for the <TT
CLASS="LITERAL"
>id</TT
> attribute. </P
><DIV
CLASS="EXAMPLE"
><A
NAME="JABTDG-CH-8-EX-9"
></A
><P
><B
>Example 8-9. An alternative instance definition for our RSS punter mechanism</B
></P
><P
><PRE
CLASS="SCREEN"
><service id='rss'>
<host>rss.qmacro.dyndns.org</host>
<accept>
<ip>localhost</ip>
<port>5999</port>
<secret>secret</secret>
</accept>
</service> </PRE
></P
></DIV
><P
>The instance definition contains all the information the Jabber server
needs. We can tell from the
<TT
CLASS="LITERAL"
><accept/></TT
> tag that this definition
definition describes a <I
CLASS="EMPHASIS"
>TCP sockets</I
> connection.
The socket connection detail is held in the
<TT
CLASS="LITERAL"
><ip/></TT
> and
<TT
CLASS="LITERAL"
><port/></TT
> tags. In this case, as we're
going to run the RSS punter component on the same host as the Jabber
server itself, we might as well kill two related birds with one stone
by specifying <I
CLASS="EMPHASIS"
><TT
CLASS="LITERAL"
>localhost</TT
></I
> in
the <TT
CLASS="LITERAL"
><ip/></TT
> tag:
<A
NAME="AEN9181"
HREF="#FTN.AEN9181"
>[4]</A
></P
><P
></P
><DIV
CLASS="VARIABLELIST"
><DL
><DT
>Performance</DT
><DD
><P
>Connecting over the loopback device, as opposed to a real network
interface, will give us a slight performance boost.</P
></DD
><DT
>Security</DT
><DD
><P
>Accepting only on the loopback device is a simple security measure
that leaves one less port open to the world.</P
></DD
></DL
></DIV
><P
>The <TT
CLASS="LITERAL"
><secret/></TT
> tag holds the secret
that the connecting component must present in the authentication
handshake. </P
><P
>Now let's look at the component's view of things. It will need to establish
a socket connection to <TT
CLASS="LITERAL"
>127.0.0.1:5999</TT
>. Once that
connection has been established, <B
CLASS="COMMAND"
>jabberd</B
> will be
expecting it to announce itself by sending its XML document stream header.
<A
HREF="x9016.htm#JABTDG-CH-8-EX-10"
>Example 8-10</A
> shows a typical stream header that our
component will need to send.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="JABTDG-CH-8-EX-10"
></A
><P
><B
>Example 8-10. The RSS component's stream header</B
></P
><P
><PRE
CLASS="SCREEN"
>SEND: <?xml version='1.0'?>
<stream:stream xmlns='jabber:component:accept'
xmlns:stream='http://etherx.jabber.org/streams'
to='localhost'> </PRE
></P
></DIV
><P
>This matches the description of a Jabber XML stream header (also known as
a stream "root" as it's the root tag of the XML document) from
<A
HREF="x3837.htm"
>the section called <I
>XML Streams</I
> in Chapter 5</A
>. The namespace that is specified
as the one qualifying the <I
CLASS="EMPHASIS"
>content</I
> of the stream
is <TT
CLASS="LITERAL"
>jabber:component:accept</TT
>. This namespace "matches" the
component connection method (<I
CLASS="EMPHASIS"
>TCP sockets</I
>) and
the significant tag name in the component instance definition
(<I
CLASS="EMPHASIS"
><TT
CLASS="LITERAL"
><accept/></TT
></I
>).
<A
NAME="AEN9211"
HREF="#FTN.AEN9211"
>[5]</A
>
The value specified in the <TT
CLASS="LITERAL"
>to</TT
> attribute matches the
hostname specified in the configuration's <TT
CLASS="LITERAL"
><ip/></TT
>
tag.</P
><P
>After receiving a valid stream header, <B
CLASS="COMMAND"
>jabberd</B
> responds
with a similar root to head up it's own XML document stream going in the
opposite direction (from server to component). A typical response to the
header in <A
HREF="x9016.htm#JABTDG-CH-8-EX-10"
>Example 8-10</A
>, received from the server by
the component, is shown in <A
HREF="x9016.htm#JABTDG-CH-8-EX-11"
>Example 8-11</A
>.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="JABTDG-CH-8-EX-11"
></A
><P
><B
>Example 8-11. The server's stream header reply</B
></P
><P
><PRE
CLASS="SCREEN"
>RECV: <?xml version='1.0'?>
<stream:stream xmlns:stream='http://etherx.jabber.org/streams'
id='3B8E3540'
xmlns='jabber:component:accept'
from='rss'> </PRE
></P
></DIV
><P
>The stream header sent in response shows that the server is confirming
the component instance's identification as "<TT
CLASS="LITERAL"
>rss</TT
>".
This reflects whatever was specified in the
<TT
CLASS="LITERAL"
><service/></TT
> tag's
<TT
CLASS="LITERAL"
>id</TT
>
attribute of the component instance definition. Here, the value of the
<TT
CLASS="LITERAL"
>id</TT
> attribute was "<TT
CLASS="LITERAL"
>rss</TT
>" as in
<A
HREF="x9016.htm#JABTDG-CH-8-EX-9"
>Example 8-9</A
>.</P
><P
>It also contains an ID for the component instance itself:
<TT
CLASS="LITERAL"
>id='3B8E3540'</TT
> in our example. The significance of
this ID is as a random string shared between both connecting parties;
the value is used in the next stage of the connection attempt—the
authenticating handshake.</P
><P
><A
HREF="x6569.htm#JABTDG-CH-6-SECT-5.3.1.2"
>the section called <I
><I
CLASS="EMPHASIS"
>Digest</I
> Authentication Method</I
> in Chapter 6</A
> describes the digest authentication
method of authentication for clients connecting to the JSM. This method
uses a similar shared random string. On receipt of the server's stream
header, the component takes the ID and and prepends it onto the secret
that it must authenticate itself with. It then creates a NIST-SHA1
message digest (in a hexadecimal format) of that value:</P
><P
><PRE
CLASS="SCREEN"
><I
CLASS="EMPHASIS"
>SHA1_HEX(ID+SECRET)</I
></PRE
></P
><P
>Having created the digest, it sends it as the first XML fragment following
the root, in a <TT
CLASS="LITERAL"
><handshake/></TT
> element:</P
><P
><PRE
CLASS="SCREEN"
>SEND: <handshake id='1'>14d437033d7735f893d509c002194be1c69dc500</handshake> </PRE
></P
><P
>On receipt of this authentication request, <B
CLASS="COMMAND"
>jabberd</B
>
does the same thing: combines the ID value (after all, it knows what
it is as it was <B
CLASS="COMMAND"
>jabberd</B
> that generated the value)
with the value from the <TT
CLASS="LITERAL"
><secret/></TT
> tag
in the component instance definition, and performs the same digest
algorithm. If the digests match, the
component is deemed to have authenticated itself correctly, and is
send back an empty <TT
CLASS="LITERAL"
><handshake/></TT
> tag
in conformation:</P
><P
><PRE
CLASS="SCREEN"
><handshake/></PRE
></P
><P
>The component may commence sending (and being sent) elements. </P
><P
>If the component sends an invalid handshake value—the secret may
be wrong, or the digest may not have been calculated correctly—the
connection is closed: <B
CLASS="COMMAND"
>jabberd</B
> sends a stream error
and therewith ends the conversation:</P
><P
><PRE
CLASS="SCREEN"
>RECV: <stream:error>Invalid handshake</stream:error></PRE
></P
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="JABTDG-CH-8-SECT-3.1.2"
>Who gets punted what?</A
></H3
><P
>So, the definitions of the RSS sources are held within the script.
But there's no reference to who might want to receive new items
from which sources. We need a way for our component to accept requests,
from users, that say things like:</P
><P
>"I'd like to have pointers to new items from the Slashdot site punted
to me please"</P
><P
>or</P
><P
>"I'd <I
CLASS="EMPHASIS"
>also</I
> like pointers to new items from Jon
Udell's site please"</P
><P
>or even</P
><P
>"Whoa, information overflow! Stop all my feeds!". </P
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -