Home  /  RSS  /  RSS Comments  /  Enter

CppCMS 1.0.2 - Bug Fix Release

Tuesday, August 14, 2012, by artyom ; one comment

CppCMS 1.0.2 Released, it is a bug fix release.

Fixed Bugs:

Easy Comet: Server-Sent Events

Sunday, August 12, 2012, by artyom ; Posted in: FastCGI, Comet; 4 comments

HTML5 Comet Technologies

Today there are two major technologies for efficient implementation of Comet applications using HTML5:

The first ones - WS - provide full-duplex communication such that both client and server can send each other events without creating a new connection. The second ones - SSE - provide real time notification in a single direction, server to client.

It seems that WS is much more popular. Indeed, WS are much more powerful and their support is scheduled for an implementation in CppCMS. On the other hand, WS have very significant limitation: WS protocol is not HTTP. It may look like HTTP and its hand-shake is done at HTTP level, but afterwards, the communication is done using different protocol that is not compatible with HTTP.

As a result, there is no way to implement WS over existing web server API as FastCGI or SCGI. More than that, even if the application uses HTTP directly, not every web server would be able to proxy web sockets communication.

So, despite that WS is very promising technology, it is very hard to deploy it today in the real production environment.

On the other hand, SSE are implemented over plain HTTP without any changes. The protocol is very simple and supported by most browsers, Firefox, Opera, Safari, Chrome and expected to be supported by IE10.

There is no special changes required to underlying web server APIs, thus FastCGI or SCGI would work perfectly well. For example, SSE can be easily implemented using stable version of CppCMS without any special tools.

Additionally SSE support stream synchronization in case of disconnect. The fall-back to long-polling using XHR can be easily implemented.

An Example

‎Lets implement a simple page that receives stock price updates.

First, we create an EventSource object and then we attach a handler that would update the appropriate html filed upon notification from the server side:

function read_data() {
    var stream = new EventSource('/ticker');
    stream.onmessage = function(e){
        document.getElementById('price').innerHTML=e.data;
    };

    stream.onerror = function(e){
        console.log(e);
    };
}

read_data();

The code is very simple and trivial, disconnects are handled automatically and transparently. Now lets take a look on the server side that is little bit more challenging:

Upon request we prepare our content type as required and fetch the ID of the last known price that was sent to the client.

void main(std::string /*url*/)
{
    response().set_content_header("text/event-stream");
    response().set_header("Cache-Control", "no-cache");

    auto last_id = atoi(request().cgetenv("HTTP_LAST_EVENT_ID"));

After that we detach the HTTP context object from the application, such that we will be able to handle multiple connections simultaneously.

    auto context=release_context();

The idle connections would be stored in a special waiters_ set. We add a special callback that allows us to cleanup the clients that had disconnected:

    context->async_on_peer_reset([=](){
        this->waiters_.erase(context);
    });

Note, we use C++11 lambda expressions that make the code much more simple and clear.

Then we check if the last price id that is known to the client and if it differs we send the client an update asynchronously, otherwise we add the client to the waiting list:

    if(last_id != counter_) {
        async_send(context);
    }
    else
        waiters_.insert(context);
} 

The code that sends the request to the client is quite simple, we send the last price id - simple counter that would use us for synchronization in case of disconnect and send an actual data.

void async_send(booster::shared_ptr<cppcms::http::context> waiter)
{
    waiter->response().out() <<
        "id:" <<  counter_ <<"\n"
        "data:" << price_ << "\n"
        "\n";

Then we setup a completion callback, if the operation fails (client had disconnected), we just exit, and the context would be automatically destroyed.

    waiter->async_flush_output([=,counter_](cppcms::http::context::completion_type status){
        if(status!=0)
            return;

Otherwise we check if there were an updates since the data was sent, and if yes we send the latest price once again, otherwise we add the client to the waiting list.

        if(counter_ != this->counter_) {
            this->async_send(waiter);
        }
        else {
            this->waiters_.insert(waiter);
        }
    });

This completes our function.

}

Note: that our lambda expression captures the waiter variable and keeps it alive till the handler is executed.

