Posts in category ‘Framework’.
Wikipp is converted to new CppCMS 1.x.x and running on main site!
Hello all,
Wikipp is now running on top of alpha version of CppCMS 1.x.x. It is one important step to release of first beta version of new generation of CppCMS.
Update me if you have any issues.
CppCMS 1.x.x updates
After big internal changes I explain some critical architectural updates that CppCMS 1.x.x did.
In few words:
- Removed
cppcms_boost
library. Only several essential tools, not visible to API are still taken fromcppcms_boost
which compiled statically into cppcms library. - Booster library was introduced. It is a library with boost-like interfaces that do not depend on actual boost. Some of the code is taken from boost library, some are wrappers of good C libraries and some totally new code I had written with ABI compatibility in mind.
Following my previous post feel free to update your working copies.
Rationale
CppCMS 1.x.x would provide backward compatible API and ABI and thus it can't relate on Boost library in it's public API. Only possible way to provide Boost API to users is actually wrapping it.
CppCMS 1.x.x introduces asynchronous even loop to web development - something that is very critical for Comet programming. This loop was based on Boost.Asio. But unfortunately it had very big limitations and writing a good wrapper Boost.Asio was unfeasible.
So a small "Boost_er_" library replaced required functionality from boost partially reimplementing, partially wrapping C libraries and partially borrowing the code from Boost itself.
Booster has following components:
- Smart pointers:
shared_ptr
,intrusive_ptr
from Boost and small pointers for pimpl design:hold_ptr
,copy_ptr
andclone_ptr
(my own). - Function - similar to
std::tr1::function
andboost::function
- small class important for callbacks implementation (my own implementation). - Regular expressions - wrapper of PCRE library with Boost.Regex like API. It provides only
regex
,match_result
,regex_search
andregex_match
. - System:
error_code
,error_category
andsystem_error
(my own). - Thread: Boost like pthreads wrapper.
- Locale - full Boost.Locale library (my own).
- And the most important AIO library - library inspired by Asio.
The AIO library it is central CppCMS event loop that has Asio like API - proactor design, callback interface, etc.
However unlike ASIO it uses very few template classes, it is prefork-friendly (unlike ASIO)
Booster.Aio interface would allow Comet application to receive various asynchronous notifications from any source that can deliver such notifications over sockets.
CppCMS 0.0.5 (Stable) Released
Hello All,
Version 0.0.5 of CppCMS framework (stable brunch) had been released. So far:
Security Bugs:
- Workaround of CgiCC bug that may cause process shutdown in case of huge POST requests - DoS.
- Fixed generation of session ids that could create session ids with very low entropy -- guessable SID.
Bugs:
- Fixed: incorrect update of "exposed" session values, when renewing session life time.
- Fixed: bug that prevent using FastCGI/SCGI over TCP.
- Fixed: build fault when using
--boost-suffix
configure option - Fixed: incorrect generation of status headers in case of errors.
- Fixed: build issues with
gcc-4.4.1
- Fixed: various issues with for Intel Compiler support.
Improvements:
- Removed all unnecessary classes from views -- compilation time speedup up to 3.2 times (of views)
CppCMS 1.x.x moves to CMake
No, I don't think that CMake is better then autotools. In fact I still think that
CMake is total "crap". It has terrible cache policy, it has broken configuration_file
support. It is crappy documentation and many broken configuration tools like CheckTypeSizeOf... and much more.
But it supports MSVC (that I may think supporting in future) and has a better Windows support... So I announce that next version of CppCMS would use CMake (and it already uses in re-factoring branch).
Autotools build system is no longer supported and will be removed from the CppCMS 1.x.x branch, because I do not really like supporting two build systems.
I hope CppCMS users would understand this terrible move.
CppCMS meets Comet
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:
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; }
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:
- Asynchronous long running application that handles all request is created.
- It manages all outstanding request and uses them for server side push.
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.