diff options
Diffstat (limited to 'storage/mroonga/vendor/groonga/src/httpd/nginx-module/ngx_http_groonga_module.c')
-rw-r--r-- | storage/mroonga/vendor/groonga/src/httpd/nginx-module/ngx_http_groonga_module.c | 739 |
1 files changed, 391 insertions, 348 deletions
diff --git a/storage/mroonga/vendor/groonga/src/httpd/nginx-module/ngx_http_groonga_module.c b/storage/mroonga/vendor/groonga/src/httpd/nginx-module/ngx_http_groonga_module.c index 727e65fa468..15836a92e05 100644 --- a/storage/mroonga/vendor/groonga/src/httpd/nginx-module/ngx_http_groonga_module.c +++ b/storage/mroonga/vendor/groonga/src/httpd/nginx-module/ngx_http_groonga_module.c @@ -1,6 +1,6 @@ /* -*- c-basic-offset: 2 -*- */ /* - Copyright(C) 2012-2015 Brazil + Copyright(C) 2012-2017 Brazil This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -25,6 +25,7 @@ #include <ngx_http.h> #include <groonga.h> +#include <groonga/plugin.h> #include <sys/stat.h> @@ -47,11 +48,13 @@ typedef struct { ngx_str_t query_log_path; ngx_open_file_t *query_log_file; size_t cache_limit; + ngx_msec_t default_request_timeout_msec; char *config_file; int config_line; char *name; - grn_ctx context; + grn_obj *database; grn_cache *cache; + ngx_str_t cache_base_path; } ngx_http_groonga_loc_conf_t; typedef struct { @@ -62,7 +65,7 @@ typedef struct { typedef struct { grn_bool initialized; - grn_ctx context; + grn_rc rc; struct { grn_bool processed; grn_bool header_sent; @@ -78,21 +81,14 @@ typedef struct { } typed; } ngx_http_groonga_handler_data_t; -typedef struct { - ngx_pool_t *pool; - ngx_open_file_t *file; -} ngx_http_groonga_logger_data_t; - -typedef struct { - ngx_pool_t *pool; - ngx_open_file_t *file; - ngx_str_t *path; -} ngx_http_groonga_query_logger_data_t; - typedef void (*ngx_http_groonga_loc_conf_callback_pt)(ngx_http_groonga_loc_conf_t *conf, void *user_data); ngx_module_t ngx_http_groonga_module; +static grn_ctx ngx_http_groonga_context; +static grn_ctx *context = &ngx_http_groonga_context; +static ngx_http_groonga_loc_conf_t *ngx_http_groonga_current_location_conf = NULL; + static char * ngx_str_null_terminate(ngx_pool_t *pool, const ngx_str_t *string) { @@ -133,6 +129,29 @@ ngx_str_is_custom_path(ngx_str_t *string) return GRN_TRUE; } +static uint32_t +ngx_http_groonga_get_thread_limit(void *data) +{ + return 1; +} + +static ngx_int_t +ngx_http_groonga_grn_rc_to_http_status(grn_rc rc) +{ + switch (rc) { + case GRN_SUCCESS : + return NGX_HTTP_OK; + case GRN_INVALID_ARGUMENT : + case GRN_FUNCTION_NOT_IMPLEMENTED : + case GRN_SYNTAX_ERROR : + return NGX_HTTP_BAD_REQUEST; + case GRN_CANCEL : + return NGX_HTTP_REQUEST_TIME_OUT; + default : + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } +} + static void ngx_http_groonga_write_fd(ngx_fd_t fd, u_char *buffer, size_t buffer_size, @@ -163,62 +182,41 @@ ngx_http_groonga_logger_log(grn_ctx *ctx, grn_log_level level, const char *message, const char *location, void *user_data) { - ngx_http_groonga_logger_data_t *logger_data = user_data; - const char level_marks[] = " EACewnid-"; + ngx_open_file_t *file = user_data; + char level_marks[] = " EACewnid-"; u_char buffer[NGX_MAX_ERROR_STR]; - u_char *last; - size_t prefix_size; - size_t message_size; - size_t location_size; - size_t postfix_size; - size_t log_message_size; - -#define LOG_PREFIX_FORMAT "%s|%c|%s " - prefix_size = - strlen(timestamp) + - 1 /* | */ + - 1 /* %c */ + - 1 /* | */ + - strlen(title) + - 1 /* a space */; - message_size = strlen(message); - if (location && *location) { - location_size = 1 /* a space */ + strlen(location); - } else { - location_size = 0; + + if (!file) { + return; } - postfix_size = 1 /* \n */; - log_message_size = prefix_size + message_size + location_size + postfix_size; - if (log_message_size > NGX_MAX_ERROR_STR) { - last = ngx_slprintf(buffer, buffer + NGX_MAX_ERROR_STR, - LOG_PREFIX_FORMAT, - timestamp, *(level_marks + level), title); - ngx_write_fd(logger_data->file->fd, buffer, last - buffer); - ngx_http_groonga_write_fd(logger_data->file->fd, + ngx_http_groonga_write_fd(file->fd, + buffer, NGX_MAX_ERROR_STR, + timestamp, strlen(timestamp)); + ngx_write_fd(file->fd, "|", 1); + ngx_write_fd(file->fd, level_marks + level, 1); + ngx_write_fd(file->fd, "|", 1); + if (location && *location) { + ngx_http_groonga_write_fd(file->fd, buffer, NGX_MAX_ERROR_STR, - message, message_size); - if (location_size > 0) { - ngx_write_fd(logger_data->file->fd, " ", 1); - ngx_http_groonga_write_fd(logger_data->file->fd, + location, strlen(location)); + ngx_write_fd(file->fd, ": ", 2); + if (title && *title) { + ngx_http_groonga_write_fd(file->fd, buffer, NGX_MAX_ERROR_STR, - location, location_size); + title, strlen(title)); + ngx_write_fd(file->fd, " ", 1); } - ngx_write_fd(logger_data->file->fd, "\n", 1); } else { - if (location && *location) { - last = ngx_slprintf(buffer, buffer + NGX_MAX_ERROR_STR, - LOG_PREFIX_FORMAT " %s %s\n", - timestamp, *(level_marks + level), title, message, - location); - } else { - last = ngx_slprintf(buffer, buffer + NGX_MAX_ERROR_STR, - LOG_PREFIX_FORMAT " %s\n", - timestamp, *(level_marks + level), title, message); - } - ngx_write_fd(logger_data->file->fd, buffer, last - buffer); + ngx_http_groonga_write_fd(file->fd, + buffer, NGX_MAX_ERROR_STR, + title, strlen(title)); + ngx_write_fd(file->fd, " ", 1); } -#undef LOG_PREFIX_FORMAT + ngx_http_groonga_write_fd(file->fd, + buffer, NGX_MAX_ERROR_STR, + message, strlen(message)); + ngx_write_fd(file->fd, "\n", 1); } static void @@ -232,14 +230,11 @@ ngx_http_groonga_logger_reopen(grn_ctx *ctx, void *user_data) static void ngx_http_groonga_logger_fin(grn_ctx *ctx, void *user_data) { - ngx_http_groonga_logger_data_t *logger_data = user_data; - - ngx_pfree(logger_data->pool, logger_data); } static grn_logger ngx_http_groonga_logger = { GRN_LOG_DEFAULT_LEVEL, - GRN_LOG_TIME | GRN_LOG_MESSAGE, + GRN_LOG_TIME | GRN_LOG_MESSAGE | GRN_LOG_PID, NULL, ngx_http_groonga_logger_log, ngx_http_groonga_logger_reopen, @@ -247,28 +242,17 @@ static grn_logger ngx_http_groonga_logger = { }; static ngx_int_t -ngx_http_groonga_context_init_logger(grn_ctx *context, - ngx_http_groonga_loc_conf_t *location_conf, +ngx_http_groonga_context_init_logger(ngx_http_groonga_loc_conf_t *location_conf, ngx_pool_t *pool, ngx_log_t *log) { - ngx_http_groonga_logger_data_t *logger_data; - - if (!location_conf->log_file) { - return NGX_OK; - } - - logger_data = ngx_pcalloc(pool, sizeof(ngx_http_groonga_logger_data_t)); - if (!logger_data) { - ngx_log_error(NGX_LOG_ERR, log, 0, - "http_groonga: failed to allocate memory for logger"); - return NGX_ERROR; + if (ngx_http_groonga_current_location_conf) { + ngx_http_groonga_current_location_conf->log_level = + grn_logger_get_max_level(context); } - logger_data->pool = pool; - logger_data->file = location_conf->log_file; ngx_http_groonga_logger.max_level = location_conf->log_level; - ngx_http_groonga_logger.user_data = logger_data; + ngx_http_groonga_logger.user_data = location_conf->log_file; grn_logger_set(context, &ngx_http_groonga_logger); return NGX_OK; @@ -279,36 +263,29 @@ ngx_http_groonga_query_logger_log(grn_ctx *ctx, unsigned int flag, const char *timestamp, const char *info, const char *message, void *user_data) { - ngx_http_groonga_query_logger_data_t *data = user_data; + ngx_open_file_t *file = user_data; u_char buffer[NGX_MAX_ERROR_STR]; u_char *last; + if (!file) { + return; + } + last = ngx_slprintf(buffer, buffer + NGX_MAX_ERROR_STR, "%s|%s%s\n", timestamp, info, message); - ngx_write_fd(data->file->fd, buffer, last - buffer); + ngx_write_fd(file->fd, buffer, last - buffer); } static void ngx_http_groonga_query_logger_reopen(grn_ctx *ctx, void *user_data) { - ngx_http_groonga_query_logger_data_t *data = user_data; - - GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_DESTINATION, " ", - "query log will be closed: <%.*s>", - (int)(data->path->len), data->path->data); ngx_reopen_files((ngx_cycle_t *)ngx_cycle, -1); - GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_DESTINATION, " ", - "query log is opened: <%.*s>", - (int)(data->path->len), data->path->data); } static void ngx_http_groonga_query_logger_fin(grn_ctx *ctx, void *user_data) { - ngx_http_groonga_query_logger_data_t *data = user_data; - - ngx_pfree(data->pool, data); } static grn_query_logger ngx_http_groonga_query_logger = { @@ -320,71 +297,61 @@ static grn_query_logger ngx_http_groonga_query_logger = { }; static ngx_int_t -ngx_http_groonga_context_init_query_logger(grn_ctx *context, - ngx_http_groonga_loc_conf_t *location_conf, +ngx_http_groonga_context_init_query_logger(ngx_http_groonga_loc_conf_t *location_conf, ngx_pool_t *pool, ngx_log_t *log) { - ngx_http_groonga_query_logger_data_t *query_logger_data; - - if (!location_conf->query_log_file) { - return NGX_OK; - } - - query_logger_data = ngx_pcalloc(pool, - sizeof(ngx_http_groonga_query_logger_data_t)); - if (!query_logger_data) { - ngx_log_error(NGX_LOG_ERR, log, 0, - "http_groonga: failed to allocate memory for query logger"); - return NGX_ERROR; - } - - query_logger_data->pool = pool; - query_logger_data->file = location_conf->query_log_file; - query_logger_data->path = &(location_conf->query_log_path); - ngx_http_groonga_query_logger.user_data = query_logger_data; + ngx_http_groonga_query_logger.user_data = location_conf->query_log_file; grn_query_logger_set(context, &ngx_http_groonga_query_logger); return NGX_OK; } static ngx_int_t -ngx_http_groonga_context_init(grn_ctx *context, - ngx_http_groonga_loc_conf_t *location_conf, +ngx_http_groonga_context_init(ngx_http_groonga_loc_conf_t *location_conf, ngx_pool_t *pool, ngx_log_t *log) { ngx_int_t status; - grn_ctx_init(context, GRN_NO_FLAGS); + if (location_conf == ngx_http_groonga_current_location_conf) { + return NGX_OK; + } - status = ngx_http_groonga_context_init_logger(context, - location_conf, + status = ngx_http_groonga_context_init_logger(location_conf, pool, log); if (status == NGX_ERROR) { - grn_ctx_fin(context); return status; } - status = ngx_http_groonga_context_init_query_logger(context, - location_conf, + status = ngx_http_groonga_context_init_query_logger(location_conf, pool, log); if (status == NGX_ERROR) { - grn_ctx_fin(context); return status; } - if (location_conf->cache) { - grn_cache_current_set(context, location_conf->cache); + grn_ctx_use(context, location_conf->database); + grn_cache_current_set(context, location_conf->cache); + + /* TODO: It doesn't work yet. We need to implement request timeout + * handler. */ + if (location_conf->default_request_timeout_msec == NGX_CONF_UNSET_MSEC) { + grn_set_default_request_timeout(0.0); + } else { + double timeout; + timeout = location_conf->default_request_timeout_msec / 1000.0; + grn_set_default_request_timeout(timeout); } + ngx_http_groonga_current_location_conf = location_conf; + return status; } static void -ngx_http_groonga_context_log_error(ngx_log_t *log, grn_ctx *context) +ngx_http_groonga_context_log_error(ngx_log_t *log) { if (context->rc == GRN_SUCCESS) { return; @@ -394,12 +361,12 @@ ngx_http_groonga_context_log_error(ngx_log_t *log, grn_ctx *context) } static ngx_int_t -ngx_http_groonga_context_check_error(ngx_log_t *log, grn_ctx *context) +ngx_http_groonga_context_check_error(ngx_log_t *log) { if (context->rc == GRN_SUCCESS) { return NGX_OK; } else { - ngx_http_groonga_context_log_error(log, context); + ngx_http_groonga_context_log_error(log); return NGX_HTTP_BAD_REQUEST; } } @@ -426,19 +393,14 @@ static void ngx_http_groonga_handler_cleanup(void *user_data) { ngx_http_groonga_handler_data_t *data = user_data; - grn_ctx *context; if (!data->initialized) { return; } - context = &(data->context); GRN_OBJ_FIN(context, &(data->typed.head)); GRN_OBJ_FIN(context, &(data->typed.body)); GRN_OBJ_FIN(context, &(data->typed.foot)); - grn_logger_set(context, NULL); - grn_query_logger_set(context, NULL); - grn_ctx_fin(context); } static void @@ -566,7 +528,8 @@ ngx_http_groonga_context_receive_handler_typed(grn_ctx *context, context->stat |= GRN_CTX_QUIT; } else { context->rc = GRN_OPERATION_NOT_PERMITTED; - GRN_TEXT_PUTS(context, &(data->typed.body), "false"); + result = "false"; + result_size = strlen(result); context->stat &= ~GRN_CTX_QUIT; } } @@ -663,23 +626,20 @@ ngx_http_groonga_handler_create_data(ngx_http_request_t *r, ngx_http_cleanup_t *cleanup; ngx_http_groonga_handler_data_t *data; - grn_ctx *context; - location_conf = ngx_http_get_module_loc_conf(r, ngx_http_groonga_module); + rc = ngx_http_groonga_context_init(location_conf, r->pool, r->connection->log); + if (rc != NGX_OK) { + return rc; + } + cleanup = ngx_http_cleanup_add(r, sizeof(ngx_http_groonga_handler_data_t)); cleanup->handler = ngx_http_groonga_handler_cleanup; data = cleanup->data; *data_return = data; - context = &(data->context); - rc = ngx_http_groonga_context_init(context, location_conf, - r->pool, r->connection->log); - if (rc != NGX_OK) { - return rc; - } - data->initialized = GRN_TRUE; + data->rc = GRN_SUCCESS; data->raw.processed = GRN_FALSE; data->raw.header_sent = GRN_FALSE; @@ -692,8 +652,8 @@ ngx_http_groonga_handler_create_data(ngx_http_request_t *r, GRN_TEXT_INIT(&(data->typed.body), GRN_NO_FLAGS); GRN_TEXT_INIT(&(data->typed.foot), GRN_NO_FLAGS); - grn_ctx_use(context, grn_ctx_db(&(location_conf->context))); - rc = ngx_http_groonga_context_check_error(r->connection->log, context); + grn_ctx_use(context, location_conf->database); + rc = ngx_http_groonga_context_check_error(r->connection->log); if (rc != NGX_OK) { return rc; } @@ -705,32 +665,28 @@ ngx_http_groonga_handler_create_data(ngx_http_request_t *r, return NGX_OK; } -static ngx_int_t +static void ngx_http_groonga_handler_process_command_path(ngx_http_request_t *r, ngx_str_t *command_path, - ngx_http_groonga_handler_data_t *data) + ngx_http_groonga_handler_data_t *data, + int flags) { - grn_ctx *context; grn_obj uri; - context = &(data->context); GRN_TEXT_INIT(&uri, 0); GRN_TEXT_PUTS(context, &uri, "/d/"); GRN_TEXT_PUT(context, &uri, command_path->data, command_path->len); - grn_ctx_send(context, GRN_TEXT_VALUE(&uri), GRN_TEXT_LEN(&uri), - GRN_NO_FLAGS); - ngx_http_groonga_context_log_error(r->connection->log, context); + grn_ctx_send(context, GRN_TEXT_VALUE(&uri), GRN_TEXT_LEN(&uri), flags); + data->rc = context->rc; + ngx_http_groonga_context_log_error(r->connection->log); GRN_OBJ_FIN(context, &uri); - - return NGX_OK; } -static ngx_int_t +static grn_bool ngx_http_groonga_handler_validate_post_command(ngx_http_request_t *r, ngx_str_t *command_path, ngx_http_groonga_handler_data_t *data) { - grn_ctx *context; ngx_str_t command; command.data = command_path->data; @@ -740,166 +696,180 @@ ngx_http_groonga_handler_validate_post_command(ngx_http_request_t *r, command.len = command_path->len - r->args.len - strlen("?"); } if (ngx_str_equal_c_string(&command, "load")) { - return NGX_OK; + return GRN_TRUE; } - context = &(data->context); + data->rc = GRN_INVALID_ARGUMENT; ngx_http_groonga_handler_set_content_type(r, "text/plain"); GRN_TEXT_PUTS(context, &(data->typed.body), "command for POST must be <load>: <"); GRN_TEXT_PUT(context, &(data->typed.body), command.data, command.len); GRN_TEXT_PUTS(context, &(data->typed.body), ">"); - return NGX_HTTP_BAD_REQUEST; -} - -static ngx_int_t -ngx_http_groonga_send_lines(grn_ctx *context, - ngx_http_request_t *r, - u_char *current, - u_char *last) -{ - ngx_int_t rc; - - u_char *line_start; - - for (line_start = current; current < last; current++) { - if (*current != '\n') { - continue; - } - - grn_ctx_send(context, (const char *)line_start, current - line_start, - GRN_NO_FLAGS); - rc = ngx_http_groonga_context_check_error(r->connection->log, context); - if (rc != NGX_OK) { - return rc; - } - line_start = current + 1; - } - if (line_start < current) { - grn_ctx_send(context, (const char *)line_start, current - line_start, - GRN_NO_FLAGS); - rc = ngx_http_groonga_context_check_error(r->connection->log, context); - if (rc != NGX_OK) { - return rc; - } - } - - return NGX_OK; + return GRN_FALSE; } -static ngx_int_t -ngx_http_groonga_join_request_body_chain(ngx_http_request_t *r, - ngx_chain_t *chain, - u_char **out_start, - u_char **out_end) +static void +ngx_http_groonga_send_body(ngx_http_request_t *r, + ngx_http_groonga_handler_data_t *data) { - ngx_int_t rc; - - ngx_log_t *log = r->connection->log; - - ngx_chain_t *current; - u_char *out; - size_t out_size; + ngx_log_t *log; + grn_obj line_buffer; + size_t line_start_offset; + size_t line_check_start_offset; + ngx_chain_t *chain; + size_t line_buffer_chunk_size = 4096; - u_char *out_cursor; - ngx_buf_t *buffer; - size_t buffer_size; + log = r->connection->log; - out_size = 0; - for (current = chain; current; current = current->next) { - out_size += ngx_buf_size(current->buf); - } - out = ngx_palloc(r->pool, out_size); - if (!out) { - ngx_log_error(NGX_LOG_ERR, log, 0, - "http_groonga: failed to allocate memory for request body"); - return NGX_ERROR; - } + GRN_TEXT_INIT(&line_buffer, 0); + line_start_offset = 0; + line_check_start_offset = 0; + for (chain = r->request_body->bufs; chain; chain = chain->next) { + ngx_buf_t *buffer; + size_t rest_buffer_size; + off_t offset; + + buffer = chain->buf; + rest_buffer_size = ngx_buf_size(buffer); + offset = 0; + while (rest_buffer_size > 0) { + size_t current_buffer_size; + + if (rest_buffer_size > line_buffer_chunk_size) { + current_buffer_size = line_buffer_chunk_size; + } else { + current_buffer_size = rest_buffer_size; + } - out_cursor = out; - for (current = chain; current; current = current->next) { - buffer = current->buf; - buffer_size = ngx_buf_size(current->buf); + if (ngx_buf_in_memory(buffer)) { + GRN_TEXT_PUT(context, + &line_buffer, + buffer->pos + offset, + current_buffer_size); + } else { + ngx_int_t rc; + grn_bulk_reserve(context, &line_buffer, current_buffer_size); + rc = ngx_read_file(buffer->file, + (u_char *)GRN_BULK_CURR(&line_buffer), + current_buffer_size, + offset); + if (rc < 0) { + GRN_PLUGIN_ERROR(context, + GRN_INPUT_OUTPUT_ERROR, + "[nginx][post][body][read] " + "failed to read a request body from file"); + goto exit; + } + GRN_BULK_INCR_LEN(&line_buffer, current_buffer_size); + } + offset += current_buffer_size; + rest_buffer_size -= current_buffer_size; + + { + const char *line_start; + const char *line_current; + const char *line_end; + + line_start = GRN_TEXT_VALUE(&line_buffer) + line_start_offset; + line_end = GRN_TEXT_VALUE(&line_buffer) + GRN_TEXT_LEN(&line_buffer); + for (line_current = line_start + line_check_start_offset; + line_current < line_end; + line_current++) { + size_t line_length; + int flags = GRN_NO_FLAGS; + + if (*line_current != '\n') { + continue; + } + + line_length = line_current - line_start + 1; + if (line_current + 1 == line_end && + !chain->next && + rest_buffer_size == 0) { + flags |= GRN_CTX_TAIL; + } + grn_ctx_send(context, line_start, line_length, flags); + line_start_offset += line_length; + line_start += line_length; + ngx_http_groonga_context_log_error(log); + if (context->rc != GRN_SUCCESS && data->rc == GRN_SUCCESS) { + data->rc = context->rc; + } + } - if (buffer->file) { - rc = ngx_read_file(buffer->file, out_cursor, buffer_size, 0); - if (rc < 0) { - ngx_log_error(NGX_LOG_ERR, log, 0, - "http_groonga: failed to read a request body stored in a file"); - return rc; + if (line_start_offset == 0) { + line_buffer_chunk_size *= 2; + line_check_start_offset = GRN_TEXT_LEN(&line_buffer); + } else if ((size_t)GRN_TEXT_LEN(&line_buffer) == line_start_offset) { + GRN_BULK_REWIND(&line_buffer); + line_start_offset = 0; + line_check_start_offset = 0; + } else { + size_t rest_line_size; + rest_line_size = GRN_TEXT_LEN(&line_buffer) - line_start_offset; + grn_memmove(GRN_TEXT_VALUE(&line_buffer), + GRN_TEXT_VALUE(&line_buffer) + line_start_offset, + rest_line_size); + grn_bulk_truncate(context, &line_buffer, rest_line_size); + line_start_offset = 0; + line_check_start_offset = GRN_TEXT_LEN(&line_buffer); + } } - } else { - ngx_memcpy(out_cursor, buffer->pos, buffer_size); } - out_cursor += buffer_size; } - *out_start = out; - *out_end = out + out_size; + if (GRN_TEXT_LEN(&line_buffer) > 0) { + grn_ctx_send(context, + GRN_TEXT_VALUE(&line_buffer), + GRN_TEXT_LEN(&line_buffer), + GRN_CTX_TAIL); + ngx_http_groonga_context_log_error(log); + if (context->rc != GRN_SUCCESS && data->rc == GRN_SUCCESS) { + data->rc = context->rc; + } + } - return NGX_OK; +exit : + GRN_OBJ_FIN(context, &line_buffer); } -static ngx_int_t +static void ngx_http_groonga_handler_process_body(ngx_http_request_t *r, ngx_http_groonga_handler_data_t *data) { - ngx_int_t rc; - - grn_ctx *context; - ngx_buf_t *body; - u_char *body_data; - u_char *body_data_end; - - context = &(data->context); body = r->request_body->bufs->buf; if (!body) { + data->rc = GRN_INVALID_ARGUMENT; ngx_http_groonga_handler_set_content_type(r, "text/plain"); GRN_TEXT_PUTS(context, &(data->typed.body), "must send load data as body"); - return NGX_HTTP_BAD_REQUEST; - } - - rc = ngx_http_groonga_join_request_body_chain(r, - r->request_body->bufs, - &body_data, - &body_data_end); - if (rc != NGX_OK) { - return rc; + return; } - rc = ngx_http_groonga_send_lines(context, r, body_data, body_data_end); - ngx_pfree(r->pool, body_data); - - return rc; + ngx_http_groonga_send_body(r, data); } -static ngx_int_t +static void ngx_http_groonga_handler_process_load(ngx_http_request_t *r, ngx_str_t *command_path, ngx_http_groonga_handler_data_t *data) { - ngx_int_t rc; - - rc = ngx_http_groonga_handler_validate_post_command(r, command_path, data); - if (rc != NGX_OK) { - return rc; - } - - rc = ngx_http_groonga_handler_process_command_path(r, command_path, data); - if (rc != NGX_OK) { - return rc; + if (!ngx_http_groonga_handler_validate_post_command(r, command_path, data)) { + return; } - rc = ngx_http_groonga_handler_process_body(r, data); - if (rc != NGX_OK) { - return rc; + ngx_http_groonga_handler_process_command_path(r, + command_path, + data, + GRN_NO_FLAGS); + if (data->rc != GRN_SUCCESS) { + return; } - return NGX_OK; + ngx_http_groonga_handler_process_body(r, data); } static ngx_chain_t * @@ -931,7 +901,6 @@ ngx_http_groonga_handler_send_response(ngx_http_request_t *r, ngx_http_groonga_handler_data_t *data) { ngx_int_t rc; - grn_ctx *context; const char *content_type; ngx_buf_t *head_buf, *body_buf, *foot_buf; ngx_chain_t head_chain, body_chain, foot_chain; @@ -941,11 +910,16 @@ ngx_http_groonga_handler_send_response(ngx_http_request_t *r, return data->raw.rc; } - context = &(data->context); - /* set the 'Content-type' header */ if (r->headers_out.content_type.len == 0) { - content_type = grn_ctx_get_mime_type(context); + grn_obj *foot = &(data->typed.foot); + if (grn_ctx_get_output_type(context) == GRN_CONTENT_JSON && + GRN_TEXT_LEN(foot) > 0 && + GRN_TEXT_VALUE(foot)[GRN_TEXT_LEN(foot) - 1] == ';') { + content_type = "application/javascript"; + } else { + content_type = grn_ctx_get_mime_type(context); + } ngx_http_groonga_handler_set_content_type(r, content_type); } @@ -974,7 +948,7 @@ ngx_http_groonga_handler_send_response(ngx_http_request_t *r, output_chain = ngx_http_groonga_attach_chain(output_chain, &foot_chain); /* set the status line */ - r->headers_out.status = NGX_HTTP_OK; + r->headers_out.status = ngx_http_groonga_grn_rc_to_http_status(data->rc); r->headers_out.content_length_n = GRN_TEXT_LEN(&(data->typed.head)) + GRN_TEXT_LEN(&(data->typed.body)) + GRN_TEXT_LEN(&(data->typed.foot)); @@ -1012,10 +986,10 @@ ngx_http_groonga_handler_get(ngx_http_request_t *r) return rc; } - rc = ngx_http_groonga_handler_process_command_path(r, &command_path, data); - if (rc != NGX_OK) { - return rc; - } + ngx_http_groonga_handler_process_command_path(r, + &command_path, + data, + GRN_CTX_TAIL); /* discard request body, since we don't need it here */ rc = ngx_http_discard_request_body(r); @@ -1058,13 +1032,8 @@ ngx_http_groonga_handler_post(ngx_http_request_t *r) return; } - rc = ngx_http_groonga_handler_process_load(r, &command_path, data); - if (rc != NGX_OK) { - ngx_http_groonga_handler_post_send_error_response(r, rc); - return; - } - - ngx_http_groonga_handler_send_response(r, data); + ngx_http_groonga_handler_process_load(r, &command_path, data); + rc = ngx_http_groonga_handler_send_response(r, data); ngx_http_finalize_request(r, rc); } @@ -1162,29 +1131,9 @@ ngx_http_groonga_conf_set_log_level_slot(ngx_conf_t *cf, ngx_command_t *cmd, value = ngx_str_null_terminate(cf->cycle->pool, ((ngx_str_t *)cf->args->elts) + 1); - if (strcasecmp(value, "none") == 0) { - groonga_location_conf->log_level = GRN_LOG_NONE; - } else if (strcasecmp(value, "emergency") == 0) { - groonga_location_conf->log_level = GRN_LOG_EMERG; - } else if (strcasecmp(value, "alert") == 0) { - groonga_location_conf->log_level = GRN_LOG_ALERT; - } else if (strcasecmp(value, "critical") == 0) { - groonga_location_conf->log_level = GRN_LOG_CRIT; - } else if (strcasecmp(value, "error") == 0) { - groonga_location_conf->log_level = GRN_LOG_ERROR; - } else if (strcasecmp(value, "warning") == 0) { - groonga_location_conf->log_level = GRN_LOG_WARNING; - } else if (strcasecmp(value, "notice") == 0) { - groonga_location_conf->log_level = GRN_LOG_NOTICE; - } else if (strcasecmp(value, "info") == 0) { - groonga_location_conf->log_level = GRN_LOG_INFO; - } else if (strcasecmp(value, "debug") == 0) { - groonga_location_conf->log_level = GRN_LOG_DEBUG; - } else if (strcasecmp(value, "dump") == 0) { - groonga_location_conf->log_level = GRN_LOG_DUMP; - } else { + if (!grn_log_level_parse(value, &(groonga_location_conf->log_level))) { status = "must be one of 'none', 'emergency', 'alert', " - "'ciritical', 'error', 'warning', 'notice', 'info', 'debug' and 'dump'"; + "'critical', 'error', 'warning', 'notice', 'info', 'debug' and 'dump'"; } ngx_pfree(cf->cycle->pool, value); @@ -1216,7 +1165,7 @@ ngx_http_groonga_conf_set_query_log_path_slot(ngx_conf_t *cf, ngx_conf_open_file(cf->cycle, &(groonga_location_conf->query_log_path)); if (!groonga_location_conf->query_log_file) { ngx_log_error(NGX_LOG_ERR, cf->cycle->log, 0, - "http_groonga: failed to open groonga query log file: <%V>", + "http_groonga: failed to open Groonga query log file: <%V>", &(groonga_location_conf->query_log_path)); return NGX_CONF_ERROR; } @@ -1251,6 +1200,8 @@ ngx_http_groonga_create_loc_conf(ngx_conf_t *cf) conf->config_file = NULL; conf->config_line = 0; conf->cache = NULL; + conf->cache_base_path.data = NULL; + conf->cache_base_path.len = 0; return conf; } @@ -1260,6 +1211,11 @@ ngx_http_groonga_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { ngx_http_groonga_loc_conf_t *prev = parent; ngx_http_groonga_loc_conf_t *conf = child; + ngx_flag_t enabled = 0; + + if (conf->enabled != NGX_CONF_UNSET) { + enabled = conf->enabled; + } ngx_conf_merge_str_value(conf->database_path, prev->database_path, NULL); ngx_conf_merge_value(conf->database_auto_create, @@ -1269,16 +1225,17 @@ ngx_http_groonga_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) GRN_CACHE_DEFAULT_MAX_N_ENTRIES); #ifdef NGX_HTTP_GROONGA_LOG_PATH - if (!conf->log_file) { - ngx_str_t default_log_path; - default_log_path.data = (u_char *)NGX_HTTP_GROONGA_LOG_PATH; - default_log_path.len = strlen(NGX_HTTP_GROONGA_LOG_PATH); - conf->log_file = ngx_conf_open_file(cf->cycle, &default_log_path); + ngx_conf_merge_str_value(conf->log_path, prev->log_path, + NGX_HTTP_GROONGA_LOG_PATH); + if (!conf->log_file && + ngx_str_is_custom_path(&(conf->log_path)) && + enabled) { + conf->log_file = ngx_conf_open_file(cf->cycle, &(conf->log_path)); if (!conf->log_file) { ngx_log_error(NGX_LOG_ERR, cf->cycle->log, 0, "http_groonga: " - "failed to open the default groonga log file: <%V>", - &default_log_path); + "failed to open the default Groonga log file: <%V>", + &(conf->log_path)); return NGX_CONF_ERROR; } } @@ -1288,18 +1245,22 @@ ngx_http_groonga_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) NGX_HTTP_GROONGA_QUERY_LOG_PATH); if (!conf->query_log_file && ngx_str_is_custom_path(&(conf->query_log_path)) && - conf->enabled) { + enabled) { conf->query_log_file = ngx_conf_open_file(cf->cycle, &(conf->query_log_path)); if (!conf->query_log_file) { ngx_log_error(NGX_LOG_ERR, cf->cycle->log, 0, "http_groonga: " - "failed to open the default groonga query log file: <%V>", + "failed to open the default Groonga query log file: <%V>", &(conf->query_log_path)); return NGX_CONF_ERROR; } } + ngx_conf_merge_str_value(conf->cache_base_path, + prev->cache_base_path, + NULL); + return NGX_CONF_OK; } @@ -1351,6 +1312,41 @@ ngx_http_groonga_each_loc_conf(ngx_http_conf_ctx_t *http_conf, ngx_http_groonga_each_loc_conf_in_tree(location_conf->static_locations, callback, user_data); + +#if NGX_PCRE + if (location_conf->regex_locations) { + ngx_uint_t j; + for (j = 0; location_conf->regex_locations[j]; j++) { + ngx_http_core_loc_conf_t *regex_location_conf; + + regex_location_conf = location_conf->regex_locations[j]; + if (regex_location_conf->handler == ngx_http_groonga_handler) { + callback(regex_location_conf->loc_conf[ngx_http_groonga_module.ctx_index], + user_data); + } + } + } +#endif + } +} + +static void +ngx_http_groonga_set_logger_callback(ngx_http_groonga_loc_conf_t *location_conf, + void *user_data) +{ + ngx_http_groonga_database_callback_data_t *data = user_data; + + data->rc = ngx_http_groonga_context_init_logger(location_conf, + data->pool, + data->log); + if (data->rc != NGX_OK) { + return; + } + data->rc = ngx_http_groonga_context_init_query_logger(location_conf, + data->pool, + data->log); + if (data->rc != NGX_OK) { + return; } } @@ -1387,7 +1383,6 @@ ngx_http_groonga_create_database(ngx_http_groonga_loc_conf_t *location_conf, ngx_http_groonga_database_callback_data_t *data) { const char *database_base_name; - grn_ctx *context; database_base_name = strrchr(location_conf->database_path_cstr, '/'); if (database_base_name) { @@ -1402,14 +1397,14 @@ ngx_http_groonga_create_database(ngx_http_groonga_loc_conf_t *location_conf, } } - context = &(location_conf->context); - grn_db_create(context, location_conf->database_path_cstr, NULL); + location_conf->database = + grn_db_create(context, location_conf->database_path_cstr, NULL); if (context->rc == GRN_SUCCESS) { return; } ngx_log_error(NGX_LOG_EMERG, data->log, 0, - "failed to create groonga database: %s", + "failed to create Groonga database: %s", context->errbuf); data->rc = NGX_ERROR; } @@ -1419,11 +1414,16 @@ ngx_http_groonga_open_database_callback(ngx_http_groonga_loc_conf_t *location_co void *user_data) { ngx_http_groonga_database_callback_data_t *data = user_data; - grn_ctx *context; - context = &(location_conf->context); - data->rc = ngx_http_groonga_context_init(context, location_conf, - data->pool, data->log); + data->rc = ngx_http_groonga_context_init_logger(location_conf, + data->pool, + data->log); + if (data->rc != NGX_OK) { + return; + } + data->rc = ngx_http_groonga_context_init_query_logger(location_conf, + data->pool, + data->log); if (data->rc != NGX_OK) { return; } @@ -1443,27 +1443,41 @@ ngx_http_groonga_open_database_callback(ngx_http_groonga_loc_conf_t *location_co ngx_str_null_terminate(data->pool, &(location_conf->database_path)); } - grn_db_open(context, location_conf->database_path_cstr); + location_conf->database = + grn_db_open(context, location_conf->database_path_cstr); if (context->rc != GRN_SUCCESS) { if (location_conf->database_auto_create) { ngx_http_groonga_create_database(location_conf, data); } else { ngx_log_error(NGX_LOG_EMERG, data->log, 0, - "failed to open groonga database: %s", + "failed to open Groonga database: %s", context->errbuf); data->rc = NGX_ERROR; + } + if (data->rc != NGX_OK) { return; } } - location_conf->cache = grn_cache_open(context); + if (location_conf->cache_base_path.data && + ngx_str_is_custom_path(&(location_conf->cache_base_path))) { + char cache_base_path[PATH_MAX]; + grn_memcpy(cache_base_path, + location_conf->cache_base_path.data, + location_conf->cache_base_path.len); + cache_base_path[location_conf->cache_base_path.len] = '\0'; + location_conf->cache = grn_persistent_cache_open(context, cache_base_path); + } else { + location_conf->cache = grn_cache_open(context); + } if (!location_conf->cache) { ngx_log_error(NGX_LOG_EMERG, data->log, 0, - "failed to open groonga cache: %s", + "failed to open Groonga cache: %s", context->errbuf); data->rc = NGX_ERROR; return; } + if (location_conf->cache_limit != NGX_CONF_UNSET_SIZE) { grn_cache_set_max_n_entries(context, location_conf->cache, @@ -1476,26 +1490,20 @@ ngx_http_groonga_close_database_callback(ngx_http_groonga_loc_conf_t *location_c void *user_data) { ngx_http_groonga_database_callback_data_t *data = user_data; - grn_ctx *context; - context = &(location_conf->context); - ngx_http_groonga_context_init_logger(context, - location_conf, + ngx_http_groonga_context_init_logger(location_conf, data->pool, data->log); - ngx_http_groonga_context_init_query_logger(context, - location_conf, + ngx_http_groonga_context_init_query_logger(location_conf, data->pool, data->log); grn_cache_current_set(context, location_conf->cache); - grn_obj_close(context, grn_ctx_db(context)); - ngx_http_groonga_context_log_error(data->log, context); + grn_obj_close(context, location_conf->database); + ngx_http_groonga_context_log_error(data->log); grn_cache_current_set(context, NULL); grn_cache_close(context, location_conf->cache); - - grn_ctx_fin(context); } static ngx_int_t @@ -1505,12 +1513,11 @@ ngx_http_groonga_init_process(ngx_cycle_t *cycle) ngx_http_conf_ctx_t *http_conf; ngx_http_groonga_database_callback_data_t data; - rc = grn_init(); - if (rc != GRN_SUCCESS) { - return NGX_ERROR; - } + grn_thread_set_get_limit_func(ngx_http_groonga_get_thread_limit, NULL); - grn_set_segv_handler(); +#ifdef NGX_HTTP_GROONGA_LOG_PATH + grn_default_logger_set_path(NGX_HTTP_GROONGA_LOG_PATH); +#endif http_conf = (ngx_http_conf_ctx_t *)ngx_get_conf(cycle->conf_ctx, ngx_http_module); @@ -1519,6 +1526,26 @@ ngx_http_groonga_init_process(ngx_cycle_t *cycle) data.pool = cycle->pool; data.rc = NGX_OK; ngx_http_groonga_each_loc_conf(http_conf, + ngx_http_groonga_set_logger_callback, + &data); + + if (data.rc != NGX_OK) { + return data.rc; + } + + rc = grn_init(); + if (rc != GRN_SUCCESS) { + return NGX_ERROR; + } + + grn_set_segv_handler(); + + rc = grn_ctx_init(context, GRN_NO_FLAGS); + if (rc != GRN_SUCCESS) { + return NGX_ERROR; + } + + ngx_http_groonga_each_loc_conf(http_conf, ngx_http_groonga_open_database_callback, &data); @@ -1539,6 +1566,8 @@ ngx_http_groonga_exit_process(ngx_cycle_t *cycle) ngx_http_groonga_close_database_callback, &data); + grn_ctx_fin(context); + grn_fin(); return; @@ -1602,6 +1631,20 @@ static ngx_command_t ngx_http_groonga_commands[] = { offsetof(ngx_http_groonga_loc_conf_t, cache_limit), NULL }, + { ngx_string("groonga_default_request_timeout"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_groonga_loc_conf_t, default_request_timeout_msec), + NULL }, + + { ngx_string("groonga_cache_base_path"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_groonga_loc_conf_t, cache_base_path), + NULL }, + ngx_null_command }; |