Now the last and the simplest thing - updating the price. Upon price update, we increase the identification counter and notify all objects in the waiting list.

void update_price(double new_one)
{
    counter_++;
    price_ = new_one;
    for(auto waiter : waiters_) {
        async_send(waiter);
    }

Afterwards we clear the list - now the callback object owns the context and would destroy it in case of an error.

    waiters_.clear();
}

The full code of the sample including simple timer based random price generation can be found there.

Falling Back to Long Polling

Is very simple. We would use the same protocol, but when the event is ready we would close the connection. In order to let the server to distinguish between EventSource and XHR long polling we would add a special header like X-Event-Source-Simulate: Long-Polling

Then we would change a little our async_send function by adding the following lines:

    if(waiter->request().getenv("HTTP_X_EVENT_SOURCE_SIMULATE")=="Long-Polling") {
       waiter->async_complete_response();
      return;
    }

Such that our function would look like:

    waiter->response().out() <<
        "id:" <<  counter_ <<"\n"
        "data:" << price_ << "\n"
        "\n";
    if(waiter->request().getenv("HTTP_X_EVENT_SOURCE_SIMULATE")=="Long-Polling") {
       waiter->async_complete_response();
       return;
    }
    waiter->async_flush_output(...)
    ...

Of course XHR simulation would have to send and manage Last-Event-Id header and parse the response, but the server side would look almost identically.

Connecting to the web server

When it is coming to configuring a web server you should make sure that it does not buffer out-coming request and sends them immediately to the client:

Now once again Nginx shows us its problems:

However, FastCGI does not implement such an option! See this ticket

So don't even try to use Nginx with FastCGI for Server-Sent Events.

Once again, give yourself a favor, use lighttpd

Thanks

Special thanks to Barbu Paul - Gheorghe,that had brought to my attention such a powerful and useful HTML5 feature.

Slides From Lecture about CppCMS

Saturday, August 11, 2012, by artyom ; 0 comments

At August 3rd, I had given a lecture about CppCMS at the annual Israeli FOSS conference August Penguin.

The slides are now available for download:

http://cppcms.com/files/ap2012/

CppDB 0.3.1 Released

Saturday, June 23, 2012, by artyom ; Posted in: Storage; 0 comments

This is an important bugfix release.

It is recommended for all users to upgrade to latest version:

Critical Bugs fixed:

Full Changelog: http://cppcms.com/sql/cppdb_0.3.1/changelog.html
Downloads: https://sourceforge.net/projects/cppcms/files/cppdb/

How not to do Unicode...

Sunday, April 29, 2012, by artyom ; Posted in: Unicode and Localization; 8 comments

All started from a small problem, how to print Unicode text to the Windows Console with option redirect to a file.

Let's say we have a program Hello that prints few words in several languages to the screen..

#include <stdio.h>

int main()
{
    printf("Мир Peace Ειρήνη\n");
    return 0;   
}

The program above is trivial and works fine under Windows if current console codepage is set to UTF-8. Also this can be fixed from the program by calling SetConsolseOutputCP(CP_UTF8).

Now simple tweak... Instead of that standard C printf we would use standard C++ std::cout... It works fine for GCC. But under Visual C++ it prints squares...

If I try redirection test.exe >test.txt - I get perfectly fine UTF-8 text...

I had started researching the issue and found the post of one of the Windows Unicode Gurus Michael Kaplan's.

I've tried to run _setmode(_fileno(stdout), _O_U8TEXT) as recommended by the Microsoft's Unicode guru and... By program crashed on attempt to write to the output stream.

Keeping searching for an answer I've got to this bug report...

Short summary:

To the summary...

If you use Visual C++ you can't use UTF-8 to print text to std::cout.

If you still want to, please read this amazingly long article about how to make wcout and cout working, but it does not really give a simple solution - finally falling to redefinition of the stream buffers...

So please, if you design API or Operating System, do not use kind of "Wide" API... This is is the wrong way to do Unicode.

Which reminds me... Spread around:

http://www.utf8everywhere.org/

Related Posts: http://blog.cppcms.com/post/62

previous page

next page

Pages

Categories