HTTP reverse push in Ejabberd

Source: Internet
Author: User

The reverse push of HTTP is usually in the form of "long polling" or "long connection". The so-called "long polling" means that the client sends a request to the server, and the server discovers that no data needs to be sent to the client.

The reverse push of HTTP is usually in the form of "long polling" or "long connection".
The so-called "long polling" refers to the client sends a request to the server, the server found no data need to send to the client so hold not return in time, and so on when the data need to send to the client, to reply, and then close the connection, the client received a reply before sending a new HTTP request, So that the server can have a corresponding request for the reverse push of the message.
The "Long connection" is to increase the "keep-alive" property on the basis of long polling, the server does not reply after receiving the request, wait for the data to be sent to the client and then response, but does not close the connection. This allows the client to send HTTP requests again on the same connection after receiving the response of the server.

In the implementation of Ejabberd, the Bosh technology is used to accomplish the corresponding work, the specific definition can be referenced:

English: http://go.rritw.com/xmpp.org/extensions/xep-0124.html

English: http://go.rritw.com/wiki.jabbercn.org/XEP-0124

Approximate implementation principle: Ejabberd receives a client HTTP request will eventually create three processes for the client: Ejabberd_http, Ejabberd_http_bind, Ejabberd_c2s.

The EJABBERD_HTTP process continuously requests the client from the corresponding socket and forwards it to the corresponding ejabberd_http_bind process for processing, then synchronously waits for the result to be processed and returns the result to the client.

