Oh boy.
The ejabberd BOSH module is difficult to understand. First there is the issue of ejabberd itself, which is difficult to figure out; then there is the interface between BOSH and the rest of the system.
After trying to go through the source code I have come to the conclusion that the best way of trying to understand what is going on is to trace through the code as it is processing a BOSH session rather than trying to figure out what is going on by contemplating the code "at rest."
So I'm going to trace through the the logfile... explaining as I go as an exercise to help myself understand what is going on. The logfile is created for a session with Pidgin to the regular ejabberd/BOSH module.
And to think: this is how I've chosen to spend my vacation time...
1: =INFO REPORT==== 2011-12-18 07:38:29 ===
2: I(<0.367.0>:ejabberd_listener:281) : (#Port<0.414>) Accepted connection {{192,168,1,101},62839} -> {{192,168,1,11},5280}
OK, this tells me that ejabberd got a connection on the BOSH port.
3:
4: =INFO REPORT==== 2011-12-18 07:38:29 ===
5: D(<0.367.0>:ejabberd_socket:61) : start module: ejabberd_http, SockMod: gen_tcp, Socket = #Port<0.414>
6:
7: =INFO REPORT==== 2011-12-18 07:38:29 ===
8: D(<0.367.0>:ejabberd_socket:104) : raw
9:
10: =INFO REPORT==== 2011-12-18 07:38:29 ===
11: D(<0.371.0>:ejabberd_http:89) : init
12:
13: =INFO REPORT==== 2011-12-18 07:38:29 ===
14: D(<0.371.0>:ejabberd_http:142) : S: [{["web"],mod_http_fileserver},
15: {["captcha"],ejabberd_captcha},
16: {["admin"],ejabberd_web_admin},
17: {["http-bind"],mod_http_bind},
18: {["http-poll"],ejabberd_http_poll}]
19:
20:
21: =INFO REPORT==== 2011-12-18 07:38:29 ===
22: I(<0.371.0>:ejabberd_http:144) : started: {gen_tcp,#Port<0.414>}
23:
24: =INFO REPORT==== 2011-12-18 07:38:29 ===
25: D(<0.371.0>:ejabberd_http:261) : (#Port<0.414>) http query: 'POST' /http-bind/
Blah, blah, blah...then the bit that mentions that this is an HTTP POST to /http-bind/, which is the URL for the BOSH module.
26:
27:
28: =INFO REPORT==== 2011-12-18 07:38:29 ===
29: D(<0.371.0>:ejabberd_http:431) : client data: "<body content='text/xml; charset=utf-8' secure='true' to='ubuntu2' xml:lang='en' xmpp:version='1.0' ver='1.6' xmlns:xmpp='urn:xmpp:xbosh' rid='2230163495637982' wait='60' hold='1' xmlns='http://jabber.org/protocol/httpbind'/>"
30:
31:
32: =INFO REPORT==== 2011-12-18 07:38:29 ===
33: D(<0.371.0>:ejabberd_http:330) : ["http-bind"] matches ["http-bind"]
34:
Blah, blah, blah...
35: =INFO REPORT==== 2011-12-18 07:38:29 ===
36: D(<0.371.0>:mod_http_bind:69) : Incoming data: <body content='text/xml; charset=utf-8' secure='true' to='ubuntu2' xml:lang='en' xmpp:version='1.0' ver='1.6' xmlns:xmpp='urn:xmpp:xbosh' rid='2230163495637982' wait='60' hold='1' xmlns='http://jabber.org/protocol/httpbind'/>
This is the first entry into the BOSH code. The source looks like this:
66 process([], #request{method = 'POST',
67 data = Data,
68 ip = IP}) ->
69 ?DEBUG("Incoming data: ~s", [Data]),
70 ejabberd_http_bind:process_request(Data, IP);
So something is calling the process method in mod_http_bind, which, in turn, calls ejabberd_http_bind:process_request. Here is the corresponding logging statement:
37:
38: =INFO REPORT==== 2011-12-18 07:38:29 ===
39: D(<0.371.0>:ejabberd_http_bind:181) : process_request with data: "<body content='text/xml; charset=utf-8' secure='true' to='ubuntu2' xml:lang='en' xmpp:version='1.0' ver='1.6' xmlns:xmpp='urn:xmpp:xbosh' rid='2230163495637982' wait='60' hold='1' xmlns='http://jabber.org/protocol/httpbind'/>"
40:
Here is the code for the corresponding section:
180 %% Entry point for data coming from client through ejabberd HTTP server:
181 process_request(Data, IP) ->
182 ?DEBUG("process_request with data: ~p", [Data]),
183 Opts1 = ejabberd_c2s_config:get_c2s_limits(),
184 Opts = [{xml_socket, true} | Opts1],
185 MaxStanzaSize =
186 case lists:keysearch(max_stanza_size, 1, Opts) of
187 {value, {_, Size}} -> Size;
188 _ -> infinity
189 end,
190 PayloadSize = iolist_size(Data),
191 case catch parse_request(Data, PayloadSize, MaxStanzaSize) of
OK, so this gets some options and then parses the request.
192 %% No existing session:
193 {ok, {"", Rid, Attrs, Payload}} ->
The result of the parse comes up with an XML stanza that does not have a SID (session ID). According to the BOSH spec this means that the client is trying to establish a new session. Also according to the spec, a new session should have a "to" attribute in order to tell the BOSH server who the request is for.
194 case xml:get_attr_s("to",Attrs) of
195 "" ->
196 ?DEBUG("Session not created (Improper addressing)", []),
197 {200, ?HEADER, "<body type='terminate' "
198 "condition='improper-addressing' "
199 "xmlns='" ++ ?NS_HTTP_BIND ++ "'/>"};
200 XmppDomain ->
201 %% create new session
202 Sid = sha:sha(term_to_binary({now(), make_ref()})),
203 case start(XmppDomain, Sid, "", IP) of
204 {error, _} ->
205 {200, ?HEADER, "<body type='terminate' "
206 "condition='internal-server-error' "
207 "xmlns='" ++ ?NS_HTTP_BIND ++ "'>BOSH module not started</body>"};
A bunch of code to handle the error case where the request does not have a to attribute.
208 {ok, Pid} ->
209 handle_session_start(
210 Pid, XmppDomain, Sid, Rid, Attrs,
211 Payload, PayloadSize, IP)
212 end
213 end;
This looks for "to" attribute in the stanza. Only a start session or a start stream can have this attribute, so if it is missing, it means we have an error case.
What does not make sense is why the value of the to attribute would be an erlang process ID. Line 208 suggests that the return value from xml:get_attr_s is {ok, Pid}. I would expect the value of the attribute to be the chat domain that the new session wants to go to.
Trying to make sense of all this, it seems like a reasonable thing to do would be to create something called mod_megaphone that starts listening to a specified port on startup. When a new message is received, the module should call the equivalent of ejabberd_http_bind:process_request.
At this point I really need to make a decision: should I try to modify the behavior of the BOSH module or strike out on my own. Changing the behavior of BOSH means that it will not work while I am trying to get the megaphone module to work. This is a bad thing (tm) because it means that I will be unable to compare the existing BOSH behavior against the new megaphone behavior.
Striking out on my own has the problem that all the module names will be wrong and nothing will work.
More decisions, decisions.
No comments:
Post a Comment