Permalink
Fetching contributors…
Cannot retrieve contributors at this time
570 lines (435 sloc) 15.9 KB
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_drizzle_module.h"
#include "ngx_http_drizzle_handler.h"
#include "ngx_http_drizzle_upstream.h"
#include "ngx_http_drizzle_keepalive.h"
static ngx_str_t ngx_http_drizzle_tid_var_name =
ngx_string("drizzle_thread_id");
/* Forward declaration */
static char *ngx_http_drizzle_set_complex_value_slot(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char *ngx_http_drizzle_query(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_drizzle_pass(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static void *ngx_http_drizzle_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_drizzle_merge_loc_conf(ngx_conf_t *cf, void *parent,
void *child);
static ngx_int_t ngx_http_drizzle_add_variables(ngx_conf_t *cf);
static ngx_int_t ngx_http_drizzle_tid_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static char *ngx_http_drizzle_enable_status(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static ngx_http_variable_t ngx_http_drizzle_variables[] = {
{ ngx_string("drizzle_thread_id"), NULL,
ngx_http_drizzle_tid_variable, 0,
NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_null_string, NULL, NULL, 0, 0, 0 }
};
/* config directives for module drizzle */
static ngx_command_t ngx_http_drizzle_cmds[] = {
{ ngx_string("drizzle_server"),
NGX_HTTP_UPS_CONF|NGX_CONF_1MORE,
ngx_http_upstream_drizzle_server,
NGX_HTTP_SRV_CONF_OFFSET,
0,
NULL },
{ ngx_string("drizzle_keepalive"),
NGX_HTTP_UPS_CONF|NGX_CONF_1MORE,
ngx_http_upstream_drizzle_keepalive,
NGX_HTTP_SRV_CONF_OFFSET,
0,
NULL },
{ ngx_string("drizzle_query"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
|NGX_HTTP_LIF_CONF|NGX_CONF_1MORE,
ngx_http_drizzle_query,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("drizzle_dbname"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
ngx_http_drizzle_set_complex_value_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_drizzle_loc_conf_t, dbname),
NULL },
{ ngx_string("drizzle_pass"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
ngx_http_drizzle_pass,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("drizzle_connect_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_drizzle_loc_conf_t, upstream.connect_timeout),
NULL },
{ ngx_string("drizzle_send_query_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_drizzle_loc_conf_t, upstream.send_timeout),
NULL },
{ ngx_string("drizzle_recv_cols_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_drizzle_loc_conf_t, recv_cols_timeout),
NULL },
{ ngx_string("drizzle_recv_rows_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_drizzle_loc_conf_t, recv_rows_timeout),
NULL },
{ ngx_string("drizzle_module_header"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
|NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_drizzle_loc_conf_t, enable_module_header),
NULL },
{ ngx_string("drizzle_buffer_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_drizzle_loc_conf_t, buf_size),
NULL },
{ ngx_string("drizzle_status"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS,
ngx_http_drizzle_enable_status,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
ngx_null_command
};
/* Nginx HTTP subsystem module hooks */
static ngx_http_module_t ngx_http_drizzle_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create_main_conf */
NULL, /* merge_main_conf */
ngx_http_upstream_drizzle_create_srv_conf,
/* create_srv_conf */
NULL, /* merge_srv_conf */
ngx_http_drizzle_create_loc_conf, /* create_loc_conf */
ngx_http_drizzle_merge_loc_conf /* merge_loc_conf */
};
ngx_module_t ngx_http_drizzle_module = {
NGX_MODULE_V1,
&ngx_http_drizzle_module_ctx, /* module context */
ngx_http_drizzle_cmds, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
ngx_drizzle_http_method_t ngx_drizzle_http_methods[] = {
{ (u_char *) "GET", (uint32_t) NGX_HTTP_GET },
{ (u_char *) "HEAD", (uint32_t) NGX_HTTP_HEAD },
{ (u_char *) "POST", (uint32_t) NGX_HTTP_POST },
{ (u_char *) "PUT", (uint32_t) NGX_HTTP_PUT },
{ (u_char *) "DELETE", (uint32_t) NGX_HTTP_DELETE },
{ (u_char *) "MKCOL", (uint32_t) NGX_HTTP_MKCOL },
{ (u_char *) "COPY", (uint32_t) NGX_HTTP_COPY },
{ (u_char *) "MOVE", (uint32_t) NGX_HTTP_MOVE },
{ (u_char *) "OPTIONS", (uint32_t) NGX_HTTP_OPTIONS },
{ (u_char *) "PROPFIND" , (uint32_t) NGX_HTTP_PROPFIND },
{ (u_char *) "PROPPATCH", (uint32_t) NGX_HTTP_PROPPATCH },
{ (u_char *) "LOCK", (uint32_t) NGX_HTTP_LOCK },
{ (u_char *) "UNLOCK", (uint32_t) NGX_HTTP_UNLOCK },
#if defined(nginx_version) && (nginx_version >= 8041)
{ (u_char *) "PATCH", (uint32_t) NGX_HTTP_PATCH },
#endif
{ NULL, 0 }
};
static void *
ngx_http_drizzle_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_drizzle_loc_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_drizzle_loc_conf_t));
if (conf == NULL) {
return NULL;
}
conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
conf->recv_cols_timeout = NGX_CONF_UNSET_MSEC;
conf->recv_rows_timeout = NGX_CONF_UNSET_MSEC;
conf->enable_module_header = NGX_CONF_UNSET;
/* the hardcoded values */
conf->upstream.cyclic_temp_file = 0;
conf->upstream.buffering = 0;
conf->upstream.ignore_client_abort = 0;
conf->upstream.send_lowat = 0;
conf->upstream.bufs.num = 0;
conf->upstream.busy_buffers_size = 0;
conf->upstream.max_temp_file_size = 0;
conf->upstream.temp_file_write_size = 0;
conf->upstream.intercept_errors = 1;
conf->upstream.intercept_404 = 1;
conf->upstream.pass_request_headers = 0;
conf->upstream.pass_request_body = 0;
/* set by ngx_pcalloc:
* conf->dbname = NULL
* conf->query = NULL
*/
conf->complex_target = NGX_CONF_UNSET_PTR;
conf->buf_size = NGX_CONF_UNSET_SIZE;
conf->tid_var_index = NGX_CONF_UNSET;
return conf;
}
static char *
ngx_http_drizzle_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_drizzle_loc_conf_t *prev = parent;
ngx_http_drizzle_loc_conf_t *conf = child;
ngx_conf_merge_value(conf->enable_module_header,
prev->enable_module_header, 1);
ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
prev->upstream.connect_timeout, 60000);
ngx_conf_merge_msec_value(conf->upstream.send_timeout,
prev->upstream.send_timeout, 60000);
ngx_conf_merge_msec_value(conf->recv_cols_timeout,
prev->recv_cols_timeout, 60000);
ngx_conf_merge_msec_value(conf->recv_rows_timeout,
prev->recv_rows_timeout, 60000);
if (conf->dbname == NULL) {
conf->dbname = prev->dbname;
}
if ((conf->default_query == NULL) && (conf->queries == NULL)) {
conf->default_query = prev->default_query;
conf->methods_set = prev->methods_set;
conf->queries = prev->queries;
}
ngx_conf_merge_size_value(conf->buf_size, prev->buf_size,
(size_t) ngx_pagesize);
if (conf->tid_var_index == NGX_CONF_UNSET) {
conf->tid_var_index = prev->tid_var_index;
}
return NGX_CONF_OK;
}
static char *
ngx_http_drizzle_set_complex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
char *p = conf;
ngx_http_complex_value_t **field;
ngx_str_t *value;
ngx_http_compile_complex_value_t ccv;
field = (ngx_http_complex_value_t **) (p + cmd->offset);
if (*field) {
return "is duplicate";
}
*field = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
if (*field == NULL) {
return NGX_CONF_ERROR;
}
value = cf->args->elts;
if (value[1].len == 0) {
ngx_memzero(*field, sizeof(ngx_http_complex_value_t));
return NGX_OK;
}
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = *field;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
char *
ngx_http_drizzle_query(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_str_t *value = cf->args->elts;
ngx_str_t sql = value[cf->args->nelts - 1];
ngx_http_drizzle_loc_conf_t *dlcf = conf;
ngx_http_compile_complex_value_t ccv;
ngx_drizzle_mixed_t *query;
ngx_drizzle_http_method_t *method;
ngx_uint_t methods, i;
if (sql.len == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"drizzle: empty value in \"%V\" directive",
&cmd->name);
return NGX_CONF_ERROR;
}
if (cf->args->nelts == 2) {
/* default query */
dd("default query");
if (dlcf->default_query != NULL) {
return "is duplicate";
}
dlcf->default_query = ngx_pcalloc(cf->pool,
sizeof(ngx_drizzle_mixed_t));
if (dlcf->default_query == NULL) {
return NGX_CONF_ERROR;
}
methods = 0xFFFF;
query = dlcf->default_query;
} else {
/* method-specific query */
dd("method-specific query");
methods = 0;
for (i = 1; i < cf->args->nelts - 1; i++) {
for (method = ngx_drizzle_http_methods; method->name; method++) {
if (ngx_strcasecmp(value[i].data, method->name) == 0) {
/* correct method name */
if (dlcf->methods_set & method->key) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"drizzle: \"%V\" directive"
" for method \"%V\" is duplicate",
&cmd->name, &value[i]);
return NGX_CONF_ERROR;
}
methods |= method->key;
goto next;
}
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"drizzle: invalid method \"%V\"", &value[i]);
return NGX_CONF_ERROR;
next:
continue;
}
if (dlcf->queries == NULL) {
dlcf->queries = ngx_array_create(cf->pool, 4,
sizeof(ngx_drizzle_mixed_t));
if (dlcf->queries == NULL) {
return NGX_CONF_ERROR;
}
}
query = ngx_array_push(dlcf->queries);
if (query == NULL) {
return NGX_CONF_ERROR;
}
ngx_memzero(query, sizeof(ngx_drizzle_mixed_t));
dlcf->methods_set |= methods;
}
if (ngx_http_script_variables_count(&sql)) {
/* complex value */
dd("complex value");
query->key = methods;
query->cv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
if (query->cv == NULL) {
return NGX_CONF_ERROR;
}
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &sql;
ccv.complex_value = query->cv;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
} else {
/* simple value */
dd("simple value");
query->key = methods;
query->sv = sql;
}
return NGX_CONF_OK;
}
static char *
ngx_http_drizzle_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_drizzle_loc_conf_t *dlcf = conf;
ngx_http_core_loc_conf_t *clcf;
ngx_str_t *value;
ngx_http_compile_complex_value_t ccv;
ngx_url_t url;
ngx_uint_t n;
if (dlcf->upstream.upstream) {
return "is duplicate";
}
if (ngx_http_drizzle_add_variables(cf) != NGX_OK) {
return NGX_CONF_ERROR;
}
dlcf->tid_var_index = ngx_http_get_variable_index(cf,
&ngx_http_drizzle_tid_var_name);
if (dlcf->tid_var_index == NGX_ERROR) {
return NGX_CONF_ERROR;
}
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_http_drizzle_handler;
if (clcf->name.data[clcf->name.len - 1] == '/') {
clcf->auto_redirect = 1;
}
value = cf->args->elts;
n = ngx_http_script_variables_count(&value[1]);
if (n) {
dlcf->complex_target = ngx_palloc(cf->pool,
sizeof(ngx_http_complex_value_t));
if (dlcf->complex_target == NULL) {
return NGX_CONF_ERROR;
}
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = dlcf->complex_target;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
dlcf->complex_target = NULL;
ngx_memzero(&url, sizeof(ngx_url_t));
url.url = value[1];
url.no_resolve = 1;
dlcf->upstream.upstream = ngx_http_upstream_add(cf, &url, 0);
if (dlcf->upstream.upstream == NULL) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_drizzle_add_variables(ngx_conf_t *cf)
{
ngx_http_variable_t *var, *v;
for (v = ngx_http_drizzle_variables; v->name.len; v++) {
var = ngx_http_add_variable(cf, &v->name, v->flags);
if (var == NULL) {
return NGX_ERROR;
}
var->get_handler = v->get_handler;
var->data = v->data;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_drizzle_tid_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->len = 0;
v->data = (u_char *) "";
return NGX_OK;
}
static char *
ngx_http_drizzle_enable_status(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_http_drizzle_status_handler;
return NGX_CONF_OK;
}