Home  /  RSS  /  RSS Comments  /  Project Site  /  Enter

CppCMS meets Comet

27/08/09, by artyom ; Posted in: Progress, Framework, Comet; 6 comments

One of the major requirements for framework refactoring was support of Comet. Now, with introduction of asynchronous request handling and persistent application servers it becomes reality.

Client Side

There is a HTML source of simple chat client, that uses Dojo toolkit. It does following:

  1. Submits new messages to the server application by posting form using XHR:

    function send_data() {
            var kw = {
                    url : "/chat/post",
                    form : "theform"
            };
            dojo.xhrPost(kw);
            dojo.byId("message").value="";
            return false;
    }
    
  2. Receives new messages from the server using long poll via XHR:

    var message_count = 0;
    function read_data() {
            dojo.xhrGet( {
                    url: "/chat/get/" + message_count,
                    timeout: 120000,
                    handleAs: "text",
                    load: function(response, ioArgs) {
                            dojo.byId("messages").innerHTML =
                                    response
                                    + '<br/>'
                                    + dojo.byId("messages").innerHTML;
                            message_count++;
                            read_data();
                            return response;
                    },
                    error: function(response,ioArgs) {
                            read_data();
                            return response;
                    }
    
            });
    }
    dojo.addOnLoad(read_data);
    

So, the client side is quite simple (however error handling should be quite better).

Server Side

First we create our long running asynchronous application, that receives two kinds for requests: "/post" – with new data, and "/get/NN" – receive message nuber NN, we assign these calls to two member functions post and get.

class chat : public cppcms::application {
public:
    chat(cppcms::service &srv) : cppcms::application(srv)
    {
        dispatcher().assign("^/post$",&chat::post,this);
        dispatcher().assign("^/get/(\\d+)$",&chat::get,this,1);
    }

Now, this class includes two data members:

private:
    std::vector<std::string> messages_;
    std::vector<cppcms::intrusive_ptr<cppcms::http::context> > waiters_;

The history of all chat messages – messages_ and all pending get requests that can’t be satisfied, because the message still not exists – waiters_

Each, "waiter" is actually pointer to request/response context that can be used for message transport.

Now, when new message arrives, post member function is called:

void post()
{
    if(request().request_method()=="POST") {
        if(request().post().find("message")!=request().post().end()) {
            messages_.push_back(request().post().find("message")->second);
            broadcast();
        }
    }
    release_context()->async_complete_response();
}

If the requested message was found, it is added to messages_ list and all waiters are notified using broadcast() member function.

At the end, the current request context is released and completed.

The broadcasting is done as following:

void broadcast()
{
    for(unsigned i=0;i<waiters_.size();i++) {
        waiters_[i]->response().set_plain_text_header();
        waiters_[i]->response().out() << messages_.back();
        waiters_[i]->async_complete_response();
        waiters_[i]=0;
    }
    waiters_.clear();
}

For each pending request the last message is written and the request closed. After that, all pending request are cleaned.

When get request arrives, it is handled by get(std::string no) member function, first of all we check if requested message exists, if so we just return it to user.

unsigned pos=atoi(no.c_str());
if(pos < messages_.size()) {
    response().set_plain_text_header();
    response().out()<<messages_[pos];
    release_context()->async_complete_response();
}

Otherwise, if the requested message is the last one, that does not exists, we add the request context to pending list waiters

else if(pos == messages_.size()) {
    waiters_.push_back(release_context());
}

If requested message it too late – probably client error, we just set status to "404 Not Found" and return the response.

else {
    response().status(404);
    release_context()->async_complete_response();
}

No, all we need to do is to add application to the main running loop under script name "/char" and start the service.

cppcms::service service(argc,argv);
cppcms::intrusive_ptr<chat> app=new chat(service);
service.applications_pool().mount(app,"/chat");
service.run();

Summary

So, the simple chat service was written with about 50 lines of C++ code and about same amount of JavaScript code.

I must admit, that it is too simplistic and not efficient, for example: if new client connects it receives all messages one by one and not as bulk (can be easily fixed), I do not handle timeouts and disconnects. But the general idea is quite clear:

This is actually a base for future development of tools like XML-RPC and JSON-RPC that allow client to call asynchronously server side objects, it can be used for implementation of any other Comet protocols.

Comments

Daniel Vallejos, at 07:38 04/09/09

Hello!

I'm start writing a qooxdoo server JSON-RPC that integrates in CppCms 0.0.4. I'm not expertize in web development…

You plan add JSON-RPC support in future?

http://qooxdoo.org/documentation/0.8/rpc_server_writer_guide

If you integrate qooxdo server RPC as add-on to your project, CppCms+qooxdo will support develop "GUI Like" web applications better than Wt.

Thanks you very much for your work.

I'll be back!

artyom, at 07:53 04/09/09

You plan add JSON-RPC support in future?

It would be the native type of RPC supported by CppCMS. Actually the next version of CppCMS (not 0.0.x series) uses JSON as its data format for communication and configuration.

JSON-RPC would be the major RPC for CppCMS.

I'm start writing a qooxdoo server JSON-RPC that integrates in CppCms 0.0.4. I'm not expertize in web development…

I would suggest to take a look on refactoring branch of framework project in SVN.

It supports new features like JSON and Comet that do not exist in CppCMS 0.0.x serices.

I still hand’t written JSON-RPC API but it is the top priority feature that would be developed very soon.

Daniel Vallejos, at 11:09 16/09/09

Hello!

I read your Coding Standards guide lines.

I want to suggest you two points (with low importance):

1) Smart Pointers: Deprecation of auto_ptr. http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=403

2) Rename cppcms::application::cout because cout means "console" out AFAIK. And if I want use std::cout and "using namespace std;"… Do you like for example "wout"? (web out).

Thank you!

Carry ON!

(sorry if this place is not adequate for this suggestions)

artyom, at 11:54 16/09/09

Hi,

I read your Coding Standards guide lines.

I would suggest to read the updated one for next CppCMS version:

http://art-blog.no-ip.info/wikipp/en/page/cppcms_1x_coding_standards

Smart Pointers: Deprecation of auto_ptr

First of all I do not agree that auto_ptr should be deprecated. When C++0x comes probably move_ptr and shared_ptr should be used more but… Until C++0x would be supported by all major compilers this project would release two or three major versions ;).

Today, in modern C++ it is the only smart pointer that has clear move semantics that is very useful for factory pattern.

In any case… I'm beginning to support backward compatible ABI, and this means that I can’t use any of boost::* classes including shared_ptr in API. Thus, auto_ptr is the only smart pointer I can use.

Rename cppcms::application::cout

It does not exist any more in CppCMS 1.x. Instead you call out() member function of http::response class. In any case, it is generally called by template rendering interface and not directly.

Daniel Vallejos, at 19:26 16/09/09

Hi!

In any case, it is generally called by > template rendering interface and not directly.

Yes. You are right!. But if I want to response for example a QOOXDOO-RPC (a variation of standard JSON-RPC) I have to use "out()" member function or another primitive method. Right?

Another question: Are you developing a new JSON parser/writer? If not, what library you are using?

Thanks!

artyom, at 21:43 16/09/09

I have to use "out()" member function or another primitive method. Right?

I think you whould not have to… Only create json-response.

Are you developing a new JSON parser/writer?

I had already written one. I didn’t want to use existing because I have strict requirements of ABI compatibility.

Take a look on this code: json.h, json.cpp

You can send a trackback to following url:

Add Comment:

Name:
E-Mail:
Site:

You can write your messages using Markdown syntax.

You must enable JavaScript in order to post comments.

Pages

Categories

Development

Powered By

3rd Party