Pushing events to the browser via Ajax
I mentioned previously that I was exploring pushing events to HTML clients using Yaws and Erlang. I started off using Ajax, changed my mind and went to using a hidden IFRAME and now I've gone back to Ajax again.
Keeping a persistent connection open turns out to be a bit of a problem with browsers that want to limit the number of connections available. But I have some example code working and am making it available now in case someone wants to play with it. The code is availble in html_rpc.tar.gz and requires Erlang and Yaws.
The simple demo shows a single web page and some example commands that you can run from the Erlang shell to send alert messages to the client. I have a partially written messenger application using the same code (just a different Ajax handler) but it's not quite complete enough to make available yet.
To run this simple demo, start Yaws serving the pages in the archive. Make sure you have compiled the html_rpc.erl file with something like the following from the Erlang shell:
You'll also need to start the processes in that file:
Once that's done load the test1.yaws file in the browser. It'll display a page with instructions showing want to run at the Erlang shell to demonstrate things.
The implementation is actually pretty simple. A page that wants to receive messages needs to include the 'html_rpc.js' file using the SCRIPT HTML tag. This provides a function to use Ajax to call call a server routine, process a message, then recall that routine. The server routine is 'long lived'. That is, it doesn't return until it has a message from the server to deliver to the client. It sends 'no-op' messages every now and then to keep the connection alive.
When the server has a message to send the Ajax request is completed. The client processes it then re-establishes the connection with the server, where things start again.
The Yaws handler to manage the connection is pretty simple:
The 'start_ajax_handler' function does all the work. Just pass a 'handler' to handle messages specific for your application. In this case I have an alert message. The handler returns a string that is 'evaled' on the client to become javascript, and that javascript object is passed to a handler in the HTML page. In this example I'm making my message object functions that will be called by my handler in the HTML:
This code from the client uses 'html_rpc_receive' to establish the Ajax connection. The URL it uses to manage the connection is the Yaws code seen above. I pass a unique page identifier along with it so the server knows which client to send the events to. In this code I've hard coded '1234' but in reality I generate a large random name. 'html_rpc_receive' also takes a handler to process the received messages (the evaled string returned from the Erlang code). As I mentioned before I'm returning a function so I just call it as a function.
From the server I can now run code like:
This will immediately display an alert on the client with the text 'Hello World'. You'd want to write handlers that do real stuff though like display messages, animation, etc.
It's quite easy to use but could be easier. I'll be looking at how to improve things and put some more examples up in future posts.
Categories: erlang, yaws, ajax
Keeping a persistent connection open turns out to be a bit of a problem with browsers that want to limit the number of connections available. But I have some example code working and am making it available now in case someone wants to play with it. The code is availble in html_rpc.tar.gz and requires Erlang and Yaws.
The simple demo shows a single web page and some example commands that you can run from the Erlang shell to send alert messages to the client. I have a partially written messenger application using the same code (just a different Ajax handler) but it's not quite complete enough to make available yet.
To run this simple demo, start Yaws serving the pages in the archive. Make sure you have compiled the html_rpc.erl file with something like the following from the Erlang shell:
1> compile:file("www/html_rpc/html_rpc.erl").
{ok,html_rpc}You'll also need to start the processes in that file:
2> html_rpc:start().
true
Once that's done load the test1.yaws file in the browser. It'll display a page with instructions showing want to run at the Erlang shell to demonstrate things.
The implementation is actually pretty simple. A page that wants to receive messages needs to include the 'html_rpc.js' file using the SCRIPT HTML tag. This provides a function to use Ajax to call call a server routine, process a message, then recall that routine. The server routine is 'long lived'. That is, it doesn't return until it has a message from the server to deliver to the client. It sends 'no-op' messages every now and then to keep the connection alive.
When the server has a message to send the Ajax request is completed. The client processes it then re-establishes the connection with the server, where things start again.
The Yaws handler to manage the connection is pretty simple:
<erl>
out(A) ->
{ok,Page} = queryvar(A, "page"),
html_rpc:start_ajax_handler(Page, fun(A,B,C) -> handler(A,B,C) end).
handler(Yaws_Pid, Page, Message) ->
case Message of
{alert, Value} ->
"function() { alert('" ++ Value ++ "'); }"
end.
</erl>
The 'start_ajax_handler' function does all the work. Just pass a 'handler' to handle messages specific for your application. In this case I have an alert message. The handler returns a string that is 'evaled' on the client to become javascript, and that javascript object is passed to a handler in the HTML page. In this example I'm making my message object functions that will be called by my handler in the HTML:
function process_message(message) {
message()
}
html_rpc_receive("ajax1.yaws?page=1234", process_message);This code from the client uses 'html_rpc_receive' to establish the Ajax connection. The URL it uses to manage the connection is the Yaws code seen above. I pass a unique page identifier along with it so the server knows which client to send the events to. In this code I've hard coded '1234' but in reality I generate a large random name. 'html_rpc_receive' also takes a handler to process the received messages (the evaled string returned from the Erlang code). As I mentioned before I'm returning a function so I just call it as a function.
From the server I can now run code like:
5> html_rpc:send("1234", {alert, "Hello World"}).
{alert,"Hello World"}This will immediately display an alert on the client with the text 'Hello World'. You'd want to write handlers that do real stuff though like display messages, animation, etc.
It's quite easy to use but could be easier. I'll be looking at how to improve things and put some more examples up in future posts.
Categories: erlang, yaws, ajax

2 Comments:
Chris,
How did you solve the problem of queuing events? I have enough events firing that there is a good chance that an event will fire while the XMLHTTPRequest is being sent back to the browser. I'm thinking of a queue for each browser or setting up a global queue with sequential event id's so each browser can tell if an event has fired while it was in round trip. I know this post was old, but I'm wondering if you have any thoughts or have come across any resources that have tried particular solutions.
Thanks
Mark, I have a process on the server for each connected browser component.
That processes blocks receiving messages from the queue and sends them to the browser, so it uses the processes mailbox to do the queuing basically.
In my Factor implementation I have something similar, with a queue for each connected browser.
I send all items in the queue in one HTTP request to save a bit of overhead. While the request is operating other items can be queued and those are sent on the next request
Does that make sense?
Post a Comment
<< Home