Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / Apache

Apache 2.x Modules In C++ (Part 2)

5.00/5 (3 votes)
12 Nov 2012CPOL2 min read 15.4K  
Part 2 - Stepping into the C++ world

Introduction

In the previous article (Part 1) we concentrated on getting setup to build our Apache custom module. In this article we will look at adding our first C++ class that steps us from the world of Apache C into the C++ domain.

Our first class, CApplication

So let's begin where we left off from the previous article. Below is the code for the foo_handler() function, let's recap and take a closer look:-

C++
EXTERN_C_FUNC
int foo_handler( request_rec* inpRequest )
{
    int nReturnVal = DECLINED;
	
    if ( inpRequest->handler != NULL && strcmp( inpRequest->handler, "foo" ) == 0 )
    {
        ap_rputs( "Hello World from FOO", inpRequest );
        nReturnVal = OK;
    }

    return nReturnVal;
}

The first thing to notice here is that the handler is invoked with a pointer to the Apache API request record request_rec* inpRequest. In turn that pointer is used to get some data (the handler name, "foo") and used in output (within ap_rputs() function). Now lets introduce our first class, CApplication. Create the following two files in you Netbeans project:

  • Header Files > Application > CApplication.hpp
  • Source Files > Application > CApplication.cpp

Now in these files here's the code:

CApplication.hpp

C++
#ifndef CAPPLICATION_HPP
#define	CAPPLICATION_HPP

#include "mod_foo.hpp"

class CApplication
{
private:

    request_rec*    m_pRequestRec;
	
public:
	
    CApplication( request_rec* inpRequestRec ) :
        m_pRequestRec( inpRequestRec )
    {}
	
    int RunHandler();
};

#endif	/* CAPPLICATION_HPP */

CApplication.cpp

C++
#include "CApplication.hpp"

int
CApplication::RunHandler()
{
    int nReturnVal = DECLINED;
	
    if ( inpRequest->handler != NULL && strcmp( inpRequest->handler, "foo" ) == 0 )
    {
        ap_rputs( "Hello World from FOO", inpRequest );
        nReturnVal = OK;
    }

    return nReturnVal;
}

As you can see the core functionality has been moved from foo_handler() to CApplication::RunHandler(). Now all we have to do is go back to mod_foo.cpp and change it to create an instance of our application and call it's CApplication::RunHandler() method thus (note, I have added comments to describe what each section does):

C++
#include "mod_foo.hpp"
#include "CApplication.hpp"

/* Custom definition to hold any configuration data we may need.
   At this stage we just use it to keep a copy of the CApplication
   object pointer. Later we will add more when we need specific custom
   configuration information. */
EXTERN_C_BLOCK_BEGIN
typedef struct
{
    void* vpCApplication;
} 
FOOCONFIG_t;
EXTERN_C_BLOCK_END

/* Forward reference to our custom function to save the FOOCONFIG_t* 
   configuration pointer with Apache. */
EXTERN_C_FUNC 
void foo_register_config_ptr( request_rec* inpRequest, FOOCONFIG_t* inpFooConfig );

/* Forward reference to our custom function to get the FOOCONFIG_t* 
   configuration pointer when we need it. */
EXTERN_C_FUNC 
FOOCONFIG_t* foo_get_config_ptr( request_rec* inpRequest );

/* Custom function to ensure our CApplication get's deleted at the
   end of the request cycle. */
EXTERN_C_FUNC
apr_status_t foo_delete_capplication_object( void* inPtr )
{
    if ( inPtr )
        delete ( CApplication* )inPtr;
	
    return OK;
}

/* Our custom handler (content generator) 
   */
EXTERN_C_FUNC
int foo_handler( request_rec* inpRequest )
{
    /* Create an instance of our application. */
    CApplication* pApp = new CApplication( inpRequest );
	
    if ( pApp == NULL )
	    return HTTP_SERVICE_UNAVAILABLE;
		    
    /* Register a C function to delete pApp
       at the end of the request cycle. */
    apr_pool_cleanup_register( 
        inpRequest->pool, 
        ( void* )pApp, 
        foo_delete_capplication_object, 
        apr_pool_cleanup_null 
    );
		
    /* Reserve a temporary memory block from the
       request pool to store data between hooks. */
    FOOCONFIG_t* pFooConfig = 
        ( FOOCONFIG_t* ) apr_palloc( 
            inpRequest->pool, sizeof( FOOCONFIG_t ) );
		
    /* Remember our application pointer for future calls. */
    pFooConfig->vpCApplication = ( void* )pApp;
		
    /* Register our config data structure for our module. */
    foo_register_config_ptr( inpRequest, pFooConfig );
		
    /* Run our application handler. */
    return pApp->RunHandler();
}

/* Apache callback to register our hooks.
   */
EXTERN_C_FUNC
void foo_hooks( apr_pool_t* inpPool )
{
    ap_hook_handler( foo_handler, NULL, NULL, APR_HOOK_MIDDLE );
}

/* Our standard module definition.
   */
EXTERN_C_BLOCK_BEGIN
module AP_MODULE_DECLARE_DATA foo_module =
{
    STANDARD20_MODULE_STUFF,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    foo_hooks
};
EXTERN_C_BLOCK_END

/* Custom function to register our FOOCONFIG_t* pointer with Apache
   for retrieval later as required. */
EXTERN_C_FUNC
void foo_register_capplication_ptr( request_rec* inpRequest, FOOCONFIG_t* inpPtr )
{
    ap_set_module_config( inpRequest->request_config, &foo_module, ( void* )inpPtr );
}

/* Custom function to retrieve our FOOCONFIG_t* pointer previously
   registered with Apache on this request cycle. */
EXTERN_C_FUNC
FOOCONFIG_t* foo_get_capplication_ptr( request_rec* inpRequest )
{
    FOOCONFIG_t* pReturnValue = NULL;
	
    if ( inpRequest != NULL )
    {
        pReturnValue = 
            ( FOOCONFIG_t* )ap_get_module_config( 
                inpRequest->request_config, &foo_module );
    }
	
    return pReturnValue;
}

As you can see we have now made the transition from the C function foo_handler() to the C++ CApplication::RunHandler() and we are in the world of C++. In the handler we create a new instance of our application. To ensure our application does not leak memory a C clean-up function is registered with the request_rec->pool garbage collection system which is responsible for calling delete on our application pointer pApp at the end of the request cycle.

Finally we reserve a small amount of memory from the request_rec->pool to hold a pointer to our application configuration data structure which is then regsitered with the Apache API. Although not used at this stage in this article this allows us to recover the pointer to our application pApp in other Apache hooks after the handler is complete but before the request_rec->pool is destroyed. In later articles we will see how this recovered pointer is useful for handling Apache output filter hooks that run after the handler function has completed and returned.

Conclusion

We have seen that transferring control from an Apache C handler to a C++ object is fairly straight forward. In the next article we will look at the the beginnings of abstracting the Apache API into a more well defined OOPs orientated set of objects.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)