Oncaphillis::Ligh++pd

Ligh++pd README

I guess this has to be spelled ligh-plus-plus-p.d.

Ligh++pd: The C++ binding for lighttd.

Table of Contents


Concepts

I came across lighttpd when I worked on my minimal webserver for my home office. It turned out this little beast was just what I was looking for. Fast and frugal in its resource hunger.

Of course I couldn't resist poking inside of the code and it turned out that most of the parts are well structured and easy to follow, which unfortunately isn't very common for many software projects.

There are a couple of concepts which are already kind of C++'ish and which I think could be worth to be covered with C++ objects:

  • Lighties modules are Object like

    Lighties concept of a module circles around a structure which holds pointers to functions. Loading a module means loading a dynamic library and calling a function which initializes the struct with pointers to functions. Afterwords when lighty does its deeds it calls these functions during certain events within request processing and by this gives the module a chance to interfere within the process of request handling and response collection. Pointers to functions which do not exist are simply left NULL. Two of these methods, init and cleaunup are called at the start and the end of a modules livecycle. They are allowed to allocate and deallocate private data and are very close to being and objects CTor/DTor.

  • buffer lighies concept of string.

    Lighty manages strings in structs called buffer which have their closest counterpart in in the std::string of the STL. In contrast to the standard C char * which are assumed to be null terminated the buffer keeps track of the current string length which should substantially boost the performance when dealing with a lot of strings.

  • chunkqueue is lighties std::streambuf.

    The chunkqueue represents lighties data storage for stuff coming into the server and waiting to be returned to the client. This is done with a linked list of chunks of memory which might be in memory or on disk. There are functions available to append data to the list or read stuff starting from the start.

    So this is quite close to the std::basic_streambuf object.

  • array lighties idea of a hash/array.

    The array struct is kind of a hybrid between std::hash_map and std::vector. So you might access data via key or index. In contrast to their counterparts from the STL the array might hold data of heterogeneous type.


Installation

Currently ligh++pd has only been tested with lighttpd-1.4.18 and lighttpd-1.4.19 under Fedora 8 with g++ 4.3.1. It does not yet compile against the upcoming lighttpd-1.5.x version. Any reports on successful installation on other platforms (or the lag thereof) will be highly appreciated.

Prerequisites

  1. You need the source of lighttpd-1.4.18 or lighttpd-1.4.19 which can be downloaded from lighties homepage.

  2. The automake / autoconf and libtools system to configure lighttpd and ligh++pd.

  3. A decent C++ compiler

  4. The boost library which can be found at www.boost.org

  5. You have to compile and install lighttpd under a prefix of your choice.

  6. The ligh++pd source code. Currently I provide a tar-ball under http://www.oncaphillis.net/ligh++pd-0.2.1.tar.gz

Configuration

After successfully configuring and installing lighttpd unpack the ligh++pd tarball which should create a ligh++pd directory in your current location. Change into the directory and create ./configure configure for ligh++pd by invocing the ./autogen.sh script.

After this you should be ready to configure ligh++pd via:

    ./configure --prefix={desired prefix dir} --libdir={libdir-of-lighttpd} LGHTTPD_SRC={path-to-lighttpd-source}

Where {desired-prefix-dir} represents the prefix (root directory) for the ligh++pd installations. The second arg {libdir-if-lighttpd}has to be set to the library directory path as used by the lighttpd installation e.g. /usr/lib/lighttpd/. If you did not explicitly define this path when compiling lighttpd you might omit this option since it defaults to {prefix}/lib. This is the place where lighttpd expects modules to be located and therefor if this path isn't properly set up you might be able to create beautiful C++ modules but nevertheless lighttpd isn't able to find them.

ligh++pd also needs an additional parameter LIGHTTPD_SRC. Please set this to the base directory of the lighttpd source file, the directory to find ./src in. E.g. $HOME/lighttpd-14.18/ if you happened to unpack lighttpd-1.4.19.tar.gz in your home directory. This is *NOT* expected to be $HOME/lighttpd-1.14.19/src/.

Now everything should be set up properly and you may invoke.

    make && make install

WHAT DID YOU GET ?

So what happened ?

Beside a test module of questionable value ligh++pd has installed a bundle of header and lib files which might help you on your way to a C++ based lighttpd plugin.

All of these wonders are described in the nexr section.


Writing a Lighttpd C++ module

As described befor lighties module are kind of classes with pure virtual methods. When loading the module the "vtable" if the module is expected to be initialized. Pure virtual methods are represented by NULL pointers.

A special function callesd init serves as the constructor and gets a chance to allocate ans initialize private data held by the module. Anotherone called cleanup represents a DTor.

Doing it C++-Style

Looking at this design it seems quite natural to map a module to an object. The plugins private data becomes the objects data and the "plugin function pointers" could be seen as a vtable on virtual functions. Unfortunately it isn't possible to create an object with pure virtual methods in C++.

The Almost Empty Module Class

It is tempting to design a module base class with all the member functions being virtual, provide a set of static functions which call the appropriate members with an initialized this pointer and all is set. If one likes to specify another behavior for a given method (s|)he might simply overwrite the virtual method of the base class.

This approach however has the drawback that every pointer of the plugin struct will be initialized to a valid function even though it might do nothing useful but waisting precious CPU cycles. Therefor I decided to follow a different approach with a lot of template magic.

A plugin module almost comes with no prerequisites except the definition of a

    static const char *name;

