2012-01-16

The Basics

While researching Push methods that would allow me to push data from a server to a client, I learned about Server Sent Events and decided to go about writing a bit of code to test the technology.

Why?

For a current project with a web based UI, I have an AJAX request update the UI every 2 seconds with data requested from the application. Most of the requested data doesn't change very often, but the data that does change, changes fast enough that 2 seconds causes a lot of latency in UI updates.

The project currently uses libsoup for creating a web server. Unfortunately, I couldn't get my current libsoup code to work the way I needed it to, so for this test, I wrote a fairly simple/limited webserver using GIO sockets.

It isn't a very proper webserver, but it is a decent example of coding in vala with sockets, and it does a fine job of showing off Server Sent Events.

Enter the Vala

using GLib;
//what port should this serve on?
const uint16 PORT 7777;
//when a request is made, handle the socket connection
bool connection_handler(SocketConnection connection) {
  string first_line ="";
  size_t size 0;
  string request "";
  //get data input and output strings for the connection
  DataInputStream dis new DataInputStream(connection.input_stream);
  DataOutputStream dos new DataOutputStream(connection.output_stream);  
  //read the first line from the input stream
  try {
    first_line dis.read_lineout size );
    request get_requestfirst_line );
  catch (Error e) {
    stderr.printf(e.message+"\n");
  }
  //do stuff based on the request
  switchrequest ) {
    case "/" :
      try {
        // create the html and javascript that will call /sse
        string html "<html>
          <head>
            <title>Soup Server Sent Event</title>
            <script type='text/javascript'>
              function init() {
                var info_div document.getElementById('info_div');
                var source new EventSource('/sse');
                source.onmessage function (event) {
                  info_div.innerHTML event.data;
                };
              }
            </script>
          </head>
          <body onload='init();'>
            <div id='info_div'></div>
          </body>
        </html>";
        dos.put_string("HTTP/1.1 200 OK\n");
        dos.put_string("Server: ValaSocket\n");
        dos.put_string("Content-Type: text/html\n");
        dos.put_string("Content-Length: %d\n".printf(html.length));
        dos.put_string("\n");//this is the end of the return headers
        dos.put_string(html);
      catch(Error e) {
        stderr.printf(e.message+"\n");
      }
      break;
    case "/sse" :
      try {
        dos.put_string("HTTP/1.1 200 OK\n");
        dos.put_string("Server: ValaSocket\n");
        dos.put_string("Content-Type: text/event-stream\n");
        dos.put_string("\n");
      catch(Error e) {
        stderr.printf(e.message+"\n");
      }
      string info;
      int count 0;
      bool looping true;
      while(looping) {
        count++;
        if (count 10 ) {
          count 1;
        }
        info @"data: hello world $count\n\n";
        try {
          dos.put_string(info);
          //sleep for $count half seconds
          Thread.usleep(count*500000);
        }catch(Error e) {
          //is the pipe broken?
          looping false;
        }
      }
      break;
    default:
      //404
      try {
        dos.put_string("HTTP/1.1 404\n");
        dos.put_string("Server: ValaSocket\n");
        dos.put_string("Content-Type: text/plain\n");
        dos.put_string("\n");
        dos.put_string("File Not Found");
      catch(Error e) {
        stderr.printf(e.message+"\n");
      }
      break;
  }
  return false;
}
// get the 'requested path' portion of a line
string get_request(string line) {
  string[] parts line.split(" ");
  return parts[1];


public static void main() {
  //we need a GLib mainloop to keep this app running
  MainLoop loop new MainLoop();
  //make the threaded socket service with hella possible threads
  ThreadedSocketService tss new ThreadedSocketService(150);
  //create an IPV4 InetAddress bound to no specific IP address
  InetAddress ia new InetAddress.any(SocketFamily.IPV4);
  //create a socket address based on the netadress and set the port
  InetSocketAddress isa new InetSocketAddress(iaPORT);
  //try to add the addess to the ThreadedSocketService
  try {
    tss.add_address(isaSocketType.STREAMSocketProtocol.TCPnullnull);
  catch(Error e) {
    stderr.printf(e.message+"\n");
    return;
  }
  //connect the 'run' signal that is emitted when there is a connection
  tss.run.connectconnection_handler );
  //start listening 
  stdout.printf(@"Serving on port $PORT\n");
  tss.start();
  //run the main loop
  loop.run();
}

Save the code as "server.vala" and compile with:

valac --pkg gio-2.0 server.vala

run the 'server' and point your browser to http://localhost:7777

If you don't see anything in your browser, there are a few possible reasons.

  1. Your browser is old and needs to be updated
  2. For some reason, you are using Internet Explorer (ouch! why do you hate the internet?)

What's Happening?

When the browser requests http://localhost:7777, a very basic webpage is returned, and the webpage contains a small bit of javascript that creates a new EventSource bound to http://localhost:7777/sse and updates a div on the page with date pushed to the EventSource.

Now I just need to strip the old libsoup code out of my project and add a nice socket based web server of my own creation. [enter evil laugh here] MUAHAHAHA

Comments
Name:
not required
Email:
not required (will not be displayed)
Website:
not required (will link your name to your site)
Comment:
required
Please do not post HTML code or bbcode unless you want it to show up as code in your post. (or if you are a blog spammer, in which case, you probably aren't reading this anyway).
Prove you are human by solving a math problem! I'm sorry, but due to an increase of blog spam, I've had to implement a CAPTCHA.
Problem:
4 plus 8
Answer:
required
subscribe
 
2019
2016
2015
2014
2013
2012
2011
2010
December
November
October
September
August
July
June
May
April
March
February
January
2009
December
November
October
September
August
July
June
May
April
March
February
January
2008