/* * 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; }