You are viewing a plain text version of this content. The canonical link for it is here.
Posted to java-dev@axis.apache.org by di...@apache.org on 2008/07/22 06:35:46 UTC

svn commit: r678637 [31/46] - in /webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd: ./ autom4te.cache/ cygwin/ doc/ openwrt/ src/ tests/ tests/docroot/ tests/docroot/123/ tests/docroot/www/ tests/docroot/www/dummydir/ tests/docroot/www/expire/ ...

Added: webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_cgi.c
URL: http://svn.apache.org/viewvc/webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_cgi.c?rev=678637&view=auto
==============================================================================
--- webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_cgi.c (added)
+++ webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_cgi.c Mon Jul 21 21:35:35 2008
@@ -0,0 +1,1357 @@
+#include <sys/types.h>
+#ifdef __WIN32
+#include <winsock2.h>
+#else
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fdevent.h>
+#include <signal.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include <stdio.h>
+#include <fcntl.h>
+
+#include "server.h"
+#include "keyvalue.h"
+#include "log.h"
+#include "connections.h"
+#include "joblist.h"
+#include "http_chunk.h"
+
+#include "plugin.h"
+
+#ifdef HAVE_SYS_FILIO_H
+# include <sys/filio.h>
+#endif
+
+enum {EOL_UNSET, EOL_N, EOL_RN};
+
+typedef struct {
+	char **ptr;
+
+	size_t size;
+	size_t used;
+} char_array;
+
+typedef struct {
+	pid_t *ptr;
+	size_t used;
+	size_t size;
+} buffer_pid_t;
+
+typedef struct {
+	array *cgi;
+} plugin_config;
+
+typedef struct {
+	PLUGIN_DATA;
+	buffer_pid_t cgi_pid;
+
+	buffer *tmp_buf;
+	buffer *parse_response;
+
+	plugin_config **config_storage;
+
+	plugin_config conf;
+} plugin_data;
+
+typedef struct {
+	pid_t pid;
+	int fd;
+	int fde_ndx; /* index into the fd-event buffer */
+
+	connection *remote_conn;  /* dumb pointer */
+	plugin_data *plugin_data; /* dumb pointer */
+
+	buffer *response;
+	buffer *response_header;
+} handler_ctx;
+
+static handler_ctx * cgi_handler_ctx_init() {
+	handler_ctx *hctx = calloc(1, sizeof(*hctx));
+
+	assert(hctx);
+
+	hctx->response = buffer_init();
+	hctx->response_header = buffer_init();
+
+	return hctx;
+}
+
+static void cgi_handler_ctx_free(handler_ctx *hctx) {
+	buffer_free(hctx->response);
+	buffer_free(hctx->response_header);
+
+	free(hctx);
+}
+
+enum {FDEVENT_HANDLED_UNSET, FDEVENT_HANDLED_FINISHED, FDEVENT_HANDLED_NOT_FINISHED, FDEVENT_HANDLED_ERROR};
+
+INIT_FUNC(mod_cgi_init) {
+	plugin_data *p;
+
+	p = calloc(1, sizeof(*p));
+
+	assert(p);
+
+	p->tmp_buf = buffer_init();
+	p->parse_response = buffer_init();
+
+	return p;
+}
+
+
+FREE_FUNC(mod_cgi_free) {
+	plugin_data *p = p_d;
+	buffer_pid_t *r = &(p->cgi_pid);
+
+	UNUSED(srv);
+
+	if (p->config_storage) {
+		size_t i;
+		for (i = 0; i < srv->config_context->used; i++) {
+			plugin_config *s = p->config_storage[i];
+
+			array_free(s->cgi);
+
+			free(s);
+		}
+		free(p->config_storage);
+	}
+
+
+	if (r->ptr) free(r->ptr);
+
+	buffer_free(p->tmp_buf);
+	buffer_free(p->parse_response);
+
+	free(p);
+
+	return HANDLER_GO_ON;
+}
+
+SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) {
+	plugin_data *p = p_d;
+	size_t i = 0;
+
+	config_values_t cv[] = {
+		{ "cgi.assign",                  NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
+		{ NULL,                          NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET}
+	};
+
+	if (!p) return HANDLER_ERROR;
+
+	p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
+
+	for (i = 0; i < srv->config_context->used; i++) {
+		plugin_config *s;
+
+		s = calloc(1, sizeof(plugin_config));
+		assert(s);
+
+		s->cgi    = array_init();
+
+		cv[0].destination = s->cgi;
+
+		p->config_storage[i] = s;
+
+		if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
+			return HANDLER_ERROR;
+		}
+	}
+
+	return HANDLER_GO_ON;
+}
+
+
+static int cgi_pid_add(server *srv, plugin_data *p, pid_t pid) {
+	int m = -1;
+	size_t i;
+	buffer_pid_t *r = &(p->cgi_pid);
+
+	UNUSED(srv);
+
+	for (i = 0; i < r->used; i++) {
+		if (r->ptr[i] > m) m = r->ptr[i];
+	}
+
+	if (r->size == 0) {
+		r->size = 16;
+		r->ptr = malloc(sizeof(*r->ptr) * r->size);
+	} else if (r->used == r->size) {
+		r->size += 16;
+		r->ptr = realloc(r->ptr, sizeof(*r->ptr) * r->size);
+	}
+
+	r->ptr[r->used++] = pid;
+
+	return m;
+}
+
+static int cgi_pid_del(server *srv, plugin_data *p, pid_t pid) {
+	size_t i;
+	buffer_pid_t *r = &(p->cgi_pid);
+
+	UNUSED(srv);
+
+	for (i = 0; i < r->used; i++) {
+		if (r->ptr[i] == pid) break;
+	}
+
+	if (i != r->used) {
+		/* found */
+
+		if (i != r->used - 1) {
+			r->ptr[i] = r->ptr[r->used - 1];
+		}
+		r->used--;
+	}
+
+	return 0;
+}
+
+static int cgi_response_parse(server *srv, connection *con, plugin_data *p, buffer *in) {
+	char *ns;
+	const char *s;
+	int line = 0;
+
+	UNUSED(srv);
+
+	buffer_copy_string_buffer(p->parse_response, in);
+
+	for (s = p->parse_response->ptr;
+	     NULL != (ns = strchr(s, '\n'));
+	     s = ns + 1, line++) {
+		const char *key, *value;
+		int key_len;
+		data_string *ds;
+
+		/* strip the \n */
+		ns[0] = '\0';
+
+		if (ns > s && ns[-1] == '\r') ns[-1] = '\0';
+
+		if (line == 0 &&
+		    0 == strncmp(s, "HTTP/1.", 7)) {
+			/* non-parsed header ... we parse them anyway */
+
+			if ((s[7] == '1' ||
+			     s[7] == '0') &&
+			    s[8] == ' ') {
+				int status;
+				/* after the space should be a status code for us */
+
+				status = strtol(s+9, NULL, 10);
+
+				if (status >= 100 &&
+				    status < 1000) {
+					/* we expected 3 digits and didn't got them */
+					con->parsed_response |= HTTP_STATUS;
+					con->http_status = status;
+				}
+			}
+		} else {
+			/* parse the headers */
+			key = s;
+			if (NULL == (value = strchr(s, ':'))) {
+				/* we expect: "<key>: <value>\r\n" */
+				continue;
+			}
+
+			key_len = value - key;
+			value += 1;
+
+			/* skip LWS */
+			while (*value == ' ' || *value == '\t') value++;
+
+			if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
+				ds = data_response_init();
+			}
+			buffer_copy_string_len(ds->key, key, key_len);
+			buffer_copy_string(ds->value, value);
+
+			array_insert_unique(con->response.headers, (data_unset *)ds);
+
+			switch(key_len) {
+			case 4:
+				if (0 == strncasecmp(key, "Date", key_len)) {
+					con->parsed_response |= HTTP_DATE;
+				}
+				break;
+			case 6:
+				if (0 == strncasecmp(key, "Status", key_len)) {
+					con->http_status = strtol(value, NULL, 10);
+					con->parsed_response |= HTTP_STATUS;
+				}
+				break;
+			case 8:
+				if (0 == strncasecmp(key, "Location", key_len)) {
+					con->parsed_response |= HTTP_LOCATION;
+				}
+				break;
+			case 10:
+				if (0 == strncasecmp(key, "Connection", key_len)) {
+					con->response.keep_alive = (0 == strcasecmp(value, "Keep-Alive")) ? 1 : 0;
+					con->parsed_response |= HTTP_CONNECTION;
+				}
+				break;
+			case 14:
+				if (0 == strncasecmp(key, "Content-Length", key_len)) {
+					con->response.content_length = strtol(value, NULL, 10);
+					con->parsed_response |= HTTP_CONTENT_LENGTH;
+				}
+				break;
+			default:
+				break;
+			}
+		}
+	}
+
+	/* CGI/1.1 rev 03 - 7.2.1.2 */
+	if ((con->parsed_response & HTTP_LOCATION) &&
+	    !(con->parsed_response & HTTP_STATUS)) {
+		con->http_status = 302;
+	}
+
+	return 0;
+}
+
+
+static int cgi_demux_response(server *srv, handler_ctx *hctx) {
+	plugin_data *p    = hctx->plugin_data;
+	connection  *con  = hctx->remote_conn;
+
+	while(1) {
+		int n;
+
+		buffer_prepare_copy(hctx->response, 1024);
+		if (-1 == (n = read(hctx->fd, hctx->response->ptr, hctx->response->size - 1))) {
+			if (errno == EAGAIN || errno == EINTR) {
+				/* would block, wait for signal */
+				return FDEVENT_HANDLED_NOT_FINISHED;
+			}
+			/* error */
+			log_error_write(srv, __FILE__, __LINE__, "sdd", strerror(errno), con->fd, hctx->fd);
+			return FDEVENT_HANDLED_ERROR;
+		}
+
+		if (n == 0) {
+			/* read finished */
+
+			con->file_finished = 1;
+
+			/* send final chunk */
+			http_chunk_append_mem(srv, con, NULL, 0);
+			joblist_append(srv, con);
+
+			return FDEVENT_HANDLED_FINISHED;
+		}
+
+		hctx->response->ptr[n] = '\0';
+		hctx->response->used = n+1;
+
+		/* split header from body */
+
+		if (con->file_started == 0) {
+			int is_header = 0;
+			int is_header_end = 0;
+			size_t last_eol = 0;
+			size_t i;
+
+			buffer_append_string_buffer(hctx->response_header, hctx->response);
+
+			/**
+			 * we have to handle a few cases:
+			 *
+			 * nph:
+			 * 
+			 *   HTTP/1.0 200 Ok\n
+			 *   Header: Value\n
+			 *   \n
+			 *
+			 * CGI:
+			 *   Header: Value\n
+			 *   Status: 200\n
+			 *   \n
+			 *
+			 * and different mixes of \n and \r\n combinations
+			 * 
+			 * Some users also forget about CGI and just send a response and hope 
+			 * we handle it. No headers, no header-content seperator
+			 * 
+			 */
+			
+			/* nph (non-parsed headers) */
+			if (0 == strncmp(hctx->response_header->ptr, "HTTP/1.", 7)) is_header = 1;
+				
+			for (i = 0; !is_header_end && i < hctx->response_header->used - 1; i++) {
+				char c = hctx->response_header->ptr[i];
+
+				switch (c) {
+				case ':':
+					/* we found a colon
+					 *
+					 * looks like we have a normal header 
+					 */
+					is_header = 1;
+					break;
+				case '\n':
+					/* EOL */
+					if (is_header == 0) {
+						/* we got a EOL but we don't seem to got a HTTP header */
+
+						is_header_end = 1;
+
+						break;
+					}
+
+					/**
+					 * check if we saw a \n(\r)?\n sequence 
+					 */
+					if (last_eol > 0 && 
+					    ((i - last_eol == 1) || 
+					     (i - last_eol == 2 && hctx->response_header->ptr[i - 1] == '\r'))) {
+						is_header_end = 1;
+						break;
+					}
+
+					last_eol = i;
+
+					break;
+				}
+			}
+
+			if (is_header_end) {
+				if (!is_header) {
+					/* no header, but a body */
+
+					if (con->request.http_version == HTTP_VERSION_1_1) {
+						con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
+					}
+
+					http_chunk_append_mem(srv, con, hctx->response_header->ptr, hctx->response_header->used);
+					joblist_append(srv, con);
+				} else {
+					const char *bstart;
+					size_t blen;
+					
+					/**
+					 * i still points to the char after the terminating EOL EOL
+					 *
+					 * put it on the last \n again
+					 */
+					i--;
+					
+					/* the body starts after the EOL */
+					bstart = hctx->response_header->ptr + (i + 1);
+					blen = (hctx->response_header->used - 1) - (i + 1);
+					
+					/* string the last \r?\n */
+					if (i > 0 && (hctx->response_header->ptr[i - 1] == '\r')) {
+						i--;
+					}
+
+					hctx->response_header->ptr[i] = '\0';
+					hctx->response_header->used = i + 1; /* the string + \0 */
+					
+					/* parse the response header */
+					cgi_response_parse(srv, con, p, hctx->response_header);
+
+					/* enable chunked-transfer-encoding */
+					if (con->request.http_version == HTTP_VERSION_1_1 &&
+					    !(con->parsed_response & HTTP_CONTENT_LENGTH)) {
+						con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
+					}
+
+					if (blen > 0) {
+						http_chunk_append_mem(srv, con, bstart, blen + 1);
+						joblist_append(srv, con);
+					}
+				}
+
+				con->file_started = 1;
+			}
+		} else {
+			http_chunk_append_mem(srv, con, hctx->response->ptr, hctx->response->used);
+			joblist_append(srv, con);
+		}
+
+#if 0
+		log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), b->ptr);
+#endif
+	}
+
+	return FDEVENT_HANDLED_NOT_FINISHED;
+}
+
+static handler_t cgi_connection_close(server *srv, handler_ctx *hctx) {
+	int status;
+	pid_t pid;
+	plugin_data *p;
+	connection  *con;
+
+	if (NULL == hctx) return HANDLER_GO_ON;
+
+	p    = hctx->plugin_data;
+	con  = hctx->remote_conn;
+
+	if (con->mode != p->id) return HANDLER_GO_ON;
+
+#ifndef __WIN32
+
+	/* the connection to the browser went away, but we still have a connection
+	 * to the CGI script
+	 *
+	 * close cgi-connection
+	 */
+
+	if (hctx->fd != -1) {
+		/* close connection to the cgi-script */
+		fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
+		fdevent_unregister(srv->ev, hctx->fd);
+
+		if (close(hctx->fd)) {
+			log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno));
+		}
+
+		hctx->fd = -1;
+		hctx->fde_ndx = -1;
+	}
+
+	pid = hctx->pid;
+
+	con->plugin_ctx[p->id] = NULL;
+
+	/* is this a good idea ? */
+	cgi_handler_ctx_free(hctx);
+
+	/* if waitpid hasn't been called by response.c yet, do it here */
+	if (pid) {
+		/* check if the CGI-script is already gone */
+		switch(waitpid(pid, &status, WNOHANG)) {
+		case 0:
+			/* not finished yet */
+#if 0
+			log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", pid);
+#endif
+			break;
+		case -1:
+			/* */
+			if (errno == EINTR) break;
+
+			/*
+			 * errno == ECHILD happens if _subrequest catches the process-status before
+			 * we have read the response of the cgi process
+			 *
+			 * -> catch status
+			 * -> WAIT_FOR_EVENT
+			 * -> read response
+			 * -> we get here with waitpid == ECHILD
+			 *
+			 */
+			if (errno == ECHILD) return HANDLER_GO_ON;
+
+			log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno));
+			return HANDLER_ERROR;
+		default:
+			/* Send an error if we haven't sent any data yet */
+			if (0 == con->file_started) {
+				connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
+				con->http_status = 500;
+				con->mode = DIRECT;
+			}
+
+			if (WIFEXITED(status)) {
+#if 0
+				log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", pid);
+#endif
+				pid = 0;
+
+				return HANDLER_GO_ON;
+			} else {
+				log_error_write(srv, __FILE__, __LINE__, "sd", "cgi died, pid:", pid);
+				pid = 0;
+				return HANDLER_GO_ON;
+			}
+		}
+
+
+		kill(pid, SIGTERM);
+
+		/* cgi-script is still alive, queue the PID for removal */
+		cgi_pid_add(srv, p, pid);
+	}
+#endif
+	return HANDLER_GO_ON;
+}
+
+static handler_t cgi_connection_close_callback(server *srv, connection *con, void *p_d) {
+	plugin_data *p = p_d;
+
+	return cgi_connection_close(srv, con->plugin_ctx[p->id]);
+}
+
+
+static handler_t cgi_handle_fdevent(void *s, void *ctx, int revents) {
+	server      *srv  = (server *)s;
+	handler_ctx *hctx = ctx;
+	connection  *con  = hctx->remote_conn;
+
+	joblist_append(srv, con);
+
+	if (hctx->fd == -1) {
+		log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), "invalid cgi-fd");
+
+		return HANDLER_ERROR;
+	}
+
+	if (revents & FDEVENT_IN) {
+		switch (cgi_demux_response(srv, hctx)) {
+		case FDEVENT_HANDLED_NOT_FINISHED:
+			break;
+		case FDEVENT_HANDLED_FINISHED:
+			/* we are done */
+
+#if 0
+			log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), "finished");
+#endif
+			cgi_connection_close(srv, hctx);
+
+			/* if we get a IN|HUP and have read everything don't exec the close twice */
+			return HANDLER_FINISHED;
+		case FDEVENT_HANDLED_ERROR:
+			connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
+			con->http_status = 500;
+			con->mode = DIRECT;
+
+			log_error_write(srv, __FILE__, __LINE__, "s", "demuxer failed: ");
+			break;
+		}
+	}
+
+	if (revents & FDEVENT_OUT) {
+		/* nothing to do */
+	}
+
+	/* perhaps this issue is already handled */
+	if (revents & FDEVENT_HUP) {
+		/* check if we still have a unfinished header package which is a body in reality */
+		if (con->file_started == 0 &&
+		    hctx->response_header->used) {
+			con->file_started = 1;
+			http_chunk_append_mem(srv, con, hctx->response_header->ptr, hctx->response_header->used);
+			joblist_append(srv, con);
+		}
+
+		if (con->file_finished == 0) {
+			http_chunk_append_mem(srv, con, NULL, 0);
+			joblist_append(srv, con);
+		}
+
+		con->file_finished = 1;
+
+		if (chunkqueue_is_empty(con->write_queue)) {
+			/* there is nothing left to write */
+			connection_set_state(srv, con, CON_STATE_RESPONSE_END);
+		} else {
+			/* used the write-handler to finish the request on demand */
+
+		}
+
+# if 0
+		log_error_write(srv, __FILE__, __LINE__, "sddd", "got HUP from cgi", con->fd, hctx->fd, revents);
+# endif
+
+		/* rtsigs didn't liked the close */
+		cgi_connection_close(srv, hctx);
+	} else if (revents & FDEVENT_ERR) {
+		con->file_finished = 1;
+
+		/* kill all connections to the cgi process */
+		cgi_connection_close(srv, hctx);
+#if 1
+		log_error_write(srv, __FILE__, __LINE__, "s", "cgi-FDEVENT_ERR");
+#endif
+		return HANDLER_ERROR;
+	}
+
+	return HANDLER_FINISHED;
+}
+
+
+static int cgi_env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) {
+	char *dst;
+
+	if (!key || !val) return -1;
+
+	dst = malloc(key_len + val_len + 3);
+	memcpy(dst, key, key_len);
+	dst[key_len] = '=';
+	/* add the \0 from the value */
+	memcpy(dst + key_len + 1, val, val_len + 1);
+
+	if (env->size == 0) {
+		env->size = 16;
+		env->ptr = malloc(env->size * sizeof(*env->ptr));
+	} else if (env->size == env->used) {
+		env->size += 16;
+		env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
+	}
+
+	env->ptr[env->used++] = dst;
+
+	return 0;
+}
+
+static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer *cgi_handler) {
+	pid_t pid;
+
+#ifdef HAVE_IPV6
+	char b2[INET6_ADDRSTRLEN + 1];
+#endif
+
+	int to_cgi_fds[2];
+	int from_cgi_fds[2];
+	struct stat st;
+
+#ifndef __WIN32
+
+	if (cgi_handler->used > 1) {
+		/* stat the exec file */
+		if (-1 == (stat(cgi_handler->ptr, &st))) {
+			log_error_write(srv, __FILE__, __LINE__, "sbss",
+					"stat for cgi-handler", cgi_handler,
+					"failed:", strerror(errno));
+			return -1;
+		}
+	}
+
+	if (pipe(to_cgi_fds)) {
+		log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno));
+		return -1;
+	}
+
+	if (pipe(from_cgi_fds)) {
+		log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno));
+		return -1;
+	}
+
+	/* fork, execve */
+	switch (pid = fork()) {
+	case 0: {
+		/* child */
+		char **args;
+		int argc;
+		int i = 0;
+		char buf[32];
+		size_t n;
+		char_array env;
+		char *c;
+		const char *s;
+		server_socket *srv_sock = con->srv_socket;
+
+		/* move stdout to from_cgi_fd[1] */
+		close(STDOUT_FILENO);
+		dup2(from_cgi_fds[1], STDOUT_FILENO);
+		close(from_cgi_fds[1]);
+		/* not needed */
+		close(from_cgi_fds[0]);
+
+		/* move the stdin to to_cgi_fd[0] */
+		close(STDIN_FILENO);
+		dup2(to_cgi_fds[0], STDIN_FILENO);
+		close(to_cgi_fds[0]);
+		/* not needed */
+		close(to_cgi_fds[1]);
+
+		/* HACK:
+		 * this is not nice, but it works
+		 *
+		 * we feed the stderr of the CGI to our errorlog, if possible
+		 */
+		if (srv->errorlog_mode == ERRORLOG_FILE) {
+			close(STDERR_FILENO);
+			dup2(srv->errorlog_fd, STDERR_FILENO);
+		}
+
+		/* create environment */
+		env.ptr = NULL;
+		env.size = 0;
+		env.used = 0;
+
+		cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_NAME"/"PACKAGE_VERSION));
+
+		if (!buffer_is_empty(con->server_name)) {
+			cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), CONST_BUF_LEN(con->server_name));
+		} else {
+#ifdef HAVE_IPV6
+			s = inet_ntop(srv_sock->addr.plain.sa_family,
+				      srv_sock->addr.plain.sa_family == AF_INET6 ?
+				      (const void *) &(srv_sock->addr.ipv6.sin6_addr) :
+				      (const void *) &(srv_sock->addr.ipv4.sin_addr),
+				      b2, sizeof(b2)-1);
+#else
+			s = inet_ntoa(srv_sock->addr.ipv4.sin_addr);
+#endif
+			cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), s, strlen(s));
+		}
+		cgi_env_add(&env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1"));
+
+		s = get_http_version_name(con->request.http_version);
+
+		cgi_env_add(&env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s));
+
+		LI_ltostr(buf,
+#ifdef HAVE_IPV6
+			ntohs(srv_sock->addr.plain.sa_family == AF_INET6 ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port)
+#else
+			ntohs(srv_sock->addr.ipv4.sin_port)
+#endif
+			);
+		cgi_env_add(&env, CONST_STR_LEN("SERVER_PORT"), buf, strlen(buf));
+
+#ifdef HAVE_IPV6
+		s = inet_ntop(srv_sock->addr.plain.sa_family,
+			      srv_sock->addr.plain.sa_family == AF_INET6 ?
+			      (const void *) &(srv_sock->addr.ipv6.sin6_addr) :
+			      (const void *) &(srv_sock->addr.ipv4.sin_addr),
+			      b2, sizeof(b2)-1);
+#else
+		s = inet_ntoa(srv_sock->addr.ipv4.sin_addr);
+#endif
+		cgi_env_add(&env, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s));
+
+		s = get_http_method_name(con->request.http_method);
+		cgi_env_add(&env, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s));
+
+		if (!buffer_is_empty(con->request.pathinfo)) {
+			cgi_env_add(&env, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
+		}
+		cgi_env_add(&env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200"));
+		if (!buffer_is_empty(con->uri.query)) {
+			cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query));
+		}
+		if (!buffer_is_empty(con->request.orig_uri)) {
+			cgi_env_add(&env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
+		}
+
+
+#ifdef HAVE_IPV6
+		s = inet_ntop(con->dst_addr.plain.sa_family,
+			      con->dst_addr.plain.sa_family == AF_INET6 ?
+			      (const void *) &(con->dst_addr.ipv6.sin6_addr) :
+			      (const void *) &(con->dst_addr.ipv4.sin_addr),
+			      b2, sizeof(b2)-1);
+#else
+		s = inet_ntoa(con->dst_addr.ipv4.sin_addr);
+#endif
+		cgi_env_add(&env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s));
+
+		LI_ltostr(buf,
+#ifdef HAVE_IPV6
+			ntohs(con->dst_addr.plain.sa_family == AF_INET6 ? con->dst_addr.ipv6.sin6_port : con->dst_addr.ipv4.sin_port)
+#else
+			ntohs(con->dst_addr.ipv4.sin_port)
+#endif
+			);
+		cgi_env_add(&env, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf));
+
+		if (!buffer_is_empty(con->authed_user)) {
+			cgi_env_add(&env, CONST_STR_LEN("REMOTE_USER"),
+				    CONST_BUF_LEN(con->authed_user));
+		}
+
+#ifdef USE_OPENSSL
+	if (srv_sock->is_ssl) {
+		cgi_env_add(&env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on"));
+	}
+#endif
+
+		/* request.content_length < SSIZE_MAX, see request.c */
+		LI_ltostr(buf, con->request.content_length);
+		cgi_env_add(&env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf));
+		cgi_env_add(&env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path));
+		cgi_env_add(&env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
+		cgi_env_add(&env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.doc_root));
+
+		/* for valgrind */
+		if (NULL != (s = getenv("LD_PRELOAD"))) {
+			cgi_env_add(&env, CONST_STR_LEN("LD_PRELOAD"), s, strlen(s));
+		}
+
+		if (NULL != (s = getenv("LD_LIBRARY_PATH"))) {
+			cgi_env_add(&env, CONST_STR_LEN("LD_LIBRARY_PATH"), s, strlen(s));
+		}
+#ifdef __CYGWIN__
+		/* CYGWIN needs SYSTEMROOT */
+		if (NULL != (s = getenv("SYSTEMROOT"))) {
+			cgi_env_add(&env, CONST_STR_LEN("SYSTEMROOT"), s, strlen(s));
+		}
+#endif
+
+		for (n = 0; n < con->request.headers->used; n++) {
+			data_string *ds;
+
+			ds = (data_string *)con->request.headers->data[n];
+
+			if (ds->value->used && ds->key->used) {
+				size_t j;
+
+				buffer_reset(p->tmp_buf);
+
+				if (0 != strcasecmp(ds->key->ptr, "CONTENT-TYPE")) {
+					buffer_copy_string(p->tmp_buf, "HTTP_");
+					p->tmp_buf->used--; /* strip \0 after HTTP_ */
+				}
+
+				buffer_prepare_append(p->tmp_buf, ds->key->used + 2);
+
+				for (j = 0; j < ds->key->used - 1; j++) {
+					char cr = '_';
+					if (light_isalpha(ds->key->ptr[j])) {
+						/* upper-case */
+						cr = ds->key->ptr[j] & ~32;
+					} else if (light_isdigit(ds->key->ptr[j])) {
+						/* copy */
+						cr = ds->key->ptr[j];
+					}
+					p->tmp_buf->ptr[p->tmp_buf->used++] = cr;
+				}
+				p->tmp_buf->ptr[p->tmp_buf->used++] = '\0';
+
+				cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value));
+			}
+		}
+
+		for (n = 0; n < con->environment->used; n++) {
+			data_string *ds;
+
+			ds = (data_string *)con->environment->data[n];
+
+			if (ds->value->used && ds->key->used) {
+				size_t j;
+
+				buffer_reset(p->tmp_buf);
+
+				buffer_prepare_append(p->tmp_buf, ds->key->used + 2);
+
+				for (j = 0; j < ds->key->used - 1; j++) {
+					p->tmp_buf->ptr[p->tmp_buf->used++] =
+						isalpha((unsigned char)ds->key->ptr[j]) ?
+						toupper((unsigned char)ds->key->ptr[j]) : '_';
+				}
+				p->tmp_buf->ptr[p->tmp_buf->used++] = '\0';
+
+				cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value));
+			}
+		}
+
+		if (env.size == env.used) {
+			env.size += 16;
+			env.ptr = realloc(env.ptr, env.size * sizeof(*env.ptr));
+		}
+
+		env.ptr[env.used] = NULL;
+
+		/* set up args */
+		argc = 3;
+		args = malloc(sizeof(*args) * argc);
+		i = 0;
+
+		if (cgi_handler->used > 1) {
+			args[i++] = cgi_handler->ptr;
+		}
+		args[i++] = con->physical.path->ptr;
+		args[i++] = NULL;
+
+		/* search for the last / */
+		if (NULL != (c = strrchr(con->physical.path->ptr, '/'))) {
+			*c = '\0';
+
+			/* change to the physical directory */
+			if (-1 == chdir(con->physical.path->ptr)) {
+				log_error_write(srv, __FILE__, __LINE__, "ssb", "chdir failed:", strerror(errno), con->physical.path);
+			}
+			*c = '/';
+		}
+
+		/* we don't need the client socket */
+		for (i = 3; i < 256; i++) {
+			if (i != srv->errorlog_fd) close(i);
+		}
+
+		/* exec the cgi */
+		execve(args[0], args, env.ptr);
+
+		log_error_write(srv, __FILE__, __LINE__, "sss", "CGI failed:", strerror(errno), args[0]);
+
+		/* */
+		SEGFAULT();
+		break;
+	}
+	case -1:
+		/* error */
+		log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno));
+		return -1;
+		break;
+	default: {
+		handler_ctx *hctx;
+		/* father */
+
+		close(from_cgi_fds[1]);
+		close(to_cgi_fds[0]);
+
+		if (con->request.content_length) {
+			chunkqueue *cq = con->request_content_queue;
+			chunk *c;
+
+			assert(chunkqueue_length(cq) == (off_t)con->request.content_length);
+
+			/* there is content to send */
+			for (c = cq->first; c; c = cq->first) {
+				int r = 0;
+
+				/* copy all chunks */
+				switch(c->type) {
+				case FILE_CHUNK:
+
+					if (c->file.mmap.start == MAP_FAILED) {
+						if (-1 == c->file.fd &&  /* open the file if not already open */
+						    -1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) {
+							log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno));
+
+							close(from_cgi_fds[0]);
+							close(to_cgi_fds[1]);
+							return -1;
+						}
+
+						c->file.mmap.length = c->file.length;
+
+						if (MAP_FAILED == (c->file.mmap.start = mmap(0,  c->file.mmap.length, PROT_READ, MAP_SHARED, c->file.fd, 0))) {
+							log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ",
+									strerror(errno), c->file.name,  c->file.fd);
+
+							close(from_cgi_fds[0]);
+							close(to_cgi_fds[1]);
+							return -1;
+						}
+
+						close(c->file.fd);
+						c->file.fd = -1;
+
+						/* chunk_reset() or chunk_free() will cleanup for us */
+					}
+
+					if ((r = write(to_cgi_fds[1], c->file.mmap.start + c->offset, c->file.length - c->offset)) < 0) {
+						switch(errno) {
+						case ENOSPC:
+							con->http_status = 507;
+
+							break;
+						default:
+							con->http_status = 403;
+							break;
+						}
+					}
+					break;
+				case MEM_CHUNK:
+					if ((r = write(to_cgi_fds[1], c->mem->ptr + c->offset, c->mem->used - c->offset - 1)) < 0) {
+						switch(errno) {
+						case ENOSPC:
+							con->http_status = 507;
+
+							break;
+						default:
+							con->http_status = 403;
+							break;
+						}
+					}
+					break;
+				case UNUSED_CHUNK:
+					break;
+				}
+
+				if (r > 0) {
+					c->offset += r;
+					cq->bytes_out += r;
+				} else {
+					break;
+				}
+				chunkqueue_remove_finished_chunks(cq);
+			}
+		}
+
+		close(to_cgi_fds[1]);
+
+		/* register PID and wait for them asyncronously */
+		con->mode = p->id;
+		buffer_reset(con->physical.path);
+
+		hctx = cgi_handler_ctx_init();
+
+		hctx->remote_conn = con;
+		hctx->plugin_data = p;
+		hctx->pid = pid;
+		hctx->fd = from_cgi_fds[0];
+		hctx->fde_ndx = -1;
+
+		con->plugin_ctx[p->id] = hctx;
+
+		fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx);
+		fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
+
+		if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) {
+			log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno));
+
+			fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
+			fdevent_unregister(srv->ev, hctx->fd);
+
+			log_error_write(srv, __FILE__, __LINE__, "sd", "cgi close:", hctx->fd);
+
+			close(hctx->fd);
+
+			cgi_handler_ctx_free(hctx);
+
+			con->plugin_ctx[p->id] = NULL;
+
+			return -1;
+		}
+
+		break;
+	}
+	}
+
+	return 0;
+#else
+	return -1;
+#endif
+}
+
+#define PATCH(x) \
+	p->conf.x = s->x;
+static int mod_cgi_patch_connection(server *srv, connection *con, plugin_data *p) {
+	size_t i, j;
+	plugin_config *s = p->config_storage[0];
+
+	PATCH(cgi);
+
+	/* skip the first, the global context */
+	for (i = 1; i < srv->config_context->used; i++) {
+		data_config *dc = (data_config *)srv->config_context->data[i];
+		s = p->config_storage[i];
+
+		/* condition didn't match */
+		if (!config_check_cond(srv, con, dc)) continue;
+
+		/* merge config */
+		for (j = 0; j < dc->value->used; j++) {
+			data_unset *du = dc->value->data[j];
+
+			if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.assign"))) {
+				PATCH(cgi);
+			}
+		}
+	}
+
+	return 0;
+}
+#undef PATCH
+
+URIHANDLER_FUNC(cgi_is_handled) {
+	size_t k, s_len;
+	plugin_data *p = p_d;
+	buffer *fn = con->physical.path;
+
+	if (fn->used == 0) return HANDLER_GO_ON;
+
+	mod_cgi_patch_connection(srv, con, p);
+
+	s_len = fn->used - 1;
+
+	for (k = 0; k < p->conf.cgi->used; k++) {
+		data_string *ds = (data_string *)p->conf.cgi->data[k];
+		size_t ct_len = ds->key->used - 1;
+
+		if (ds->key->used == 0) continue;
+		if (s_len < ct_len) continue;
+
+		if (0 == strncmp(fn->ptr + s_len - ct_len, ds->key->ptr, ct_len)) {
+			if (cgi_create_env(srv, con, p, ds->value)) {
+				con->http_status = 500;
+
+				buffer_reset(con->physical.path);
+				return HANDLER_FINISHED;
+			}
+			/* one handler is enough for the request */
+			break;
+		}
+	}
+
+	return HANDLER_GO_ON;
+}
+
+TRIGGER_FUNC(cgi_trigger) {
+	plugin_data *p = p_d;
+	size_t ndx;
+	/* the trigger handle only cares about lonely PID which we have to wait for */
+#ifndef __WIN32
+
+	for (ndx = 0; ndx < p->cgi_pid.used; ndx++) {
+		int status;
+
+		switch(waitpid(p->cgi_pid.ptr[ndx], &status, WNOHANG)) {
+		case 0:
+			/* not finished yet */
+#if 0
+			log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", p->cgi_pid.ptr[ndx]);
+#endif
+			break;
+		case -1:
+			log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno));
+
+			return HANDLER_ERROR;
+		default:
+
+			if (WIFEXITED(status)) {
+#if 0
+				log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", p->cgi_pid.ptr[ndx]);
+#endif
+			} else if (WIFSIGNALED(status)) {
+				/* FIXME: what if we killed the CGI script with a kill(..., SIGTERM) ?
+				 */
+				if (WTERMSIG(status) != SIGTERM) {
+					log_error_write(srv, __FILE__, __LINE__, "sd", "cleaning up CGI: process died with signal", WTERMSIG(status));
+				}
+			} else {
+				log_error_write(srv, __FILE__, __LINE__, "s", "cleaning up CGI: ended unexpectedly");
+			}
+
+			cgi_pid_del(srv, p, p->cgi_pid.ptr[ndx]);
+			/* del modified the buffer structure
+			 * and copies the last entry to the current one
+			 * -> recheck the current index
+			 */
+			ndx--;
+		}
+	}
+#endif
+	return HANDLER_GO_ON;
+}
+
+SUBREQUEST_FUNC(mod_cgi_handle_subrequest) {
+	int status;
+	plugin_data *p = p_d;
+	handler_ctx *hctx = con->plugin_ctx[p->id];
+
+	if (con->mode != p->id) return HANDLER_GO_ON;
+	if (NULL == hctx) return HANDLER_GO_ON;
+
+#if 0
+	log_error_write(srv, __FILE__, __LINE__, "sdd", "subrequest, pid =", hctx, hctx->pid);
+#endif
+	if (hctx->pid == 0) return HANDLER_FINISHED;
+#ifndef __WIN32
+	switch(waitpid(hctx->pid, &status, WNOHANG)) {
+	case 0:
+		/* we only have for events here if we don't have the header yet,
+		 * otherwise the event-handler will send us the incoming data */
+		if (con->file_started) return HANDLER_FINISHED;
+
+		return HANDLER_WAIT_FOR_EVENT;
+	case -1:
+		if (errno == EINTR) return HANDLER_WAIT_FOR_EVENT;
+
+		if (errno == ECHILD && con->file_started == 0) {
+			/*
+			 * second round but still not response
+			 */
+			return HANDLER_WAIT_FOR_EVENT;
+		}
+
+		log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno));
+		con->mode = DIRECT;
+		con->http_status = 500;
+
+		hctx->pid = 0;
+
+		fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
+		fdevent_unregister(srv->ev, hctx->fd);
+
+		if (close(hctx->fd)) {
+			log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno));
+		}
+
+		cgi_handler_ctx_free(hctx);
+
+		con->plugin_ctx[p->id] = NULL;
+
+		return HANDLER_FINISHED;
+	default:
+		/* cgi process exited cleanly
+		 *
+		 * check if we already got the response
+		 */
+
+		if (!con->file_started) return HANDLER_WAIT_FOR_EVENT;
+
+		if (WIFEXITED(status)) {
+			/* nothing */
+		} else {
+			log_error_write(srv, __FILE__, __LINE__, "s", "cgi died ?");
+
+			con->mode = DIRECT;
+			con->http_status = 500;
+
+		}
+
+		hctx->pid = 0;
+
+		fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
+		fdevent_unregister(srv->ev, hctx->fd);
+
+		if (close(hctx->fd)) {
+			log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno));
+		}
+
+		cgi_handler_ctx_free(hctx);
+
+		con->plugin_ctx[p->id] = NULL;
+		return HANDLER_FINISHED;
+	}
+#else
+	return HANDLER_ERROR;
+#endif
+}
+
+
+int mod_cgi_plugin_init(plugin *p) {
+	p->version     = LIGHTTPD_VERSION_ID;
+	p->name        = buffer_init_string("cgi");
+
+	p->connection_reset = cgi_connection_close_callback;
+	p->handle_subrequest_start = cgi_is_handled;
+	p->handle_subrequest = mod_cgi_handle_subrequest;
+#if 0
+	p->handle_fdevent = cgi_handle_fdevent;
+#endif
+	p->handle_trigger = cgi_trigger;
+	p->init           = mod_cgi_init;
+	p->cleanup        = mod_cgi_free;
+	p->set_defaults   = mod_fastcgi_set_defaults;
+
+	p->data        = NULL;
+
+	return 0;
+}