member. This is the name under which the associated plugin will be registered to lighttpd. It furthermore has to provide a standard constructor (taking no arguments), If you like to be lighttpd 1.5.0 conformant (which isn't implemented up until now) you also should provide a constructor taking a lighttpd::Server object reference as an argument. No further assumption are made about a lighttpd C++-module. in ligh++pd/module however you will find a nifty module class template which inherits a couple of convenient enums from a lighttpd::module_base class.

The lighttpd::module_base is just a bunch of typedefs and enums defining internal types and values. Most of these are imported from the corresponding plugin.h header files.

The abstract module class is just an almost empty templated class which takes a const char* template argument defining the name of the module under which it will be known to lighttpd.

    class module_base {
    public:
      enum result_t {
        result_unset         = ::HANDLER_UNSET,
        result_go_on         = ::HANDLER_GO_ON,
        result_finished      = ::HANDLER_FINISHED,
        result_comeback      = ::HANDLER_COMEBACK,
        result_wait_for_event= ::HANDLER_WAIT_FOR_EVENT,
        result_error         = ::HANDLER_ERROR,
        result_wait_for_fd   = ::HANDLER_WAIT_FOR_FD
      };
    };

    template<const char * NAME>
    class module : public module_base {
    public:
      static const char * name;
    };

So the declaration of a valid module object might look as simple as:

    extern const char HelloWorldName[] = "mod_hello";

    class mod_hello : public lighttpd::module<HelloWorldName> {
    };

In order to make this module known to lighttpd you need a small piece of old shool C-Code. Assume You'd like to implement the module mod_hello you'd do the following:

    #include <ligh++pd/plugin.h>
    #include <ligh++pd/module.h>

    extern "C" {
    extern const char HelloWorldName[] = "mod_hello";

    class mod_hello : public lighttpd::module<HelloWorldName> {
    };
  
    int mod_hello_plugin_init( ::plugin *p) {
    return lighttpd::init_plugin<mod_hello>(p);
    }
    }

The "extern C" stuff is needed since lighttpd expects an old fashioned C-binding for the mod_hello_plugin_init function.

This code has to end up in a library called mod_hello.so (on linux) within the lighttpd module directory.

VoilĂ  -- your first working lighttpd plugin. It doesn't do anything though. The lighttpd::init_plugin<...>() function takes care about the initialization of lighties "vtable" creating valid pointers only for those functions for which a corresponding method in the module class exists.

Beefing It Up

So of course this is quite boring. We want our module to do something valuable. For this to happen we have to define module member functions which come in two flavors. server functions which take a reference to an object of class Server as an argument, and connection functions which need a reference to a lighttpd::Server and and lighttpd::Connection object as arguments. All of these are expected to return lighttpd::module_base::result_t.

So you might write:

    const char my_module_name[]="my_module";
   

    class my_module : module<my_module_name> {
    private:
      typedef lighttpd::module<my_module_name> super;
    public:
      typedef super::result_t result_t;
      result_t handle_uri_clean(lighttpd::Server &s,lighttpd::Connection &c) {
        s.out() << "<html>"     << std::endl
                << "<body>"     << std::endl
                << "Hello Word" << std::endl
                << "</body>"    << std::endl
                << </html>"     << std::endl;
        return result_finished;
      }
    };

And you get a nice lighttpd which joyfully greats everyone coming around (and doing nothing else).

Mothers little helpe: Connection, Server, String

lighttpd defines a hand full of importand data structures from which a plugin might draw informations about the current connection, request state etc.. the server is currently in. The two most importand are server and connection. A normal C-API plugin gets pointers to these structs handed over in order to perform its task. So what are these lighttpd::Server and lighttpd::Connection thingies ?

First of all they behave like transparent pointers to the server and connection pointers they are deduced from. So if one needs to do the old style coding (s|)he might use the Connection reference just as if it would be a connection pointer. On the other hand they are spiced up with member functions intendet to make live a little bit easier.

Up until now the most importand extensions are probably a couple of std:iostream which allow easy access to the current request/ the current response and the servers logging.

e.g:

    module_base::result_t handle_uri_clean(Server &srv,Connection &con) {

     std::string r;

     // Read in data from the current request

    con.in() >> r;
    
    // Dump out the date back to the client
    // Spiced up with some html

    con.out() << "<html><head><title>My report</title></head><body>" << r << "</body></html>" << std::endl;

    // Dump out the same stuff to the logging stream
    //

    srv.log() << "<html>Hello World</html>;
     return module_base::ok;
    }

All of this functionality is based on niftyLib::IO::device_stream and relatives.

In addition to this I've enveloped buffer into lighttpd::String which can be used where ever a buffer * is needed. The lighttpd::String::to_string method also returns a std::string. It also allows output to std::ostream. So thinks like:

     ...

     lighttpd::Connection con;
     lighttpd::Server     src;

     srv.log() << lighttpd::String(c->uri.scheme) << std::endl;

become possible.

If you want to learn more, you might want to have a look at my Completely useless Mod::Hello module


LICENSE

ligh++pd is made available under the BSD license.

Copyright (c) 2008, Sebastian Kloska, Oncaphillis

All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  • Neither the name of the "Oncaphillis" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY Sebastian Kloska "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Sebastian Kloska BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


Generated by  doxygen
© 2008; Dr. Sebastian Kloska ( Oncaphillis )
Powered by: [?]