Init ()... receive_headers (state). Receive_headers (#state {Trail=trail} = State)Sockmod=State#state.sockmod, Socket=State#state.socket, Data= Sockmod:recv (Socket, 0, 300000),     CaseState#state.sockmod ofgen_tcp-newstate=Process_header (State, Data), CaseNewstate#state.end_of_request of                true-OK; _ -receive_headers (newstate)End; _ - CaseData of{OK, D}-parse_headers (State#state{trail= <<trail/binary, d/binary>>}); {error, _}-OKEnd    End. Process_header (State, Data)- CaseData of        ... {OK, Http_eoh}-            ... out=process_request (State2), Send_text (State2, out), CaseState2#state.request_keepalive of                true-... #state {sockmod=Sockmod, Socket=Socket, Request_handlers=State#state.request_handlers}; _ -#state {end_of_request=true, Request_handlers= State#state.request_handlers}

As can be seen from the code, when the Keep-alive property is not set, the process finishes processing an HTTP request and ends itself (long polling mode). When the Keep-alive property is set, the process continuously loops over the HTTP request and forwards the receive and response (Long connection mode).

The Ejabberd_http_bind process is responsible for holding the HTTP request, and for normal client requests, the Ejabberd_http_bind process forwards the request to the corresponding EJABBERD_C2S process for the actual business processing. For empty requests (for server reverse push data), Ejabberd_http_bind sets the timer, waits for the ejabberd_c2s process to respond to the actual request, or a message that needs to be pushed to the client.

handle_sync_event ({send_xml,packet},_from,statename, #state {http_receiver= undefined} = statedata)Output= [Packet |Statedata#state.output], Reply=OK, {reply, reply, statename, Statedata#state{output=Output}}; Handle_sync_event ({send_xml, Packet}, _from, StateName, Statedata)-Output= [Packet |Statedata#state.output], Cancel_timer (statedata#state.timer), Timer=Set_inactivity_timer (Statedata#state.pause, statedata#state.max_inactivity), HTTPR Eply={OK, Output}, Gen_fsm:reply (Statedata#state.http_receiver, httpreply), Cancel_timer (Statedata#state.wait_timer ), Rid=Statedata#state.rid, Reqlist= [#hbr {rid = Rid,key =Statedata#state.key, out=Output}|[El|| El <-statedata#state.req_list, El#hbr.rid/=Rid]], Reply=OK, {reply, reply, statename, Statedata#state{output=[], Http_receiver=Undefined, req_list=reqlist, Wait_timer=Undefined, timer=Timer}}; Handle_sync_event ({http_get,rid,wait,hold},from,statename, statedata)-%%Setup Timersend_receiver_reply (statedata#state.http_receiver, {OK, empty}), Cancel_timer (Statedata#state.wait_timer), T now=Tnow (),if( hold> 0) and(Statedata#state.output== []) and((Tnow-statedata#state.ctime< (wait*1000*1000)) and(Statedata#state.rid= = Rid) and(Statedata#state.input/= cancel) and(Statedata#state.pause= = 0)Waittimer= Erlang:start_timer (Wait * 1000), self (), []),%%Mr:not sure we should cancel the state timer here.Cancel_timer (Statedata#state.timer), {next_state,statename, Statedata#state{http_receiver=From , Wait_timer=Waittimer, Timer=undefined}}; (Statedata#state.input= = Cancel)Cancel_timer (statedata#state.timer), Timer=Set_inactivity_timer (Statedata#state.pause, statedata#state.max_inactivity), Reply={OK, cancel}, {reply, reply, statename, Statedata#state{input=queue:new (), Http_receiver=Undefined, Wait_timer=Undefined, timer=Timer}}; true-Cancel_timer (statedata#state.timer), Timer=Set_inactivity_timer (Statedata#state.pause, statedata#state.max_inactivity), Reply={OK, statedata#state.output},%%Save RequestReqlist = [#hbr {rid =Rid, Key=Statedata#state.key, out=Statedata#state.output}|[El|| El <-statedata#state.req_list, El#hbr.rid/=Rid]], {reply, reply, statename, Statedata#state{output=[], Http_receiver=Undefined, Wait_timer=Undefined, timer=Timer, Req_list=Reqlist}} End; Handle_info ({timeout, waittimer, _}, StateName, #state {wait_timer= Waittimer} = statedata)ifStatedata#state.http_receiver/= undefinedCancel_timer (statedata#state.timer), Timer=Set_inactivity_timer (Statedata#state.pause, statedata#state.max_inactivity), Gen_fsm:reply (Statedata#state.http_receiver, {OK, empty}), Rid=Statedata#state.rid, Reqlist= [#hbr {rid =Rid, Key=Statedata#state.key, out= []                           } |[El|| El <-statedata#state.req_list, El#hbr.rid/=Rid]], {next_state, statename, Statedata#state{http_receiver=Undefined, req_list=reqlist, Wait_timer=Undefined, timer=Timer}}; true-{next_state, statename, statedata}End;

The ejabberd_http process eventually calls the Ejabberd_http_bind Http_get method to get the response result of the request or the data that needs to be pushed, and the Ejabberd_http_bind process receives the request and handles it accordingly. For example, there is data to reply directly, or set the timer. When receiving the data pushed by the EJABBERD_C2S process, stop the timer and reply the data to the ejabberd_http process immediately, and reply to an empty message if the timer expires.
=============================
Ejabberd_c2s is the client-side session process, responsible for maintaining the client's online status, contact list, request processing, and so on.
============
When the server creates the Ejabberd_http_bind process, it generates a unique SID that identifies the process, which is stored in Mnesia with the ejabberd_http_bind process PID, and the server will also tell the client that the SID , subsequent requests from the client are also required to bring the SID. Http_bind finds a matching ejabberd_http_bind process based on SID from Mnesia when the request is received.

Process_request (Data, IP)    ...     Case CatchParse_request (Data, Payloadsize, Maxstanzasize) of        %%No Existing session:{OK, {"", Rid, Attrs, Payload}},            ... Sid=Sha:sha (Term_to_binary ({now (), Make_ref ()}), CaseStart (Xmppdomain, Sid, "", IP) of{OK, Pid}-Handle_session_start (Pid, Xmppdomain, Sid, Rid, Attrs,payload, Payloadsize, IP):             . End; {OK, {Sid, Rid, Attrs, Payload1}}-... handle_http_put (Sid, Rid, Attrs, Payload2, Payloadsize, Streamst Art, IP); Handle_session_start (Pid, Xmppdomain, Sid, Rid, Attrs, Payload, Payloadsize, IP)-... mnesia:dirty_write (#http_bind {ID=Sid, PID=Pid, to={xmppdomain,xmppversion}, hold=Hold , wait.=Wait, Process_delay.=Pdelay, Version=Version}), Handle_http_put (Sid, Rid, Attrs, Payload, Payloadsize,true, IP). Http_put (Sid, Rid, Attrs, Payload, Payloadsize, Streamstart, IP)- CaseMnesia:dirty_read ({http_bind, Sid}) of        [] -{error, not_exists}; [#http_bind {pid= Fsmref, Hold=hold, To={to, streamversion}}=sess]            ... {gen_fsm:sync_send_all_state_event (fsmref, #http_put {RID= Rid, Attrs =attrs, Payload=Payload, Payload_size= Payloadsize, hold =Hold , Stream= newstream, IP = IP}, 30000), Sess}End.
Ejabberd_http_bind and Ejabberd_c2s will remember each other's process PID, so that each client has its own unique ejabberd_http_bind and ejabberd_c2s process to do the corresponding request processing. "Turn from"  http://fanli7.net/a/bianchengyuyan/C__/20140213/469448.html

HTTP reverse push in Ejabberd

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.