lighttpd1.4/src/mod_cml.c

314 lines
8.5 KiB
C
Raw Normal View History

#include "first.h"
#include "mod_cml.h"
#include "base.h"
#include "buffer.h"
#include "log.h"
#include "plugin.h"
#include <sys/stat.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
INIT_FUNC(mod_cml_init) {
return calloc(1, sizeof(plugin_data));
}
FREE_FUNC(mod_cml_free) {
plugin_data * const p = p_d;
free(p->trigger_handler.ptr);
free(p->basedir.ptr);
free(p->baseurl.ptr);
if (NULL == p->cvlist) return;
#if defined(USE_MEMCACHED)
/* (init i to 0 if global context; to 1 to skip empty global context) */
for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
for (; -1 != cpv->k_id; ++cpv) {
switch (cpv->k_id) {
case 1: /* cml.memcache-hosts */
if (cpv->vtype == T_CONFIG_LOCAL && NULL != cpv->v.v)
memcached_free(cpv->v.v); /* mod_cml_free_memcached() */
break;
default:
break;
}
}
}
#endif
}
static int mod_cml_init_memcached(server *srv, config_plugin_value_t * const cpv) {
const array * const mc_hosts = cpv->v.a;
if (0 == mc_hosts->used) {
cpv->v.v = NULL;
return 1;
}
#if defined(USE_MEMCACHED)
buffer * const opts = srv->tmp_buf;
buffer_clear(opts);
for (uint32_t k = 0; k < mc_hosts->used; ++k) {
const data_string * const ds = (const data_string *)mc_hosts->data[k];
buffer_append_string_len(opts, CONST_STR_LEN(" --SERVER="));
buffer_append_string_buffer(opts, &ds->value);
}
cpv->v.v = memcached(opts->ptr+1, buffer_string_length(opts)-1);
if (cpv->v.v) {
cpv->vtype = T_CONFIG_LOCAL;
return 1;
}
else {
log_error(srv->errh, __FILE__, __LINE__,
"configuring memcached failed for option string: %s", opts->ptr);
return 0;
}
#else
log_error(srv->errh, __FILE__, __LINE__,
"memcache support is not compiled in but cml.memcache-hosts is set, "
"aborting");
return 0;
#endif
}
static void mod_cml_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) {
switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
case 0: /* cml.extension */
pconf->ext = cpv->v.b;
break;
case 1: /* cml.memcache-hosts *//* setdefaults inits memcached_st *memc */
#if defined(USE_MEMCACHED)
if (cpv->vtype != T_CONFIG_LOCAL) break;
pconf->memc = cpv->v.v;
#endif
break;
case 2: /* cml.memcache-namespace */
/*pconf->mc_namespace = cpv->v.b;*//*(unused)*/
break;
case 3: /* cml.power-magnet */
pconf->power_magnet = cpv->v.b;
break;
default:/* should not happen */
return;
}
}
static void mod_cml_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) {
do {
mod_cml_merge_config_cpv(pconf, cpv);
} while ((++cpv)->k_id != -1);
}
static void mod_cml_patch_config(request_st * const r, plugin_data * const p) {
memcpy(&p->conf, &p->defaults, sizeof(plugin_config));
for (int i = 1, used = p->nconfig; i < used; ++i) {
if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
mod_cml_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]);
}
}
SETDEFAULTS_FUNC(mod_cml_set_defaults) {
static const config_plugin_keys_t cpk[] = {
{ CONST_STR_LEN("cml.extension"),
T_CONFIG_STRING,
T_CONFIG_SCOPE_CONNECTION }
,{ CONST_STR_LEN("cml.memcache-hosts"),
T_CONFIG_ARRAY_VLIST,
T_CONFIG_SCOPE_CONNECTION }
,{ CONST_STR_LEN("cml.memcache-namespace"), /*(unused)*/
T_CONFIG_STRING,
T_CONFIG_SCOPE_CONNECTION }
,{ CONST_STR_LEN("cml.power-magnet"),
T_CONFIG_STRING,
T_CONFIG_SCOPE_CONNECTION }
,{ NULL, 0,
T_CONFIG_UNSET,
T_CONFIG_SCOPE_UNSET }
};
plugin_data * const p = p_d;
if (!config_plugin_values_init(srv, p, cpk, "mod_cml"))
return HANDLER_ERROR;
/* process and validate config directives
* (init i to 0 if global context; to 1 to skip empty global context) */
for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
for (; -1 != cpv->k_id; ++cpv) {
switch (cpv->k_id) {
case 0: /* cml.extension */
break;
case 1: /* cml.memcache-hosts */ /* config converted to memc handles */
if (!mod_cml_init_memcached(srv, cpv)) {
return HANDLER_ERROR;
}
break;
case 2: /* cml.memcache-namespace *//*(unused)*/
case 3: /* cml.power-magnet */
break;
default:/* should not happen */
break;
}
}
}
/* initialize p->defaults from global config context */
if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
if (-1 != cpv->k_id)
mod_cml_merge_config(&p->defaults, cpv);
}
return HANDLER_GO_ON;
}
static int cache_call_lua(request_st * const r, plugin_data * const p, const buffer * const cml_file) {
buffer *b;
char *c;
/* cleanup basedir */
b = &p->baseurl;
buffer_copy_buffer(b, &r->uri.path);
for (c = b->ptr + buffer_string_length(b); c > b->ptr && *c != '/'; c--);
if (*c == '/') {
buffer_string_set_length(b, c - b->ptr + 1);
}
b = &p->basedir;
buffer_copy_buffer(b, &r->physical.path);
for (c = b->ptr + buffer_string_length(b); c > b->ptr && *c != '/'; c--);
if (*c == '/') {
buffer_string_set_length(b, c - b->ptr + 1);
}
/* prepare variables
* - cookie-based
* - get-param-based
*/
return cache_parse_lua(r, p, cml_file);
}
URIHANDLER_FUNC(mod_cml_power_magnet) {
plugin_data *p = p_d;
mod_cml_patch_config(r, p);
fix buffer, chunk and http_chunk API * remove unused structs and functions (buffer_array, read_buffer) * change return type from int to void for many functions, as the return value (indicating error/success) was never checked, and the function would only fail on programming errors and not on invalid input; changed functions to use force_assert instead of returning an error. * all "len" parameters now are the real size of the memory to be read. the length of strings is given always without the terminating 0. * the "buffer" struct still counts the terminating 0 in ->used, provide buffer_string_length() to get the length of a string in a buffer. unset config "strings" have used == 0, which is used in some places to distinguish unset values from "" (empty string) values. * most buffer usages should now use it as string container. * optimise some buffer copying by "moving" data to other buffers * use (u)intmax_t for generic int-to-string functions * remove unused enum values: UNUSED_CHUNK, ENCODING_UNSET * converted BUFFER_APPEND_SLASH to inline function (no macro feature needed) * refactor: create chunkqueue_steal: moving (partial) chunks into another queue * http_chunk: added separate function to terminate chunked body instead of magic handling in http_chunk_append_mem(). http_chunk_append_* now handle empty chunks, and never terminate the chunked body. From: Stefan Bühler <stbuehler@web.de> git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@2975 152afb58-edef-0310-8abb-c4023f1b3aa9
2015-02-08 12:37:10 +00:00
if (buffer_string_is_empty(p->conf.power_magnet)) return HANDLER_GO_ON;
buffer_clear(&p->basedir);
buffer_clear(&p->baseurl);
buffer_clear(&p->trigger_handler);
/*
* 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 necessary.
*
* First use:
* if file_exists("/maintenance.html") {
* output_include = ( "/maintenance.html" )
* return CACHE_HIT
* }
*
* as we only want to rewrite HTML like requests we should cover it in a conditional
*
* */
switch(cache_call_lua(r, p, p->conf.power_magnet)) {
case -1:
/* error */
if (r->conf.log_request_handling) {
log_error(r->conf.errh, __FILE__, __LINE__, "cache-error");
}
r->http_status = 500;
return HANDLER_COMEBACK;
case 0:
if (r->conf.log_request_handling) {
log_error(r->conf.errh, __FILE__, __LINE__, "cache-hit");
}
/* cache-hit */
buffer_reset(&r->physical.path);
return HANDLER_FINISHED;
case 1:
/* cache miss */
return HANDLER_GO_ON;
default:
r->http_status = 500;
return HANDLER_COMEBACK;
}
}
URIHANDLER_FUNC(mod_cml_is_handled) {
plugin_data *p = p_d;
if (buffer_string_is_empty(&r->physical.path)) return HANDLER_ERROR;
mod_cml_patch_config(r, p);
fix buffer, chunk and http_chunk API * remove unused structs and functions (buffer_array, read_buffer) * change return type from int to void for many functions, as the return value (indicating error/success) was never checked, and the function would only fail on programming errors and not on invalid input; changed functions to use force_assert instead of returning an error. * all "len" parameters now are the real size of the memory to be read. the length of strings is given always without the terminating 0. * the "buffer" struct still counts the terminating 0 in ->used, provide buffer_string_length() to get the length of a string in a buffer. unset config "strings" have used == 0, which is used in some places to distinguish unset values from "" (empty string) values. * most buffer usages should now use it as string container. * optimise some buffer copying by "moving" data to other buffers * use (u)intmax_t for generic int-to-string functions * remove unused enum values: UNUSED_CHUNK, ENCODING_UNSET * converted BUFFER_APPEND_SLASH to inline function (no macro feature needed) * refactor: create chunkqueue_steal: moving (partial) chunks into another queue * http_chunk: added separate function to terminate chunked body instead of magic handling in http_chunk_append_mem(). http_chunk_append_* now handle empty chunks, and never terminate the chunked body. From: Stefan Bühler <stbuehler@web.de> git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@2975 152afb58-edef-0310-8abb-c4023f1b3aa9
2015-02-08 12:37:10 +00:00
if (buffer_string_is_empty(p->conf.ext)) return HANDLER_GO_ON;
if (!buffer_is_equal_right_len(&r->physical.path, p->conf.ext, buffer_string_length(p->conf.ext))) {
return HANDLER_GO_ON;
}
buffer_clear(&p->basedir);
buffer_clear(&p->baseurl);
buffer_clear(&p->trigger_handler);
switch(cache_call_lua(r, p, &r->physical.path)) {
case -1:
/* error */
if (r->conf.log_request_handling) {
log_error(r->conf.errh, __FILE__, __LINE__, "cache-error");
}
r->http_status = 500;
return HANDLER_COMEBACK;
case 0:
if (r->conf.log_request_handling) {
log_error(r->conf.errh, __FILE__, __LINE__, "cache-hit");
}
/* cache-hit */
buffer_reset(&r->physical.path);
return HANDLER_FINISHED;
case 1:
if (r->conf.log_request_handling) {
log_error(r->conf.errh, __FILE__, __LINE__, "cache-miss");
}
/* cache miss */
return HANDLER_COMEBACK;
default:
r->http_status = 500;
return HANDLER_COMEBACK;
}
}
int mod_cml_plugin_init(plugin *p);
int mod_cml_plugin_init(plugin *p) {
p->version = LIGHTTPD_VERSION_ID;
p->name = "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;
return 0;
}