Added: webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_cml.c
URL: http://svn.apache.org/viewvc/webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_cml.c?rev=678637&view=auto
==============================================================================
--- webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_cml.c (added)
+++ webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_cml.c Mon Jul 21 21:35:35 2008
@@ -0,0 +1,322 @@
+#include <sys/stat.h>
+#include <time.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include "buffer.h"
+#include "server.h"
+#include "log.h"
+#include "plugin.h"
+#include "response.h"
+
+#include "stream.h"
+
+#include "mod_cml.h"
+
+/* init the plugin data */
+INIT_FUNC(mod_cml_init) {
+	plugin_data *p;
+
+	p = calloc(1, sizeof(*p));
+
+	p->basedir         = buffer_init();
+	p->baseurl         = buffer_init();
+	p->trigger_handler = buffer_init();
+
+	return p;
+}
+
+/* detroy the plugin data */
+FREE_FUNC(mod_cml_free) {
+	plugin_data *p = p_d;
+
+	UNUSED(srv);
+
+	if (!p) return HANDLER_GO_ON;
+
+	if (p->config_storage) {
+		size_t i;
+		for (i = 0; i < srv->config_context->used; i++) {
+			plugin_config *s = p->config_storage[i];
+
+			buffer_free(s->ext);
+
+			buffer_free(s->mc_namespace);
+			buffer_free(s->power_magnet);
+			array_free(s->mc_hosts);
+
+#if defined(HAVE_MEMCACHE_H)
+			if (s->mc) mc_free(s->mc);
+#endif
+
+			free(s);
+		}
+		free(p->config_storage);
+	}
+
+	buffer_free(p->trigger_handler);
+	buffer_free(p->basedir);
+	buffer_free(p->baseurl);
+
+	free(p);
+
+	return HANDLER_GO_ON;
+}
+
+/* handle plugin config and check values */
+
+SETDEFAULTS_FUNC(mod_cml_set_defaults) {
+	plugin_data *p = p_d;
+	size_t i = 0;
+
+	config_values_t cv[] = {
+		{ "cml.extension",              NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
+		{ "cml.memcache-hosts",         NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },        /* 1 */
+		{ "cml.memcache-namespace",     NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 2 */
+		{ "cml.power-magnet",           NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 3 */
+		{ NULL,                         NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
+	};
+
+	if (!p) return HANDLER_ERROR;
+
+	p->config_storage = malloc(srv->config_context->used * sizeof(specific_config *));
+
+	for (i = 0; i < srv->config_context->used; i++) {
+		plugin_config *s;
+
+		s = malloc(sizeof(plugin_config));
+		s->ext    = buffer_init();
+		s->mc_hosts       = array_init();
+		s->mc_namespace   = buffer_init();
+		s->power_magnet   = buffer_init();
+#if defined(HAVE_MEMCACHE_H)
+		s->mc = NULL;
+#endif
+
+		cv[0].destination = s->ext;
+		cv[1].destination = s->mc_hosts;
+		cv[2].destination = s->mc_namespace;
+		cv[3].destination = s->power_magnet;
+
+		p->config_storage[i] = s;
+
+		if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
+			return HANDLER_ERROR;
+		}
+
+		if (s->mc_hosts->used) {
+#if defined(HAVE_MEMCACHE_H)
+			size_t k;
+			s->mc = mc_new();
+
+			for (k = 0; k < s->mc_hosts->used; k++) {
+				data_string *ds = (data_string *)s->mc_hosts->data[k];
+
+				if (0 != mc_server_add4(s->mc, ds->value->ptr)) {
+					log_error_write(srv, __FILE__, __LINE__, "sb",
+							"connection to host failed:",
+							ds->value);
+
+					return HANDLER_ERROR;
+				}
+			}
+#else
+			log_error_write(srv, __FILE__, __LINE__, "s",
+					"memcache support is not compiled in but cml.memcache-hosts is set, aborting");
+			return HANDLER_ERROR;
+#endif
+		}
+	}
+
+	return HANDLER_GO_ON;
+}
+
+#define PATCH(x) \
+	p->conf.x = s->x;
+static int mod_cml_patch_connection(server *srv, connection *con, plugin_data *p) {
+	size_t i, j;
+	plugin_config *s = p->config_storage[0];
+
+	PATCH(ext);
+#if defined(HAVE_MEMCACHE_H)
+	PATCH(mc);
+#endif
+	PATCH(mc_namespace);
+	PATCH(power_magnet);
+
+	/* skip the first, the global context */
+	for (i = 1; i < srv->config_context->used; i++) {
+		data_config *dc = (data_config *)srv->config_context->data[i];
+		s = p->config_storage[i];
+
+		/* condition didn't match */
+		if (!config_check_cond(srv, con, dc)) continue;
+
+		/* merge config */
+		for (j = 0; j < dc->value->used; j++) {
+			data_unset *du = dc->value->data[j];
+
+			if (buffer_is_equal_string(du->key, CONST_STR_LEN("cml.extension"))) {
+				PATCH(ext);
+			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cml.memcache-hosts"))) {
+#if defined(HAVE_MEMCACHE_H)
+				PATCH(mc);
+#endif
+			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cml.memcache-namespace"))) {
+				PATCH(mc_namespace);
+			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cml.power-magnet"))) {
+				PATCH(power_magnet);
+			}
+		}
+	}
+
+	return 0;
+}
+#undef PATCH
+
+int cache_call_lua(server *srv, connection *con, plugin_data *p, buffer *cml_file) {
+	buffer *b;
+	char *c;
+
+	/* cleanup basedir */
+	b = p->baseurl;
+	buffer_copy_string_buffer(b, con->uri.path);
+	for (c = b->ptr + b->used - 1; c > b->ptr && *c != '/'; c--);
+
+	if (*c == '/') {
+		b->used = c - b->ptr + 2;
+		*(c+1) = '\0';
+	}
+
+	b = p->basedir;
+	buffer_copy_string_buffer(b, con->physical.path);
+	for (c = b->ptr + b->used - 1; c > b->ptr && *c != '/'; c--);
+
+	if (*c == '/') {
+		b->used = c - b->ptr + 2;
+		*(c+1) = '\0';
+	}
+
+
+	/* prepare variables
+	 *   - cookie-based
+	 *   - get-param-based
+	 */
+	return cache_parse_lua(srv, con, p, cml_file);
+}
+
+URIHANDLER_FUNC(mod_cml_power_magnet) {
+	plugin_data *p = p_d;
+
+	mod_cml_patch_connection(srv, con, p);
+
+	buffer_reset(p->basedir);
+	buffer_reset(p->baseurl);
+	buffer_reset(p->trigger_handler);
+
+	if (buffer_is_empty(p->conf.power_magnet)) return HANDLER_GO_ON;
+
+	/*
+	 * power-magnet:
+	 * cml.power-magnet = server.docroot + "/rewrite.cml"
+	 *
+	 * is called on EACH request, take the original REQUEST_URI and modifies the
+	 * request header as neccesary.
+	 *
+	 * First use:
+	 * if file_exists("/maintainance.html") {
+	 *   output_include = ( "/maintainance.html" )
+	 *   return CACHE_HIT
+	 * }
+	 *
+	 * as we only want to rewrite HTML like requests we should cover it in a conditional
+	 *
+	 * */
+
+	switch(cache_call_lua(srv, con, p, p->conf.power_magnet)) {
+	case -1:
+		/* error */
+		if (con->conf.log_request_handling) {
+			log_error_write(srv, __FILE__, __LINE__, "s", "cache-error");
+		}
+		con->http_status = 500;
+		return HANDLER_COMEBACK;
+	case 0:
+		if (con->conf.log_request_handling) {
+			log_error_write(srv, __FILE__, __LINE__, "s", "cache-hit");
+		}
+		/* cache-hit */
+		buffer_reset(con->physical.path);
+		return HANDLER_FINISHED;
+	case 1:
+		/* cache miss */
+		return HANDLER_GO_ON;
+	default:
+		con->http_status = 500;
+		return HANDLER_COMEBACK;
+	}
+}
+
+URIHANDLER_FUNC(mod_cml_is_handled) {
+	plugin_data *p = p_d;
+
+	if (buffer_is_empty(con->physical.path)) return HANDLER_ERROR;
+
+	mod_cml_patch_connection(srv, con, p);
+
+	buffer_reset(p->basedir);
+	buffer_reset(p->baseurl);
+	buffer_reset(p->trigger_handler);
+
+	if (buffer_is_empty(p->conf.ext)) return HANDLER_GO_ON;
+
+	if (!buffer_is_equal_right_len(con->physical.path, p->conf.ext, p->conf.ext->used - 1)) {
+		return HANDLER_GO_ON;
+	}
+
+	switch(cache_call_lua(srv, con, p, con->physical.path)) {
+	case -1:
+		/* error */
+		if (con->conf.log_request_handling) {
+			log_error_write(srv, __FILE__, __LINE__, "s", "cache-error");
+		}
+		con->http_status = 500;
+		return HANDLER_COMEBACK;
+	case 0:
+		if (con->conf.log_request_handling) {
+			log_error_write(srv, __FILE__, __LINE__, "s", "cache-hit");
+		}
+		/* cache-hit */
+		buffer_reset(con->physical.path);
+		return HANDLER_FINISHED;
+	case 1:
+		if (con->conf.log_request_handling) {
+			log_error_write(srv, __FILE__, __LINE__, "s", "cache-miss");
+		}
+		/* cache miss */
+		return HANDLER_COMEBACK;
+	default:
+		con->http_status = 500;
+		return HANDLER_COMEBACK;
+	}
+}
+
+int mod_cml_plugin_init(plugin *p) {
+	p->version     = LIGHTTPD_VERSION_ID;
+	p->name        = buffer_init_string("cache");
+
+	p->init        = mod_cml_init;
+	p->cleanup     = mod_cml_free;
+	p->set_defaults  = mod_cml_set_defaults;
+
+	p->handle_subrequest_start = mod_cml_is_handled;
+	p->handle_physical         = mod_cml_power_magnet;
+
+	p->data        = NULL;
+
+	return 0;
+}

Added: webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_cml.h
URL: http://svn.apache.org/viewvc/webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_cml.h?rev=678637&view=auto
==============================================================================
--- webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_cml.h (added)
+++ webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_cml.h Mon Jul 21 21:35:35 2008
@@ -0,0 +1,43 @@
+#ifndef _MOD_CACHE_H_
+#define _MOD_CACHE_H_
+
+#include "buffer.h"
+#include "server.h"
+#include "response.h"
+
+#include "stream.h"
+#include "plugin.h"
+
+#if defined(HAVE_MEMCACHE_H)
+#include <memcache.h>
+#endif
+
+#define plugin_data mod_cache_plugin_data
+
+typedef struct {
+	buffer *ext;
+
+	array  *mc_hosts;
+	buffer *mc_namespace;
+#if defined(HAVE_MEMCACHE_H)
+	struct memcache *mc;
+#endif
+	buffer *power_magnet;
+} plugin_config;
+
+typedef struct {
+	PLUGIN_DATA;
+
+	buffer *basedir;
+	buffer *baseurl;
+
+	buffer *trigger_handler;
+
+	plugin_config **config_storage;
+
+	plugin_config conf;
+} plugin_data;
+
+int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn);
+
+#endif

Added: webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_cml_funcs.c
URL: http://svn.apache.org/viewvc/webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_cml_funcs.c?rev=678637&view=auto
==============================================================================
--- webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_cml_funcs.c (added)
+++ webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_cml_funcs.c Mon Jul 21 21:35:35 2008
@@ -0,0 +1,301 @@
+#include <sys/stat.h>
+#include <time.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <stdio.h>
+
+#include "buffer.h"
+#include "server.h"
+#include "log.h"
+#include "plugin.h"
+#include "response.h"
+
+#include "mod_cml.h"
+#include "mod_cml_funcs.h"
+
+#ifdef USE_OPENSSL
+# include <openssl/md5.h>
+#else
+# include "md5.h"
+#endif
+
+#define HASHLEN 16
+typedef unsigned char HASH[HASHLEN];
+#define HASHHEXLEN 32
+typedef char HASHHEX[HASHHEXLEN+1];
+#ifdef USE_OPENSSL
+#define IN const
+#else
+#define IN
+#endif
+#define OUT
+
+#ifdef HAVE_LUA_H
+
+int f_crypto_md5(lua_State *L) {
+	MD5_CTX Md5Ctx;
+	HASH HA1;
+	buffer b;
+	char hex[33];
+	int n = lua_gettop(L);
+
+	b.ptr = hex;
+	b.used = 0;
+	b.size = sizeof(hex);
+
+	if (n != 1) {
+		lua_pushstring(L, "md5: expected one argument");
+		lua_error(L);
+	}
+
+	if (!lua_isstring(L, 1)) {
+		lua_pushstring(L, "md5: argument has to be a string");
+		lua_error(L);
+	}
+
+	MD5_Init(&Md5Ctx);
+	MD5_Update(&Md5Ctx, (unsigned char *)lua_tostring(L, 1), lua_strlen(L, 1));
+	MD5_Final(HA1, &Md5Ctx);
+
+	buffer_copy_string_hex(&b, (char *)HA1, 16);
+
+	lua_pushstring(L, b.ptr);
+
+	return 1;
+}
+
+
+int f_file_mtime(lua_State *L) {
+	struct stat st;
+	int n = lua_gettop(L);
+
+	if (n != 1) {
+		lua_pushstring(L, "file_mtime: expected one argument");
+		lua_error(L);
+	}
+
+	if (!lua_isstring(L, 1)) {
+		lua_pushstring(L, "file_mtime: argument has to be a string");
+		lua_error(L);
+	}
+
+	if (-1 == stat(lua_tostring(L, 1), &st)) {
+		lua_pushnil(L);
+		return 1;
+	}
+
+	lua_pushnumber(L, st.st_mtime);
+
+	return 1;
+}
+
+int f_dir_files_iter(lua_State *L) {
+	DIR *d;
+	struct dirent *de;
+
+	d = lua_touserdata(L, lua_upvalueindex(1));
+
+	if (NULL == (de = readdir(d))) {
+		/* EOF */
+		closedir(d);
+
+		return 0;
+	} else {
+		lua_pushstring(L, de->d_name);
+		return 1;
+	}
+}
+
+int f_dir_files(lua_State *L) {
+	DIR *d;
+	int n = lua_gettop(L);
+
+	if (n != 1) {
+		lua_pushstring(L, "dir_files: expected one argument");
+		lua_error(L);
+	}
+
+	if (!lua_isstring(L, 1)) {
+		lua_pushstring(L, "dir_files: argument has to be a string");
+		lua_error(L);
+	}
+
+	/* check if there is a valid DIR handle on the stack */
+	if (NULL == (d = opendir(lua_tostring(L, 1)))) {
+		lua_pushnil(L);
+		return 1;
+	}
+
+	/* push d into registry */
+	lua_pushlightuserdata(L, d);
+	lua_pushcclosure(L, f_dir_files_iter, 1);
+
+	return 1;
+}
+
+int f_file_isreg(lua_State *L) {
+	struct stat st;
+	int n = lua_gettop(L);
+
+	if (n != 1) {
+		lua_pushstring(L, "file_isreg: expected one argument");
+		lua_error(L);
+	}
+
+	if (!lua_isstring(L, 1)) {
+		lua_pushstring(L, "file_isreg: argument has to be a string");
+		lua_error(L);
+	}
+
+	if (-1 == stat(lua_tostring(L, 1), &st)) {
+		lua_pushnil(L);
+		return 1;
+	}
+
+	lua_pushnumber(L, S_ISREG(st.st_mode));
+
+	return 1;
+}
+
+int f_file_isdir(lua_State *L) {
+	struct stat st;
+	int n = lua_gettop(L);
+
+	if (n != 1) {
+		lua_pushstring(L, "file_isreg: expected one argument");
+		lua_error(L);
+	}
+
+	if (!lua_isstring(L, 1)) {
+		lua_pushstring(L, "file_isreg: argument has to be a string");
+		lua_error(L);
+	}
+
+	if (-1 == stat(lua_tostring(L, 1), &st)) {
+		lua_pushnil(L);
+		return 1;
+	}
+
+	lua_pushnumber(L, S_ISDIR(st.st_mode));
+
+	return 1;
+}
+
+
+
+#ifdef HAVE_MEMCACHE_H
+int f_memcache_exists(lua_State *L) {
+	char *r;
+	int n = lua_gettop(L);
+	struct memcache *mc;
+
+	if (!lua_islightuserdata(L, lua_upvalueindex(1))) {
+		lua_pushstring(L, "where is my userdata ?");
+		lua_error(L);
+	}
+
+	mc = lua_touserdata(L, lua_upvalueindex(1));
+
+	if (n != 1) {
+		lua_pushstring(L, "expected one argument");
+		lua_error(L);
+	}
+
+	if (!lua_isstring(L, 1)) {
+		lua_pushstring(L, "argument has to be a string");
+		lua_error(L);
+	}
+
+	if (NULL == (r = mc_aget(mc,
+				 lua_tostring(L, 1), lua_strlen(L, 1)))) {
+
+		lua_pushboolean(L, 0);
+		return 1;
+	}
+
+	free(r);
+
+	lua_pushboolean(L, 1);
+	return 1;
+}
+
+int f_memcache_get_string(lua_State *L) {
+	char *r;
+	int n = lua_gettop(L);
+
+	struct memcache *mc;
+
+	if (!lua_islightuserdata(L, lua_upvalueindex(1))) {
+		lua_pushstring(L, "where is my userdata ?");
+		lua_error(L);
+	}
+
+	mc = lua_touserdata(L, lua_upvalueindex(1));
+
+
+	if (n != 1) {
+		lua_pushstring(L, "expected one argument");
+		lua_error(L);
+	}
+
+	if (!lua_isstring(L, 1)) {
+		lua_pushstring(L, "argument has to be a string");
+		lua_error(L);
+	}
+
+	if (NULL == (r = mc_aget(mc,
+				 lua_tostring(L, 1), lua_strlen(L, 1)))) {
+		lua_pushnil(L);
+		return 1;
+	}
+
+	lua_pushstring(L, r);
+
+	free(r);
+
+	return 1;
+}
+
+int f_memcache_get_long(lua_State *L) {
+	char *r;
+	int n = lua_gettop(L);
+
+	struct memcache *mc;
+
+	if (!lua_islightuserdata(L, lua_upvalueindex(1))) {
+		lua_pushstring(L, "where is my userdata ?");
+		lua_error(L);
+	}
+
+	mc = lua_touserdata(L, lua_upvalueindex(1));
+
+
+	if (n != 1) {
+		lua_pushstring(L, "expected one argument");
+		lua_error(L);
+	}
+
+	if (!lua_isstring(L, 1)) {
+		lua_pushstring(L, "argument has to be a string");
+		lua_error(L);
+	}
+
+	if (NULL == (r = mc_aget(mc,
+				 lua_tostring(L, 1), lua_strlen(L, 1)))) {
+		lua_pushnil(L);
+		return 1;
+	}
+
+	lua_pushnumber(L, strtol(r, NULL, 10));
+
+	free(r);
+
+	return 1;
+}
+#endif
+
+#endif

Added: webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_cml_funcs.h
URL: http://svn.apache.org/viewvc/webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_cml_funcs.h?rev=678637&view=auto
==============================================================================
--- webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_cml_funcs.h (added)
+++ webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_cml_funcs.h Mon Jul 21 21:35:35 2008
@@ -0,0 +1,21 @@
+#ifndef _MOD_CML_FUNCS_H_
+#define _MOD_CML_FUNCS_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_LUA_H
+#include <lua.h>
+
+int f_crypto_md5(lua_State *L);
+int f_file_mtime(lua_State *L);
+int f_file_isreg(lua_State *L);
+int f_file_isdir(lua_State *L);
+int f_dir_files(lua_State *L);
+
+int f_memcache_exists(lua_State *L);
+int f_memcache_get_string(lua_State *L);
+int f_memcache_get_long(lua_State *L);
+#endif
+#endif

Added: webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_cml_lua.c
URL: http://svn.apache.org/viewvc/webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_cml_lua.c?rev=678637&view=auto
==============================================================================
--- webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_cml_lua.c (added)
+++ webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_cml_lua.c Mon Jul 21 21:35:35 2008
@@ -0,0 +1,468 @@
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <time.h>
+
+#include "mod_cml.h"
+#include "mod_cml_funcs.h"
+#include "log.h"
+#include "stream.h"
+
+#include "stat_cache.h"
+
+#ifdef USE_OPENSSL
+# include <openssl/md5.h>
+#else
+# include "md5.h"
+#endif
+
+#define HASHLEN 16
+typedef unsigned char HASH[HASHLEN];
+#define HASHHEXLEN 32
+typedef char HASHHEX[HASHHEXLEN+1];
+#ifdef USE_OPENSSL
+#define IN const
+#else
+#define IN
+#endif
+#define OUT
+
+#ifdef HAVE_LUA_H
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+typedef struct {
+	stream st;
+	int done;
+} readme;
+
+static const char * load_file(lua_State *L, void *data, size_t *size) {
+	readme *rm = data;
+
+	UNUSED(L);
+
+	if (rm->done) return 0;
+
+	*size = rm->st.size;
+	rm->done = 1;
+	return rm->st.start;
+}
+
+static int lua_to_c_get_string(lua_State *L, const char *varname, buffer *b) {
+	int curelem;
+
+	lua_pushstring(L, varname);
+
+	curelem = lua_gettop(L);
+	lua_gettable(L, LUA_GLOBALSINDEX);
+
+	/* it should be a table */
+	if (!lua_isstring(L, curelem)) {
+		lua_settop(L, curelem - 1);
+
+		return -1;
+	}
+
+	buffer_copy_string(b, lua_tostring(L, curelem));
+
+	lua_pop(L, 1);
+
+	assert(curelem - 1 == lua_gettop(L));
+
+	return 0;
+}
+
+static int lua_to_c_is_table(lua_State *L, const char *varname) {
+	int curelem;
+
+	lua_pushstring(L, varname);
+
+	curelem = lua_gettop(L);
+	lua_gettable(L, LUA_GLOBALSINDEX);
+
+	/* it should be a table */
+	if (!lua_istable(L, curelem)) {
+		lua_settop(L, curelem - 1);
+
+		return 0;
+	}
+
+	lua_settop(L, curelem - 1);
+
+	assert(curelem - 1 == lua_gettop(L));
+
+	return 1;
+}
+
+static int c_to_lua_push(lua_State *L, int tbl, const char *key, size_t key_len, const char *val, size_t val_len) {
+	lua_pushlstring(L, key, key_len);
+	lua_pushlstring(L, val, val_len);
+	lua_settable(L, tbl);
+
+	return 0;
+}
+
+
+int cache_export_get_params(lua_State *L, int tbl, buffer *qrystr) {
+	size_t is_key = 1;
+	size_t i;
+	char *key = NULL, *val = NULL;
+
+	key = qrystr->ptr;
+
+	/* we need the \0 */
+	for (i = 0; i < qrystr->used; i++) {
+		switch(qrystr->ptr[i]) {
+		case '=':
+			if (is_key) {
+				val = qrystr->ptr + i + 1;
+
+				qrystr->ptr[i] = '\0';
+
+				is_key = 0;
+			}
+
+			break;
+		case '&':
+		case '\0': /* fin symbol */
+			if (!is_key) {
+				/* we need at least a = since the last & */
+
+				/* terminate the value */
+				qrystr->ptr[i] = '\0';
+
+				c_to_lua_push(L, tbl,
+					      key, strlen(key),
+					      val, strlen(val));
+			}
+
+			key = qrystr->ptr + i + 1;
+			val = NULL;
+			is_key = 1;
+			break;
+		}
+	}
+
+	return 0;
+}
+#if 0
+int cache_export_cookie_params(server *srv, connection *con, plugin_data *p) {
+	data_unset *d;
+
+	UNUSED(srv);
+
+	if (NULL != (d = array_get_element(con->request.headers, "Cookie"))) {
+		data_string *ds = (data_string *)d;
+		size_t key = 0, value = 0;
+		size_t is_key = 1, is_sid = 0;
+		size_t i;
+
+		/* found COOKIE */
+		if (!DATA_IS_STRING(d)) return -1;
+		if (ds->value->used == 0) return -1;
+
+		if (ds->value->ptr[0] == '\0' ||
+		    ds->value->ptr[0] == '=' ||
+		    ds->value->ptr[0] == ';') return -1;
+
+		buffer_reset(p->session_id);
+		for (i = 0; i < ds->value->used; i++) {
+			switch(ds->value->ptr[i]) {
+			case '=':
+				if (is_key) {
+					if (0 == strncmp(ds->value->ptr + key, "PHPSESSID", i - key)) {
+						/* found PHP-session-id-key */
+						is_sid = 1;
+					}
+					value = i + 1;
+
+					is_key = 0;
+				}
+
+				break;
+			case ';':
+				if (is_sid) {
+					buffer_copy_string_len(p->session_id, ds->value->ptr + value, i - value);
+				}
+
+				is_sid = 0;
+				key = i + 1;
+				value = 0;
+				is_key = 1;
+				break;
+			case ' ':
+				if (is_key == 1 && key == i) key = i + 1;
+				if (is_key == 0 && value == i) value = i + 1;
+				break;
+			case '\0':
+				if (is_sid) {
+					buffer_copy_string_len(p->session_id, ds->value->ptr + value, i - value);
+				}
+				/* fin */
+				break;
+			}
+		}
+	}
+
+	return 0;
+}
+#endif
+
+int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) {
+	lua_State *L;
+	readme rm;
+	int ret = -1;
+	buffer *b = buffer_init();
+	int header_tbl = 0;
+
+	rm.done = 0;
+	stream_open(&rm.st, fn);
+
+	/* push the lua file to the interpreter and see what happends */
+	L = luaL_newstate();
+	luaL_openlibs(L);
+
+	/* register functions */
+	lua_register(L, "md5", f_crypto_md5);
+	lua_register(L, "file_mtime", f_file_mtime);
+	lua_register(L, "file_isreg", f_file_isreg);
+	lua_register(L, "file_isdir", f_file_isreg);
+	lua_register(L, "dir_files", f_dir_files);
+
+#ifdef HAVE_MEMCACHE_H
+	lua_pushliteral(L, "memcache_get_long");
+	lua_pushlightuserdata(L, p->conf.mc);
+	lua_pushcclosure(L, f_memcache_get_long, 1);
+	lua_settable(L, LUA_GLOBALSINDEX);
+
+	lua_pushliteral(L, "memcache_get_string");
+	lua_pushlightuserdata(L, p->conf.mc);
+	lua_pushcclosure(L, f_memcache_get_string, 1);
+	lua_settable(L, LUA_GLOBALSINDEX);
+
+	lua_pushliteral(L, "memcache_exists");
+	lua_pushlightuserdata(L, p->conf.mc);
+	lua_pushcclosure(L, f_memcache_exists, 1);
+	lua_settable(L, LUA_GLOBALSINDEX);
+#endif
+	/* register CGI environment */
+	lua_pushliteral(L, "request");
+	lua_newtable(L);
+	lua_settable(L, LUA_GLOBALSINDEX);
+
+	lua_pushliteral(L, "request");
+	header_tbl = lua_gettop(L);
+	lua_gettable(L, LUA_GLOBALSINDEX);
+
+	c_to_lua_push(L, header_tbl, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
+	c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
+	c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path));
+	c_to_lua_push(L, header_tbl, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.doc_root));
+	if (!buffer_is_empty(con->request.pathinfo)) {
+		c_to_lua_push(L, header_tbl, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
+	}
+
+	c_to_lua_push(L, header_tbl, CONST_STR_LEN("CWD"), CONST_BUF_LEN(p->basedir));
+	c_to_lua_push(L, header_tbl, CONST_STR_LEN("BASEURL"), CONST_BUF_LEN(p->baseurl));
+
+	/* register GET parameter */
+	lua_pushliteral(L, "get");
+	lua_newtable(L);
+	lua_settable(L, LUA_GLOBALSINDEX);
+
+	lua_pushliteral(L, "get");
+	header_tbl = lua_gettop(L);
+	lua_gettable(L, LUA_GLOBALSINDEX);
+
+	buffer_copy_string_buffer(b, con->uri.query);
+	cache_export_get_params(L, header_tbl, b);
+	buffer_reset(b);
+
+	/* 2 default constants */
+	lua_pushliteral(L, "CACHE_HIT");
+	lua_pushboolean(L, 0);
+	lua_settable(L, LUA_GLOBALSINDEX);
+
+	lua_pushliteral(L, "CACHE_MISS");
+	lua_pushboolean(L, 1);
+	lua_settable(L, LUA_GLOBALSINDEX);
+
+	/* load lua program */
+	if (lua_load(L, load_file, &rm, fn->ptr) || lua_pcall(L,0,1,0)) {
+		log_error_write(srv, __FILE__, __LINE__, "s",
+				lua_tostring(L,-1));
+
+		goto error;
+	}
+
+	/* get return value */
+	ret = (int)lua_tonumber(L, -1);
+	lua_pop(L, 1);
+
+	/* fetch the data from lua */
+	lua_to_c_get_string(L, "trigger_handler", p->trigger_handler);
+
+	if (0 == lua_to_c_get_string(L, "output_contenttype", b)) {
+		response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(b));
+	}
+
+	if (ret == 0) {
+		/* up to now it is a cache-hit, check if all files exist */
+
+		int curelem;
+		time_t mtime = 0;
+
+		if (!lua_to_c_is_table(L, "output_include")) {
+			log_error_write(srv, __FILE__, __LINE__, "s",
+				"output_include is missing or not a table");
+			ret = -1;
+
+			goto error;
+		}
+
+		lua_pushstring(L, "output_include");
+
+		curelem = lua_gettop(L);
+		lua_gettable(L, LUA_GLOBALSINDEX);
+
+		/* HOW-TO build a etag ?
+		 * as we don't just have one file we have to take the stat()
+		 * from all base files, merge them and build the etag from
+		 * it later.
+		 *
+		 * The mtime of the content is the mtime of the freshest base file
+		 *
+		 * */
+
+		lua_pushnil(L);  /* first key */
+		while (lua_next(L, curelem) != 0) {
+			stat_cache_entry *sce = NULL;
+			/* key' is at index -2 and value' at index -1 */
+
+			if (lua_isstring(L, -1)) {
+				const char *s = lua_tostring(L, -1);
+
+				/* the file is relative, make it absolute */
+				if (s[0] != '/') {
+					buffer_copy_string_buffer(b, p->basedir);
+					buffer_append_string(b, lua_tostring(L, -1));
+				} else {
+					buffer_copy_string(b, lua_tostring(L, -1));
+				}
+
+				if (HANDLER_ERROR == stat_cache_get_entry(srv, con, b, &sce)) {
+					/* stat failed */
+
+					switch(errno) {
+					case ENOENT:
+						/* a file is missing, call the handler to generate it */
+						if (!buffer_is_empty(p->trigger_handler)) {
+							ret = 1; /* cache-miss */
+
+							log_error_write(srv, __FILE__, __LINE__, "s",
+									"a file is missing, calling handler");
+
+							break;
+						} else {
+							/* handler not set -> 500 */
+							ret = -1;
+
+							log_error_write(srv, __FILE__, __LINE__, "s",
+									"a file missing and no handler set");
+
+							break;
+						}
+						break;
+					default:
+						break;
+					}
+				} else {
+					chunkqueue_append_file(con->write_queue, b, 0, sce->st.st_size);
+					if (sce->st.st_mtime > mtime) mtime = sce->st.st_mtime;
+				}
+			} else {
+				/* not a string */
+				ret = -1;
+				log_error_write(srv, __FILE__, __LINE__, "s",
+						"not a string");
+				break;
+			}
+
+			lua_pop(L, 1);  /* removes value'; keeps key' for next iteration */
+		}
+
+		lua_settop(L, curelem - 1);
+
+		if (ret == 0) {
+			data_string *ds;
+			char timebuf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
+			buffer tbuf;
+
+			con->file_finished = 1;
+
+			ds = (data_string *)array_get_element(con->response.headers, "Last-Modified");
+
+			/* no Last-Modified specified */
+			if ((mtime) && (NULL == ds)) {
+
+				strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime));
+
+				response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), timebuf, sizeof(timebuf) - 1);
+
+
+				tbuf.ptr = timebuf;
+				tbuf.used = sizeof(timebuf);
+				tbuf.size = sizeof(timebuf);
+			} else if (ds) {
+				tbuf.ptr = ds->value->ptr;
+				tbuf.used = ds->value->used;
+				tbuf.size = ds->value->size;
+			} else {
+				tbuf.size = 0;
+				tbuf.used = 0;
+				tbuf.ptr = NULL;
+			}
+
+			if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, &tbuf)) {
+				/* ok, the client already has our content,
+				 * no need to send it again */
+
+				chunkqueue_reset(con->write_queue);
+				ret = 0; /* cache-hit */
+			}
+		} else {
+			chunkqueue_reset(con->write_queue);
+		}
+	}
+
+	if (ret == 1 && !buffer_is_empty(p->trigger_handler)) {
+		/* cache-miss */
+		buffer_copy_string_buffer(con->uri.path, p->baseurl);
+		buffer_append_string_buffer(con->uri.path, p->trigger_handler);
+
+		buffer_copy_string_buffer(con->physical.path, p->basedir);
+		buffer_append_string_buffer(con->physical.path, p->trigger_handler);
+
+		chunkqueue_reset(con->write_queue);
+	}
+
+error:
+	lua_close(L);
+
+	stream_close(&rm.st);
+	buffer_free(b);
+
+	return ret /* cache-error */;
+}
+#else
+int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) {
+	UNUSED(srv);
+	UNUSED(con);
+	UNUSED(p);
+	UNUSED(fn);
+	/* error */
+	return -1;
+}
+#endif

