On Tuesday 24 of March 2009 11:07:12 Ratko Rudič wrote:
> I'll be replying to this email with a sorce code,
>
> Ratko

Here it is,,,

Ratko
/**
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * memcache apache module
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * This is a memcache module for apache web server.
 * It connects to memcache server and tries to fetch HTML content from it.
 *
 * Memcache key is based on the hostname and URI after it.
 *
 * If HTML is found, it is returned with an OK status.
 * If HTML is not found, module is skipped and apache does whatever it does next.
 *
 * Memcache module is enabled in the <Directory> or <Location> directive with:
 * 	SetHandler memcache_handler
 *
 * Memcache servers are added with (IP, port pairs):
 * 	MemCacheServerAdd 10.0.0.212 11211
 *
 * You can set debugging with:
 * 	MemCacheDebug INT
 *   where INT is one of the:
 *	0 - no debugging info
 *	1 - debugging is written into error log file
 *	2 - debugging is written to browser and error log file
 *	3 - debugging is written at the bottom of HTML
 *
 * To build this module use:
 *	# apxs2 -a -c -l memcached -l mhash -i mod_memcache.c
 *
 * You will also need libmemcache from:
 *	http://tangent.org/552/libmemcached.html
 *
 * and mhash library for generating md5() hash keys.
 *
 *
 * BSD License
 *
 * POP-ONLINE
 * Ratko Rudic
 * ratko.ru...@pop-tv.si
 *
 * Version: 1 at 2008-12-04
 *	Initial source code.
 *
 */

#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "ap_config.h"
#include <string.h>

#include <libmemcached/memcached.h>
#include "common.h"

#include <mhash.h>


#define DEBUG_NONE	0
#define DEBUG_LOG	1
#define DEBUG_COMMENT	2
#define DEBUG_HTML	3

#define URL_MAX_LEN	512


/**
 * Forward-declarations
 */
extern module AP_MODULE_DECLARE_DATA memcache_module;


/**
 * Structure that holds pointer to memcache object and log level.
 * It is initialized at the server start.
 */
typedef struct {
	memcached_st *memc;
	int debug;
} memcache_config;


/**
 * Returns md5() of the given key.
 */
static void memcache_key( const char *key, char *md5_ascii )
{
        MHASH td;
        int bsize;
	unsigned char *md5;
	char c[10];
	int i;
        
        bsize = mhash_get_block_size( MHASH_MD5 );
        
        td = mhash_init( MHASH_MD5 );
        mhash( td, key, strlen(key) );
        
        md5 = mhash_end( td );

        for (i = 0; i < bsize; i++) {
		snprintf(c, sizeof(c), "%02x", md5[i] );
		strncat( md5_ascii, c, sizeof(md5_ascii) - 1 );
	}

        return;
}


/**
 * Adds a new server to the pool of memcached servers.
 * It is called if user adds MemCacheServerAdd parameter
 */
static const char *memcache_server_add(cmd_parms *parms, void *mconfig, const char *arg1, const char *arg2)
{
        memcache_config *cfg = ap_get_module_config(parms->server->module_config, &memcache_module);
        memcached_server_add( cfg->memc, (char *)arg1, atoi(arg2) );

        return NULL;
}


/**
 * Returns basename of the URI
 */
char *memcache_basename( const char *name )
{
	const char *base;

	for ( base = name; *name; name++ )
	{
		if ( (*name) == '/' )
			base = name + 1;
	}

	return (char *)base;
}


/**
 * Function adds "/" to the end of given URL, if URL is a
 *	folder (eg: "http://24ur.com/my-dir";)
 */
void memcache_url_setup( request_rec *r, char *url )
{
	char *bname;
	char last_char = '\0';

	if ( strlen(url) == 0 )
		return;

	bname = memcache_basename( url );

	last_char = url[ (int)strlen(url) - 1 ];

	if ( (last_char != '/') && (strstr(bname,".")==NULL) )
		strcat( url, "/index.html" );

	if ( last_char == '/' )
		strcat( url, "index.html" );
}


/**
 * Returns current URL
 */
void memcache_url_get( request_rec *r, char *url )
{
        strncpy(url, r->hostname, URL_MAX_LEN);
        strncat(url, r->uri, URL_MAX_LEN - strlen(url) );
}


/**
 * Main function
 * Tries to find HTML for the current URL in the memcache.
 *
 * Returns HTML to user and stops additional serving if URL is found in memcache. 
 */