Added: webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_compress.c
URL: http://svn.apache.org/viewvc/webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_compress.c?rev=678637&view=auto
==============================================================================
--- webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_compress.c (added)
+++ webservices/axis2/branches/c/lighttpd_mod_axis2/lighttpd/src/mod_compress.c Mon Jul 21 21:35:35 2008
@@ -0,0 +1,768 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+
+#include "base.h"
+#include "log.h"
+#include "buffer.h"
+#include "response.h"
+#include "stat_cache.h"
+
+#include "plugin.h"
+
+#include "crc32.h"
+#include "etag.h"
+
+#if defined HAVE_ZLIB_H && defined HAVE_LIBZ
+# define USE_ZLIB
+# include <zlib.h>
+#endif
+
+#if defined HAVE_BZLIB_H && defined HAVE_LIBBZ2
+# define USE_BZ2LIB
+/* we don't need stdio interface */
+# define BZ_NO_STDIO
+# include <bzlib.h>
+#endif
+
+#include "sys-mmap.h"
+
+/* request: accept-encoding */
+#define HTTP_ACCEPT_ENCODING_IDENTITY BV(0)
+#define HTTP_ACCEPT_ENCODING_GZIP     BV(1)
+#define HTTP_ACCEPT_ENCODING_DEFLATE  BV(2)
+#define HTTP_ACCEPT_ENCODING_COMPRESS BV(3)
+#define HTTP_ACCEPT_ENCODING_BZIP2    BV(4)
+
+#ifdef __WIN32
+#define mkdir(x,y) mkdir(x)
+#endif
+
+typedef struct {
+	buffer *compress_cache_dir;
+	array  *compress;
+	off_t   compress_max_filesize; /** max filesize in kb */
+} plugin_config;
+
+typedef struct {
+	PLUGIN_DATA;
+	buffer *ofn;
+	buffer *b;
+
+	plugin_config **config_storage;
+	plugin_config conf;
+} plugin_data;
+
+INIT_FUNC(mod_compress_init) {
+	plugin_data *p;
+
+	p = calloc(1, sizeof(*p));
+
+	p->ofn = buffer_init();
+	p->b = buffer_init();
+
+	return p;
+}
+
+FREE_FUNC(mod_compress_free) {
+	plugin_data *p = p_d;
+
+	UNUSED(srv);
+
+	if (!p) return HANDLER_GO_ON;
+
+	buffer_free(p->ofn);
+	buffer_free(p->b);
+
+	if (p->config_storage) {
+		size_t i;
+		for (i = 0; i < srv->config_context->used; i++) {
+			plugin_config *s = p->config_storage[i];
+
+			if (!s) continue;
+
+			array_free(s->compress);
+			buffer_free(s->compress_cache_dir);
+
+			free(s);
+		}
+		free(p->config_storage);
+	}
+
+
+	free(p);
+
+	return HANDLER_GO_ON;
+}
+
+// 0 on success, -1 for error
+int mkdir_recursive(char *dir) {
+	char *p = dir;
+
+	if (!dir || !dir[0])
+		return 0;
+
+	while ((p = strchr(p + 1, '/')) != NULL) {
+
+		*p = '\0';
+		if ((mkdir(dir, 0700) != 0) && (errno != EEXIST)) {
+			*p = '/';
+			return -1;
+		}
+
+		*p++ = '/';
+		if (!*p) return 0; // Ignore trailing slash
+	}
+
+	return (mkdir(dir, 0700) != 0) && (errno != EEXIST) ? -1 : 0;
+}
+
+// 0 on success, -1 for error
+int mkdir_for_file(char *filename) {
+	char *p = filename;
+
+	if (!filename || !filename[0])
+		return -1;
+
+	while ((p = strchr(p + 1, '/')) != NULL) {
+
+		*p = '\0';
+		if ((mkdir(filename, 0700) != 0) && (errno != EEXIST)) {
+			*p = '/';
+			return -1;
+		}
+
+		*p++ = '/';
+		if (!*p) return -1; // Unexpected trailing slash in filename
+	}
+
+	return 0;
+}
+
+SETDEFAULTS_FUNC(mod_compress_setdefaults) {
+	plugin_data *p = p_d;
+	size_t i = 0;
+
+	config_values_t cv[] = {
+		{ "compress.cache-dir",             NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
+		{ "compress.filetype",              NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },
+		{ "compress.max-filesize",          NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },
+		{ NULL,                             NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
+	};
+
+	p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
+
+	for (i = 0; i < srv->config_context->used; i++) {
+		plugin_config *s;
+
+		s = calloc(1, sizeof(plugin_config));
+		s->compress_cache_dir = buffer_init();
+		s->compress = array_init();
+		s->compress_max_filesize = 0;
+
+		cv[0].destination = s->compress_cache_dir;
+		cv[1].destination = s->compress;
+		cv[2].destination = &(s->compress_max_filesize);
+
+		p->config_storage[i] = s;
+
+		if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
+			return HANDLER_ERROR;
+		}
+
+		if (!buffer_is_empty(s->compress_cache_dir)) {
+			mkdir_recursive(s->compress_cache_dir->ptr);
+
+			struct stat st;
+			if (0 != stat(s->compress_cache_dir->ptr, &st)) {
+				log_error_write(srv, __FILE__, __LINE__, "sbs", "can't stat compress.cache-dir",
+						s->compress_cache_dir, strerror(errno));
+
+				return HANDLER_ERROR;
+			}
+		}
+	}
+
+	return HANDLER_GO_ON;
+
+}
+
+#ifdef USE_ZLIB
+static int deflate_file_to_buffer_gzip(server *srv, connection *con, plugin_data *p, char *start, off_t st_size, time_t mtime) {
+	unsigned char *c;
+	unsigned long crc;
+	z_stream z;
+
+	UNUSED(srv);
+	UNUSED(con);
+
+	z.zalloc = Z_NULL;
+	z.zfree = Z_NULL;
+	z.opaque = Z_NULL;
+
+	if (Z_OK != deflateInit2(&z,
+				 Z_DEFAULT_COMPRESSION,
+				 Z_DEFLATED,
+				 -MAX_WBITS,  /* supress zlib-header */
+				 8,
+				 Z_DEFAULT_STRATEGY)) {
+		return -1;
+	}
+
+	z.next_in = (unsigned char *)start;
+	z.avail_in = st_size;
+	z.total_in = 0;
+
+
+	buffer_prepare_copy(p->b, (z.avail_in * 1.1) + 12 + 18);
+
+	/* write gzip header */
+
+	c = (unsigned char *)p->b->ptr;
+	c[0] = 0x1f;
+	c[1] = 0x8b;
+	c[2] = Z_DEFLATED;
+	c[3] = 0; /* options */
+	c[4] = (mtime >>  0) & 0xff;
+	c[5] = (mtime >>  8) & 0xff;
+	c[6] = (mtime >> 16) & 0xff;
+	c[7] = (mtime >> 24) & 0xff;
+	c[8] = 0x00; /* extra flags */
+	c[9] = 0x03; /* UNIX */
+
+	p->b->used = 10;
+	z.next_out = (unsigned char *)p->b->ptr + p->b->used;
+	z.avail_out = p->b->size - p->b->used - 8;
+	z.total_out = 0;
+
+	if (Z_STREAM_END != deflate(&z, Z_FINISH)) {
+		deflateEnd(&z);
+		return -1;
+	}
+
+	/* trailer */
+	p->b->used += z.total_out;
+
+	crc = generate_crc32c(start, st_size);
+
+	c = (unsigned char *)p->b->ptr + p->b->used;
+
+	c[0] = (crc >>  0) & 0xff;
+	c[1] = (crc >>  8) & 0xff;
+	c[2] = (crc >> 16) & 0xff;
+	c[3] = (crc >> 24) & 0xff;
+	c[4] = (z.total_in >>  0) & 0xff;
+	c[5] = (z.total_in >>  8) & 0xff;
+	c[6] = (z.total_in >> 16) & 0xff;
+	c[7] = (z.total_in >> 24) & 0xff;
+	p->b->used += 8;
+
+	if (Z_OK != deflateEnd(&z)) {
+		return -1;
+	}
+
+	return 0;
+}
+
+static int deflate_file_to_buffer_deflate(server *srv, connection *con, plugin_data *p, unsigned char *start, off_t st_size) {
+	z_stream z;
+
+	UNUSED(srv);
+	UNUSED(con);
+
+	z.zalloc = Z_NULL;
+	z.zfree = Z_NULL;
+	z.opaque = Z_NULL;
+
+	if (Z_OK != deflateInit2(&z,
+				 Z_DEFAULT_COMPRESSION,
+				 Z_DEFLATED,
+				 -MAX_WBITS,  /* supress zlib-header */
+				 8,
+				 Z_DEFAULT_STRATEGY)) {
+		return -1;
+	}
+
+	z.next_in = start;
+	z.avail_in = st_size;
+	z.total_in = 0;
+
+	buffer_prepare_copy(p->b, (z.avail_in * 1.1) + 12);
+
+	z.next_out = (unsigned char *)p->b->ptr;
+	z.avail_out = p->b->size;
+	z.total_out = 0;
+
+	if (Z_STREAM_END != deflate(&z, Z_FINISH)) {
+		deflateEnd(&z);
+		return -1;
+	}
+
+	/* trailer */
+	p->b->used += z.total_out;
+
+	if (Z_OK != deflateEnd(&z)) {
+		return -1;
+	}
+
+	return 0;
+}
+
+#endif
+
+#ifdef USE_BZ2LIB
+static int deflate_file_to_buffer_bzip2(server *srv, connection *con, plugin_data *p, unsigned char *start, off_t st_size) {
+	bz_stream bz;
+
+	UNUSED(srv);
+	UNUSED(con);
+
+	bz.bzalloc = NULL;
+	bz.bzfree = NULL;
+	bz.opaque = NULL;
+
+	if (BZ_OK != BZ2_bzCompressInit(&bz,
+					9, /* blocksize = 900k */
+					0, /* no output */
+					0)) { /* workFactor: default */
+		return -1;
+	}
+
+	bz.next_in = (char *)start;
+	bz.avail_in = st_size;
+	bz.total_in_lo32 = 0;
+	bz.total_in_hi32 = 0;
+
+	buffer_prepare_copy(p->b, (bz.avail_in * 1.1) + 12);
+
+	bz.next_out = p->b->ptr;
+	bz.avail_out = p->b->size;
+	bz.total_out_lo32 = 0;
+	bz.total_out_hi32 = 0;
+
+	if (BZ_STREAM_END != BZ2_bzCompress(&bz, BZ_FINISH)) {
+		BZ2_bzCompressEnd(&bz);
+		return -1;
+	}
+
+	/* file is too large for now */
+	if (bz.total_out_hi32) return -1;
+
+	/* trailer */
+	p->b->used = bz.total_out_lo32;
+
+	if (BZ_OK != BZ2_bzCompressEnd(&bz)) {
+		return -1;
+	}
+
+	return 0;
+}
+#endif
+
+static int deflate_file_to_file(server *srv, connection *con, plugin_data *p, buffer *fn, stat_cache_entry *sce, int type) {
+	int ifd, ofd;
+	int ret = -1;
+	void *start;
+	const char *filename = fn->ptr;
+	ssize_t r;
+
+	/* overflow */
+	if ((off_t)(sce->st.st_size * 1.1) < sce->st.st_size) return -1;
+
+	/* don't mmap files > 128Mb
+	 *
+	 * we could use a sliding window, but currently there is no need for it
+	 */
+
+	if (sce->st.st_size > 128 * 1024 * 1024) return -1;
+
+	buffer_reset(p->ofn);
+	buffer_copy_string_buffer(p->ofn, p->conf.compress_cache_dir);
+	BUFFER_APPEND_SLASH(p->ofn);
+
+	if (0 == strncmp(con->physical.path->ptr, con->physical.doc_root->ptr, con->physical.doc_root->used-1)) {
+		buffer_append_string(p->ofn, con->physical.path->ptr + con->physical.doc_root->used - 1);
+		buffer_copy_string_buffer(p->b, p->ofn);
+	} else {
+		buffer_append_string_buffer(p->ofn, con->uri.path);
+	}
+
+	switch(type) {
+	case HTTP_ACCEPT_ENCODING_GZIP:
+		buffer_append_string(p->ofn, "-gzip-");
+		break;
+	case HTTP_ACCEPT_ENCODING_DEFLATE:
+		buffer_append_string(p->ofn, "-deflate-");
+		break;
+	case HTTP_ACCEPT_ENCODING_BZIP2:
+		buffer_append_string(p->ofn, "-bzip2-");
+		break;
+	default:
+		log_error_write(srv, __FILE__, __LINE__, "sd", "unknown compression type", type);
+		return -1;
+	}
+
+	buffer_append_string_buffer(p->ofn, sce->etag);
+
+	if (-1 == mkdir_for_file(p->ofn->ptr)) {
+		log_error_write(srv, __FILE__, __LINE__, "sb", "couldn't create directory for file", p->ofn);
+		return -1;
+	}
+
+	if (-1 == (ofd = open(p->ofn->ptr, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0600))) {
+		if (errno == EEXIST) {
+			/* cache-entry exists */
+#if 0
+			log_error_write(srv, __FILE__, __LINE__, "bs", p->ofn, "compress-cache hit");
+#endif
+			buffer_copy_string_buffer(con->physical.path, p->ofn);
+
+			return 0;
+		}
+
+		log_error_write(srv, __FILE__, __LINE__, "sbss", "creating cachefile", p->ofn, "failed", strerror(errno));
+
+		return -1;
+	}
+#if 0
+	log_error_write(srv, __FILE__, __LINE__, "bs", p->ofn, "compress-cache miss");
+#endif
+	if (-1 == (ifd = open(filename, O_RDONLY | O_BINARY))) {
+		log_error_write(srv, __FILE__, __LINE__, "sbss", "opening plain-file", fn, "failed", strerror(errno));
+
+		close(ofd);
+
+		/* Remove the incomplete cache file, so that later hits aren't served from it */
+		if (-1 == unlink(p->ofn->ptr)) {
+			log_error_write(srv, __FILE__, __LINE__, "sbss", "unlinking incomplete cachefile", p->ofn, "failed:", strerror(errno));
+		}
+
+		return -1;
+	}
+
+
+	if (MAP_FAILED == (start = mmap(NULL, sce->st.st_size, PROT_READ, MAP_SHARED, ifd, 0))) {
+		log_error_write(srv, __FILE__, __LINE__, "sbss", "mmaping", fn, "failed", strerror(errno));
+
+		close(ofd);
+		close(ifd);
+
+		/* Remove the incomplete cache file, so that later hits aren't served from it */
+		if (-1 == unlink(p->ofn->ptr)) {
+			log_error_write(srv, __FILE__, __LINE__, "sbss", "unlinking incomplete cachefile", p->ofn, "failed:", strerror(errno));
+		}
+
+		return -1;
+	}
+
+	switch(type) {
+#ifdef USE_ZLIB
+	case HTTP_ACCEPT_ENCODING_GZIP:
+		ret = deflate_file_to_buffer_gzip(srv, con, p, start, sce->st.st_size, sce->st.st_mtime);
+		break;
+	case HTTP_ACCEPT_ENCODING_DEFLATE:
+		ret = deflate_file_to_buffer_deflate(srv, con, p, start, sce->st.st_size);
+		break;
+#endif
+#ifdef USE_BZ2LIB
+	case HTTP_ACCEPT_ENCODING_BZIP2:
+		ret = deflate_file_to_buffer_bzip2(srv, con, p, start, sce->st.st_size);
+		break;
+#endif
+	default:
+		ret = -1;
+		break;
+	}
+
+	if (ret == 0) {
+		r = write(ofd, p->b->ptr, p->b->used);
+		if (-1 == r) {
+			log_error_write(srv, __FILE__, __LINE__, "sbss", "writing cachefile", p->ofn, "failed:", strerror(errno));
+			ret = -1;
+		} else if ((size_t)r != p->b->used) {
+			log_error_write(srv, __FILE__, __LINE__, "sbs", "writing cachefile", p->ofn, "failed: not enough bytes written");
+			ret = -1;
+		}
+	}
+
+	munmap(start, sce->st.st_size);
+	close(ofd);
+	close(ifd);
+
+	if (ret != 0) {
+		/* Remove the incomplete cache file, so that later hits aren't served from it */
+		if (-1 == unlink(p->ofn->ptr)) {
+			log_error_write(srv, __FILE__, __LINE__, "sbss", "unlinking incomplete cachefile", p->ofn, "failed:", strerror(errno));
+		}
+
+		return -1;
+	}
+
+	buffer_copy_string_buffer(con->physical.path, p->ofn);
+
+	return 0;
+}
+
+static int deflate_file_to_buffer(server *srv, connection *con, plugin_data *p, buffer *fn, stat_cache_entry *sce, int type) {
+	int ifd;
+	int ret = -1;
+	void *start;
+	buffer *b;
+
+	/* overflow */
+	if ((off_t)(sce->st.st_size * 1.1) < sce->st.st_size) return -1;
+
+	/* don't mmap files > 128M
+	 *
+	 * we could use a sliding window, but currently there is no need for it
+	 */
+
+	if (sce->st.st_size > 128 * 1024 * 1024) return -1;
+
+
+	if (-1 == (ifd = open(fn->ptr, O_RDONLY | O_BINARY))) {
+		log_error_write(srv, __FILE__, __LINE__, "sbss", "opening plain-file", fn, "failed", strerror(errno));
+
+		return -1;
+	}
+
+
+	if (MAP_FAILED == (start = mmap(NULL, sce->st.st_size, PROT_READ, MAP_SHARED, ifd, 0))) {
+		log_error_write(srv, __FILE__, __LINE__, "sbss", "mmaping", fn, "failed", strerror(errno));
+
+		close(ifd);
+		return -1;
+	}
+
+	switch(type) {
+#ifdef USE_ZLIB
+	case HTTP_ACCEPT_ENCODING_GZIP:
+		ret = deflate_file_to_buffer_gzip(srv, con, p, start, sce->st.st_size, sce->st.st_mtime);
+		break;
+	case HTTP_ACCEPT_ENCODING_DEFLATE:
+		ret = deflate_file_to_buffer_deflate(srv, con, p, start, sce->st.st_size);
+		break;
+#endif
+#ifdef USE_BZ2LIB
+	case HTTP_ACCEPT_ENCODING_BZIP2:
+		ret = deflate_file_to_buffer_bzip2(srv, con, p, start, sce->st.st_size);
+		break;
+#endif
+	default:
+		ret = -1;
+		break;
+	}
+
+	munmap(start, sce->st.st_size);
+	close(ifd);
+
+	if (ret != 0) return -1;
+
+	chunkqueue_reset(con->write_queue);
+	b = chunkqueue_get_append_buffer(con->write_queue);
+	buffer_copy_memory(b, p->b->ptr, p->b->used + 1);
+
+	buffer_reset(con->physical.path);
+
+	con->file_finished = 1;
+	con->file_started  = 1;
+
+	return 0;
+}
+
+
+#define PATCH(x) \
+	p->conf.x = s->x;
+static int mod_compress_patch_connection(server *srv, connection *con, plugin_data *p) {
+	size_t i, j;
+	plugin_config *s = p->config_storage[0];
+
+	PATCH(compress_cache_dir);
+	PATCH(compress);
+	PATCH(compress_max_filesize);
+
+	/* skip the first, the global context */
+	for (i = 1; i < srv->config_context->used; i++) {
+		data_config *dc = (data_config *)srv->config_context->data[i];
+		s = p->config_storage[i];
+
+		/* condition didn't match */
+		if (!config_check_cond(srv, con, dc)) continue;
+
+		/* merge config */
+		for (j = 0; j < dc->value->used; j++) {
+			data_unset *du = dc->value->data[j];
+
+			if (buffer_is_equal_string(du->key, CONST_STR_LEN("compress.cache-dir"))) {
+				PATCH(compress_cache_dir);
+			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("compress.filetype"))) {
+				PATCH(compress);
+			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("compress.max-filesize"))) {
+				PATCH(compress_max_filesize);
+			}
+		}
+	}
+
+	return 0;
+}
+#undef PATCH
+
+PHYSICALPATH_FUNC(mod_compress_physical) {
+	plugin_data *p = p_d;
+	size_t m;
+	off_t max_fsize;
+	stat_cache_entry *sce = NULL;
+
+	if (con->mode != DIRECT || con->http_status) return HANDLER_GO_ON;
+
+	/* only GET and POST can get compressed */
+	if (con->request.http_method != HTTP_METHOD_GET &&
+	    con->request.http_method != HTTP_METHOD_POST) {
+		return HANDLER_GO_ON;
+	}
+
+	if (buffer_is_empty(con->physical.path)) {
+		return HANDLER_GO_ON;
+	}
+
+	mod_compress_patch_connection(srv, con, p);
+
+	max_fsize = p->conf.compress_max_filesize;
+
+	stat_cache_get_entry(srv, con, con->physical.path, &sce);
+
+	/* don't compress files that are too large as we need to much time to handle them */
+	if (max_fsize && (sce->st.st_size >> 10) > max_fsize) return HANDLER_GO_ON;
+
+	/* don't try to compress files less than 128 bytes
+	 *
+	 * - extra overhead for compression
+	 * - mmap() fails for st_size = 0 :)
+	 */
+	if (sce->st.st_size < 128) return HANDLER_GO_ON;
+
+	/* check if mimetype is in compress-config */
+	for (m = 0; m < p->conf.compress->used; m++) {
+		data_string *compress_ds = (data_string *)p->conf.compress->data[m];
+
+		if (!compress_ds) {
+			log_error_write(srv, __FILE__, __LINE__, "sbb", "evil", con->physical.path, con->uri.path);
+
+			return HANDLER_GO_ON;
+		}
+
+		if (buffer_is_equal(compress_ds->value, sce->content_type)) {
+			/* mimetype found */
+			data_string *ds;
+
+			/* the response might change according to Accept-Encoding */
+			response_header_insert(srv, con, CONST_STR_LEN("Vary"), CONST_STR_LEN("Accept-Encoding"));
+
+			if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Accept-Encoding"))) {
+				int accept_encoding = 0;
+				char *value = ds->value->ptr;
+				int srv_encodings = 0;
+				int matched_encodings = 0;
+
+				/* get client side support encodings */
+				if (NULL != strstr(value, "gzip")) accept_encoding |= HTTP_ACCEPT_ENCODING_GZIP;
+				if (NULL != strstr(value, "deflate")) accept_encoding |= HTTP_ACCEPT_ENCODING_DEFLATE;
+				if (NULL != strstr(value, "compress")) accept_encoding |= HTTP_ACCEPT_ENCODING_COMPRESS;
+				if (NULL != strstr(value, "bzip2")) accept_encoding |= HTTP_ACCEPT_ENCODING_BZIP2;
+				if (NULL != strstr(value, "identity")) accept_encoding |= HTTP_ACCEPT_ENCODING_IDENTITY;
+
+				/* get server side supported ones */
+#ifdef USE_BZ2LIB
+				srv_encodings |= HTTP_ACCEPT_ENCODING_BZIP2;
+#endif
+#ifdef USE_ZLIB
+				srv_encodings |= HTTP_ACCEPT_ENCODING_GZIP;
+				srv_encodings |= HTTP_ACCEPT_ENCODING_DEFLATE;
+#endif
+
+				/* find matching entries */
+				matched_encodings = accept_encoding & srv_encodings;
+
+				if (matched_encodings) {
+					const char *dflt_gzip = "gzip";
+					const char *dflt_deflate = "deflate";
+					const char *dflt_bzip2 = "bzip2";
+
+					const char *compression_name = NULL;
+					int compression_type = 0;
+
+					/* select best matching encoding */
+					if (matched_encodings & HTTP_ACCEPT_ENCODING_BZIP2) {
+						compression_type = HTTP_ACCEPT_ENCODING_BZIP2;
+						compression_name = dflt_bzip2;
+					} else if (matched_encodings & HTTP_ACCEPT_ENCODING_GZIP) {
+						compression_type = HTTP_ACCEPT_ENCODING_GZIP;
+						compression_name = dflt_gzip;
+					} else if (matched_encodings & HTTP_ACCEPT_ENCODING_DEFLATE) {
+						compression_type = HTTP_ACCEPT_ENCODING_DEFLATE;
+						compression_name = dflt_deflate;
+					}
+
+					/* deflate it */
+					if (p->conf.compress_cache_dir->used) {
+						if (0 == deflate_file_to_file(srv, con, p,
+									      con->physical.path, sce, compression_type)) {
+							buffer *mtime;
+
+							response_header_overwrite(srv, con, CONST_STR_LEN("Content-Encoding"), compression_name, strlen(compression_name));
+
+							mtime = strftime_cache_get(srv, sce->st.st_mtime);
+							response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime));
+
+							etag_mutate(con->physical.etag, sce->etag);
+							response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag));
+
+							response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type));
+
+							return HANDLER_GO_ON;
+						}
+					} else if (0 == deflate_file_to_buffer(srv, con, p,
+									       con->physical.path, sce, compression_type)) {
+						buffer *mtime;
+
+						response_header_overwrite(srv, con, CONST_STR_LEN("Content-Encoding"), compression_name, strlen(compression_name));
+
+						mtime = strftime_cache_get(srv, sce->st.st_mtime);
+						response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime));
+
+						etag_mutate(con->physical.etag, sce->etag);
+						response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag));
+
+						response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type));
+
+						return HANDLER_FINISHED;
+					}
+					break;
+				}
+			}
+		}
+	}
+
+	return HANDLER_GO_ON;
+}
+
+int mod_compress_plugin_init(plugin *p) {
+	p->version     = LIGHTTPD_VERSION_ID;
+	p->name        = buffer_init_string("compress");
+
+	p->init        = mod_compress_init;
+	p->set_defaults = mod_compress_setdefaults;
+	p->handle_subrequest_start  = mod_compress_physical;
+	p->cleanup     = mod_compress_free;
+
+	p->data        = NULL;
+
+	return 0;
+}