static int memcache_handler(request_rec *r)
{
        // Return immediatelly if handler is not defined        
        if (strcmp(r->handler, "memcache_handler")) {
                return DECLINED;
        }

        memcache_config *cfg = ap_get_module_config(r->server->module_config, &memcache_module);

        char url[URL_MAX_LEN];
        char key[128];
        uint32_t flags;
        size_t html_length = 0;
        memcached_return error;
        char *html;

        if (!r->header_only) {
                // Write some debug info for current page
                if ( cfg->debug == DEBUG_HTML ) {
                        r->content_type = "text/html";
            
                        ap_rprintf(r, "<b>Debug from mod_memcache.c - disable it  with MemCacheDebug 0, or delete MemCacheDebug statement from apache conf.</b><hr>\n");
                        ap_rprintf(r, "Hostname: %s <br>", r->hostname);
                        ap_rprintf(r, "Unparsed URI: %s <br>", r->unparsed_uri);
                        ap_rprintf(r, "URI: %s <br>", r->uri);
                        ap_rprintf(r, "Filename: %s <br>", r->filename);
                        ap_rprintf(r, "Path info: %s <br><hr>", r->path_info);
                }
        }

        // Get current URL and set it up properly
	memcache_url_get( r, url );
	memcache_url_setup( r, url );

        // Create key that we'll be looking for in memcache
        // key is a md5() of the hostname + URI
        memset(key, 0, sizeof(key)); 
        memcache_key( url, key );

        if ( cfg->debug == DEBUG_HTML )
                ap_rprintf(r, "Looking for URL: '%s' that has KEY: %s <br>", url, key);
            
        // Get HTML for the current key
        html = memcached_get( cfg->memc, (char *)key, strlen((char *)key), &html_length, &flags, &error );

        if ( cfg->debug == DEBUG_HTML )
                ap_rprintf(r, "Memcache response: (%i) %s - %d in length<br>", error, memcached_strerror(cfg->memc, error), (html == NULL ? 0 : (int)html_length)  );
            
        if ( cfg->debug >= DEBUG_LOG )
                fprintf(stderr,"MEMCACHE: key: %s for url: %s, got: (%i) %s, response is %d in length\n", (char *)key, url, error, memcached_strerror(cfg->memc, error), (html == NULL ? 0 : (int)html_length) );
            
        // If HTML is found in memcache, Write it to user
        if ( html != NULL ) {
                r->content_type = "text/html";
                ap_rprintf(r, "%s", html);

                if ( cfg->debug >= DEBUG_COMMENT )
                        ap_rprintf(r, "\n<!-- %s | %s | %d -->", memcached_strerror(cfg->memc, error), key, (int)strlen(html) );

                return OK;
        }

        if ( cfg->debug == DEBUG_HTML )
                return OK;
        
        return DECLINED;
}


/**
 * Creates config struct
 */
static void *memcache_create_config( apr_pool_t *p )
{
	memcache_config *conf;

	conf = (memcache_config *) apr_pcalloc(p, sizeof(memcache_config));

	conf->memc = memcached_create(NULL);
	conf->debug = 0;

	return (void *)conf;
}


/**
 * Per server config
 */
static void *memcache_create_server_config(apr_pool_t *p, server_rec *s)
{
        return memcache_create_config( p );
}


/**
 * Per directory config
 */
static void *memcache_create_dir_config(apr_pool_t *p, char *dir)
{
        return memcache_create_config( p );
}


/**
 * Initialize debug level
 */
static const char *memcache_server_debug_set( cmd_parms *parms, void *mconfig, const char *arg1 )
{
        int debug_level = atoi( arg1 );
        memcache_config *cfg = ap_get_module_config(parms->server->module_config, &memcache_module);
        
        if ( (debug_level < 0) || (debug_level > 3) ) {
                fprintf(stderr,"memcache: Wrong debug level (%s). must be 0, 1, 2 or 3. Setting it to 0 - no debug.\n",arg1);
                debug_level = 0;
        }

        fprintf(stderr,"memcache: Setting debug level to: %d.\n",debug_level);
        cfg->debug = debug_level;

        return NULL;
}


/**
 * Apache register hook so that our code will get called.
 */
static void memcache_register_hooks(apr_pool_t *p)
{
    ap_hook_handler(memcache_handler, NULL, NULL, APR_HOOK_FIRST);
}


static const command_rec memcache_cmds[] =
{
  AP_INIT_TAKE2(
    "MemCacheServerAdd", memcache_server_add, NULL, ACCESS_CONF,
    "IP & PORT of memcached server (ex: MemCacheServerAdd 192.168.1.100 11211 ) !"
  ),
  AP_INIT_TAKE1(
    "MemCacheDebug", memcache_server_debug_set, NULL, ACCESS_CONF,
    "Show DEBUG: 0: none, 1: log, 2: on top of html"
  ),
  {NULL}
};


/* Dispatch list for API hooks */
module AP_MODULE_DECLARE_DATA memcache_module = {
    STANDARD20_MODULE_STUFF, 
    memcache_create_dir_config,			/* create per-dir    config structures */
    NULL,                  			/* merge  per-dir    config structures */
    memcache_create_server_config,		/* create per-server config structures */
    NULL,                  /* merge  per-server config structures */
    memcache_cmds,         /* table of config file commands       */
    memcache_register_hooks  /* register hooks                      */
};

Reply via email to