Permalink
Switch branches/tags
Find file
Fetching contributors…
Cannot retrieve contributors at this time
1750 lines (1351 sloc) 49.7 KB
/*
* Copyright (C) 2009 Valery Kholodkov
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include <nginx.h>
/*
* NOTE: Once you change the value of this macro to >10,
* you need to do adapt the code accordingly
*/
#define NGX_MOGILEFS_MAX_PATHS 10
typedef enum {
NGX_MOGILEFS_MAIN,
NGX_MOGILEFS_CREATE_OPEN,
NGX_MOGILEFS_CREATE_CLOSE,
NGX_MOGILEFS_FETCH,
} ngx_http_mogilefs_location_type_t;
typedef struct {
ngx_int_t status;
ngx_str_t name;
ngx_flag_t delete_ok;
} ngx_http_mogilefs_error_t;
typedef struct {
ngx_uint_t method;
ngx_str_t name;
ngx_str_t output_param;
ngx_str_t output_count_param;
} ngx_http_mogilefs_cmd_t;
typedef struct {
ngx_str_t source;
ngx_array_t *lengths;
ngx_array_t *values;
} ngx_http_mogilefs_class_template_t;
typedef struct ngx_http_mogilefs_loc_conf_s {
struct ngx_http_mogilefs_loc_conf_s *parent;
ngx_uint_t methods;
ngx_str_t key;
ngx_array_t *key_lengths;
ngx_array_t *key_values;
ngx_int_t index[NGX_MOGILEFS_MAX_PATHS];
ngx_http_upstream_conf_t upstream;
ngx_array_t *tracker_lengths;
ngx_array_t *tracker_values;
ngx_str_t domain;
ngx_array_t *class_templates;
ngx_str_t fetch_location;
ngx_flag_t noverify;
ngx_http_mogilefs_location_type_t location_type;
ngx_str_t create_open_spare_location;
ngx_str_t create_close_spare_location;
} ngx_http_mogilefs_loc_conf_t;
typedef struct {
ngx_str_t name, value;
} ngx_http_mogilefs_aux_param_t;
typedef struct {
ngx_http_mogilefs_cmd_t *cmd;
ngx_array_t sources;
ssize_t num_paths_returned;
ngx_array_t *aux_params;
ngx_str_t key;
ngx_int_t status;
struct sockaddr *peer_addr;
socklen_t peer_addr_len;
} ngx_http_mogilefs_ctx_t;
typedef enum {
START,
CREATE_OPEN,
FETCH,
CREATE_CLOSE,
} ngx_http_mogilefs_put_state_t;
typedef struct {
ngx_http_post_subrequest_t *psr;
ngx_http_mogilefs_put_state_t state;
ngx_int_t status;
ngx_http_mogilefs_ctx_t *create_open_ctx;
ngx_str_t key;
ngx_uint_t num_successful_stores;
} ngx_http_mogilefs_put_ctx_t;
typedef struct {
ssize_t priority;
ngx_str_t path;
} ngx_http_mogilefs_src_t;
static ngx_int_t ngx_http_mogilefs_put_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_mogilefs_finish_phase_handler(ngx_http_request_t *r, void *data, ngx_int_t rc);
static ngx_int_t ngx_http_mogilefs_eval_tracker(ngx_http_request_t *r, ngx_http_mogilefs_loc_conf_t *mgcf);
static ngx_int_t ngx_http_mogilefs_eval_class(ngx_http_request_t *r, ngx_http_mogilefs_loc_conf_t *mgcf);
static ngx_int_t ngx_http_mogilefs_eval_key(ngx_http_request_t *r, ngx_str_t *key);
static ngx_int_t ngx_http_mogilefs_set_cmd(ngx_http_request_t *r, ngx_http_mogilefs_ctx_t *ctx);
static ngx_int_t ngx_http_mogilefs_create_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_mogilefs_reinit_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_mogilefs_process_header(ngx_http_request_t *r);
static void ngx_http_mogilefs_abort_request(ngx_http_request_t *r);
static void ngx_http_mogilefs_finalize_request(ngx_http_request_t *r, ngx_int_t rc);
static ngx_int_t ngx_http_mogilefs_filter_init(void *data);
static ngx_int_t ngx_http_mogilefs_filter(void *data, ssize_t bytes);
static ngx_int_t ngx_http_mogilefs_parse_param(ngx_http_request_t *r, ngx_str_t *param);
static ngx_int_t ngx_http_mogilefs_add_aux_param(ngx_http_request_t *r, ngx_str_t *name,
ngx_str_t *value);
static ngx_int_t ngx_http_mogilefs_path_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static void *ngx_http_mogilefs_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_mogilefs_merge_loc_conf(ngx_conf_t *cf, void *parent,
void *child);
static ngx_int_t ngx_http_mogilefs_add_variables(ngx_conf_t *cf);
static char *
ngx_http_mogilefs_tracker_command(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char *
ngx_http_mogilefs_class_command(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char *
ngx_http_mogilefs_pass_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_int_t ngx_http_mogilefs_init(ngx_conf_t *cf);
static ngx_http_mogilefs_error_t ngx_http_mogilefs_errors[] = {
{NGX_HTTP_NOT_FOUND, ngx_string("unknown_key"), 1},
{NGX_HTTP_NOT_FOUND, ngx_string("domain_not_found"), 0},
{NGX_HTTP_SERVICE_UNAVAILABLE, ngx_string("no_devices"), 0},
{NGX_HTTP_BAD_REQUEST, ngx_string("no_key"), 0},
{NGX_HTTP_BAD_REQUEST, ngx_string("unreg_class"), 0},
{NGX_HTTP_INTERNAL_SERVER_ERROR, ngx_null_string, 0},
};
static ngx_http_mogilefs_cmd_t ngx_http_mogilefs_cmds[] = {
{NGX_HTTP_GET, ngx_string("get_paths"), ngx_string("path"), ngx_string("paths") },
{NGX_HTTP_HEAD, ngx_string("get_paths"), ngx_string("path"), ngx_string("paths") },
{NGX_HTTP_PUT, ngx_string("create_open"), ngx_string("path_"), ngx_string("dev_count") },
{NGX_HTTP_DELETE, ngx_string("delete"), ngx_null_string, ngx_null_string },
{0, ngx_null_string, ngx_null_string, ngx_null_string },
};
static ngx_conf_bitmask_t ngx_http_mogilefs_methods_mask[] = {
{ ngx_string("get"), NGX_HTTP_GET },
{ ngx_string("put"), NGX_HTTP_PUT },
{ ngx_string("delete"), NGX_HTTP_DELETE },
{ ngx_null_string, 0 }
};
static ngx_str_t ngx_http_mogilefs_put_method = { 3, (u_char *) "PUT " };
static ngx_command_t ngx_http_mogilefs_commands[] = {
{ ngx_string("mogilefs_pass"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1|NGX_CONF_BLOCK,
ngx_http_mogilefs_pass_block,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("mogilefs_tracker"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_mogilefs_tracker_command,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("mogilefs_domain"),
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_mogilefs_loc_conf_t, domain),
NULL },
{ ngx_string("mogilefs_connect_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_mogilefs_loc_conf_t, upstream.connect_timeout),
NULL },
{ ngx_string("mogilefs_send_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_mogilefs_loc_conf_t, upstream.send_timeout),
NULL },
{ ngx_string("mogilefs_read_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_mogilefs_loc_conf_t, upstream.read_timeout),
NULL },
{ ngx_string("mogilefs_noverify"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_mogilefs_loc_conf_t, noverify),
NULL },
{ ngx_string("mogilefs_methods"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_mogilefs_loc_conf_t, methods),
&ngx_http_mogilefs_methods_mask },
{ ngx_string("mogilefs_class"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_http_mogilefs_class_command,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_mogilefs_loc_conf_t, class_templates),
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_mogilefs_module_ctx = {
ngx_http_mogilefs_add_variables, /* preconfiguration */
ngx_http_mogilefs_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_mogilefs_create_loc_conf, /* create location configuration */
ngx_http_mogilefs_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_http_mogilefs_module = {
NGX_MODULE_V1,
&ngx_http_mogilefs_module_ctx, /* module context */
ngx_http_mogilefs_commands, /* 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
};
static u_char ngx_http_mogilefs_path_str[] = "mogilefs_path#";
static ngx_str_t ngx_http_mogilefs_path = ngx_string(ngx_http_mogilefs_path_str);
static ngx_http_variable_t ngx_http_mogilefs_path_variable_template = { /* {{{ */
ngx_string(ngx_http_mogilefs_path_str), NULL, ngx_http_mogilefs_path_variable,
(uintptr_t) offsetof(ngx_http_mogilefs_ctx_t, sources),
NGX_HTTP_VAR_CHANGEABLE, 0
}; /* }}} */
static ngx_str_t ngx_http_mogilefs_class = ngx_string("class");
static ngx_str_t ngx_http_mogilefs_size = ngx_string("size");
static ngx_int_t
ngx_http_mogilefs_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_http_upstream_t *u;
ngx_http_mogilefs_ctx_t *ctx;
ngx_http_mogilefs_loc_conf_t *mgcf;
mgcf = ngx_http_get_module_loc_conf(r, ngx_http_mogilefs_module);
if (mgcf->location_type == NGX_MOGILEFS_MAIN) {
if(!(r->method & mgcf->methods)) {
return NGX_HTTP_NOT_ALLOWED;
}
if(r->method & NGX_HTTP_PUT) {
return NGX_DECLINED;
}
}
switch(r->method) {
case NGX_HTTP_GET:
if (ngx_http_set_content_type(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
case NGX_HTTP_DELETE:
rc = ngx_http_discard_request_body(r);
if (rc != NGX_OK) {
return rc;
}
break;
default:
break;
}
u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t));
if (u == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
u->peer.log = r->connection->log;
u->peer.log_error = NGX_ERROR_ERR;
#if (NGX_THREADS)
u->peer.lock = &r->connection->lock;
#endif
u->output.tag = (ngx_buf_tag_t) &ngx_http_mogilefs_module;
u->conf = &mgcf->upstream;
u->create_request = ngx_http_mogilefs_create_request;
u->reinit_request = ngx_http_mogilefs_reinit_request;
u->process_header = ngx_http_mogilefs_process_header;
u->abort_request = ngx_http_mogilefs_abort_request;
u->finalize_request = ngx_http_mogilefs_finalize_request;
r->upstream = u;
ctx = ngx_http_get_module_ctx(r, ngx_http_mogilefs_module);
if(ctx == NULL) {
ctx = ngx_palloc(r->pool, sizeof(ngx_http_mogilefs_ctx_t));
if (ctx == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ctx->peer_addr = NULL;
ctx->peer_addr_len = 0;
ctx->num_paths_returned = -1;
ctx->aux_params = NULL;
ctx->status = 0;
ngx_array_init(&ctx->sources, r->pool, 1, sizeof(ngx_http_mogilefs_src_t));
if(ngx_http_mogilefs_eval_key(r, &ctx->key) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_http_set_ctx(r, ctx, ngx_http_mogilefs_module);
}
u->input_filter_init = ngx_http_mogilefs_filter_init;
u->input_filter = ngx_http_mogilefs_filter;
u->input_filter_ctx = ctx;
if (mgcf->tracker_lengths != 0) {
if (ngx_http_mogilefs_eval_tracker(r, mgcf) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
}
if(ngx_http_mogilefs_set_cmd(r, ctx) != NGX_OK) {
return NGX_ERROR;
}
#if defined nginx_version && nginx_version >= 8011
r->main->count++;
#endif
ngx_http_upstream_init(r);
return NGX_DONE;
}
static void
ngx_http_mogilefs_body_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"mogilefs body handler");
rc = ngx_http_mogilefs_put_handler(r);
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
ngx_http_finalize_request(r, rc);
}
}
static ngx_int_t
ngx_http_mogilefs_put_handler(ngx_http_request_t *r)
{
ngx_http_mogilefs_put_ctx_t *ctx;
ngx_str_t args;
ngx_uint_t flags;
ngx_http_request_t *sr;
ngx_str_t spare_location = ngx_null_string, uri, value;
ngx_int_t rc;
u_char *p;
ngx_http_core_loc_conf_t *clcf;
ngx_http_mogilefs_loc_conf_t *mgcf;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"mogilefs put handler");
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
mgcf = ngx_http_get_module_loc_conf(r, ngx_http_mogilefs_module);
if (clcf->handler != ngx_http_mogilefs_handler ||
(mgcf->location_type == NGX_MOGILEFS_MAIN && !(r->method & mgcf->methods)))
{
return NGX_DECLINED;
}
ctx = ngx_http_get_module_ctx(r, ngx_http_mogilefs_module);
if(ctx == NULL) {
ctx = ngx_palloc(r->pool, sizeof(ngx_http_mogilefs_put_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
ctx->psr = NULL;
ctx->state = START;
ctx->status = 0;
ctx->create_open_ctx = NULL;
if(ngx_http_mogilefs_eval_key(r, &ctx->key) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_http_set_ctx(r, ctx, ngx_http_mogilefs_module);
}
if(ctx->psr == NULL) {
ctx->psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
if (ctx->psr == NULL) {
return NGX_ERROR;
}
}
if(r->request_body == NULL) {
rc = ngx_http_read_client_request_body(r, ngx_http_mogilefs_body_handler);
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
return rc;
}
return NGX_DONE;
}
// Still receiving body?
if(r->request_body->rest) {
return NGX_DONE;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"mogilefs put handler state: %ui, status: %i", ctx->state, ctx->status);
if(ctx->state == CREATE_OPEN || ctx->state == FETCH || ctx->state == CREATE_CLOSE) {
if(ctx->status != NGX_OK && ctx->status != NGX_HTTP_CREATED && ctx->status != NGX_HTTP_NO_CONTENT) {
return (ctx->status >= NGX_HTTP_SPECIAL_RESPONSE) ?
ctx->status : NGX_HTTP_INTERNAL_SERVER_ERROR;
}
}
switch(ctx->state) {
case START:
spare_location = mgcf->create_open_spare_location;
ctx->state = CREATE_OPEN;
break;
case CREATE_OPEN:
spare_location = mgcf->fetch_location;
ctx->state = FETCH;
break;
case FETCH:
#if defined nginx_version && nginx_version >= 8011
r->main->count++;
#endif
spare_location = mgcf->create_close_spare_location;
ctx->state = CREATE_CLOSE;
#if defined nginx_version && nginx_version >= 8011
r->main->count++;
#endif
break;
case CREATE_CLOSE:
#if defined nginx_version && nginx_version >= 8011
r->main->count++;
#endif
r->headers_out.content_length_n = 0;
r->headers_out.status = NGX_HTTP_CREATED;
r->header_only = 1;
return ngx_http_send_header(r);
}
uri.len = spare_location.len + ctx->key.len;
uri.data = ngx_palloc(r->pool, uri.len);
p = ngx_cpymem(uri.data, spare_location.data, spare_location.len);
p = ngx_cpymem(p, ctx->key.data, ctx->key.len);
args.len = 0;
args.data = NULL;
flags = 0;
if (ngx_http_parse_unsafe_uri(r, &uri, &args, &flags) != NGX_OK) {
return NGX_ERROR;
}
ctx->psr->handler = ngx_http_mogilefs_finish_phase_handler;
ctx->psr->data = ctx;
flags |= NGX_HTTP_SUBREQUEST_WAITED;
if(ctx->state == FETCH) {
flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY;
}
rc = ngx_http_subrequest(r, &uri, &args, &sr, ctx->psr, flags);
if (rc == NGX_ERROR) {
return rc;
}
if(ctx->state == CREATE_CLOSE) {
ngx_http_set_ctx(sr, ctx->create_open_ctx, ngx_http_mogilefs_module);
value.data = ngx_palloc(r->pool, NGX_OFF_T_LEN);
if(value.data == NULL) {
return NGX_ERROR;
}
value.len = ngx_sprintf(value.data, "%O", r->headers_in.content_length_n)
- value.data;
if(ngx_http_mogilefs_add_aux_param(sr, &ngx_http_mogilefs_size, &value) != NGX_OK) {
return NGX_ERROR;
}
}
/*
* Nginx closes temporary file with buffered body
* whenever it starts sending reponse from upstream
* and it is not doing subrequest in memory.
*
* Since the request body in create_open subrequest is
* inherited from main request, it is necessary to prevent
* nginx from closing the temporary file with request body,
* before it could be passed to the storage node on fetch/store
* stage.
*
* We do it by "hiding" the request body from nginx internals.
*/
if(ctx->state == CREATE_OPEN) {
sr->request_body = NULL;
}
sr->method = NGX_HTTP_PUT;
sr->method_name = ngx_http_mogilefs_put_method;
/*
* Wait for subrequest to complete
*/
return NGX_DONE;
}
static ngx_int_t
ngx_http_mogilefs_finish_phase_handler(ngx_http_request_t *r, void *data, ngx_int_t rc)
{
ngx_http_mogilefs_put_ctx_t *ctx = data;
ngx_http_mogilefs_ctx_t *subrequest_ctx;
subrequest_ctx = ngx_http_get_module_ctx(r, ngx_http_mogilefs_module);
if(ctx->state == CREATE_OPEN) {
ctx->create_open_ctx = subrequest_ctx;
}
ctx->status = (subrequest_ctx != NULL && subrequest_ctx->status >= NGX_HTTP_SPECIAL_RESPONSE)
? subrequest_ctx->status : rc;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"mogilefs finish phase handler: state=%ui, status=%i", ctx->state, ctx->status);
return NGX_OK;
}
static ngx_int_t
ngx_http_mogilefs_eval_tracker(ngx_http_request_t *r, ngx_http_mogilefs_loc_conf_t *mgcf)
{
ngx_str_t tracker;
ngx_http_upstream_t *u;
ngx_http_mogilefs_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_mogilefs_module);
if (ngx_http_script_run(r, &tracker, mgcf->tracker_lengths->elts, 0,
mgcf->tracker_values->elts)
== NULL)
{
return NGX_ERROR;
}
u = r->upstream;
u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));
if (u->resolved == NULL) {
return NGX_ERROR;
}
if(ctx->peer_addr == NULL) {
u->resolved->host = tracker;
u->resolved->no_port = 1;
}
else {
u->resolved->sockaddr = ctx->peer_addr;
u->resolved->socklen = ctx->peer_addr_len;
u->resolved->naddrs = 1;
u->resolved->host = tracker;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_mogilefs_eval_class(ngx_http_request_t *r, ngx_http_mogilefs_loc_conf_t *mgcf)
{
ngx_uint_t i;
ngx_http_mogilefs_class_template_t *t;
ngx_str_t class;
if(mgcf->class_templates == NULL) {
return NGX_DECLINED;
}
t = mgcf->class_templates->elts;
for(i = 0;i < mgcf->class_templates->nelts;i++) {
if(t->lengths != NULL && t->values != NULL) {
if(ngx_http_script_run(r, &class, t->lengths->elts, 0,
t->values->elts)
== NULL)
{
return NGX_ERROR;
}
}
else {
if(ngx_http_mogilefs_add_aux_param(r, &ngx_http_mogilefs_class, &t->source) != NGX_OK) {
return NGX_ERROR;
}
return NGX_OK;
}
if(class.len) {
if(ngx_http_mogilefs_add_aux_param(r, &ngx_http_mogilefs_class, &class) != NGX_OK) {
return NGX_ERROR;
}
return NGX_OK;
}
t++;
}
return NGX_DECLINED;
}
static ngx_int_t
ngx_http_mogilefs_eval_key(ngx_http_request_t *r, ngx_str_t *key)
{
size_t loc_len;
ngx_http_mogilefs_loc_conf_t *mgcf;
ngx_http_core_loc_conf_t *clcf;
mgcf = ngx_http_get_module_loc_conf(r, ngx_http_mogilefs_module);
/*
* If key is empty take the remaining part of request URI,
* otherwise run script to obtain key
*/
if(mgcf->key.len == 0) {
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
loc_len = r->valid_location ? clcf->name.len : 0;
key->data = r->uri.data + loc_len;
key->len = r->uri.len - loc_len;
}
else {
if(mgcf->key_lengths != NULL) {
if (ngx_http_script_run(r, key, mgcf->key_lengths->elts, 0,
mgcf->key_values->elts)
== NULL)
{
return NGX_ERROR;
}
}
else {
*key = mgcf->key;
}
}
return NGX_OK;
}
static ngx_int_t
ngx_http_mogilefs_set_cmd(ngx_http_request_t *r, ngx_http_mogilefs_ctx_t *ctx)
{
ngx_http_mogilefs_cmd_t *c;
c = ngx_http_mogilefs_cmds;
while(c->name.data != NULL) {
if(c->method & r->method)
break;
c++;
}
if(c->name.data != NULL) {
ctx->cmd = c;
return NGX_OK;
}
return NGX_ERROR;
}
static ngx_int_t
ngx_http_mogilefs_create_request(ngx_http_request_t *r)
{
size_t len;
uintptr_t escape_domain, escape_key;
ngx_str_t cmd;
ngx_buf_t *b;
ngx_chain_t *cl;
ngx_http_mogilefs_loc_conf_t *mgcf;
ngx_str_t request, domain;
ngx_http_mogilefs_ctx_t *ctx;
ngx_http_mogilefs_aux_param_t *a;
ngx_uint_t i;
ngx_int_t rc;
mgcf = ngx_http_get_module_loc_conf(r, ngx_http_mogilefs_module);
ctx = ngx_http_get_module_ctx(r, ngx_http_mogilefs_module);
/*
* Save peer address, so that we contact the same host while doing create_close
*/
if(mgcf->location_type == NGX_MOGILEFS_CREATE_OPEN && ctx->cmd->method & NGX_HTTP_PUT) {
if(r->upstream->peer.sockaddr != NULL) {
ctx->peer_addr = ngx_palloc(r->main->pool, r->upstream->peer.socklen);
if(ctx->peer_addr == NULL) {
return NGX_ERROR;
}
ngx_memcpy(ctx->peer_addr, r->upstream->peer.sockaddr, r->upstream->peer.socklen);
ctx->peer_addr_len = r->upstream->peer.socklen;
}
}
cmd = ctx->cmd->name;
if(mgcf->location_type == NGX_MOGILEFS_CREATE_CLOSE && ctx->cmd->method & NGX_HTTP_PUT) {
cmd.data = (u_char*)"create_close";
cmd.len = sizeof("create_close") - 1;
}
if(ctx->key.len == 0) {
return NGX_HTTP_BAD_REQUEST;
}
if(mgcf->parent != NULL) {
domain.len = mgcf->parent->domain.len;
domain.data = mgcf->parent->domain.data;
}
else {
domain.len = mgcf->domain.len;
domain.data = mgcf->domain.data;
}
rc = ngx_http_mogilefs_eval_class(r, mgcf->parent != NULL ? mgcf->parent : mgcf);
if(rc == NGX_ERROR) {
return rc;
}
escape_domain = 2 * ngx_escape_uri(NULL, domain.data, domain.len, NGX_ESCAPE_MEMCACHED);
escape_key = 2 * ngx_escape_uri(NULL, ctx->key.data, ctx->key.len, NGX_ESCAPE_MEMCACHED);
len = cmd.len + 1 + sizeof("key=") - 1 + ctx->key.len + escape_key + 1 +
sizeof("domain=") - 1 + domain.len + escape_domain + sizeof(CRLF) - 1 +
(mgcf->noverify ? 1 + sizeof("noverify=1") - 1 : 0);
if(ctx->aux_params != NULL && ctx->aux_params->nelts) {
a = ctx->aux_params->elts;
for (i = 0; i < ctx->aux_params->nelts; i++) {
len += a[i].name.len + 1 + 1 + a[i].value.len;
}
}
b = ngx_create_temp_buf(r->pool, len);
if (b == NULL) {
return NGX_ERROR;
}
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf = b;
cl->next = NULL;
r->upstream->request_bufs = cl;
b->last = ngx_copy(b->last, cmd.data, cmd.len);
*b->last++ = ' ';
b->last = ngx_copy(b->last, "key=", sizeof("key=") - 1);
if (escape_key == 0) {
b->last = ngx_copy(b->last, ctx->key.data, ctx->key.len);
} else {
b->last = (u_char *) ngx_escape_uri(b->last, ctx->key.data, ctx->key.len,
NGX_ESCAPE_MEMCACHED);
}
*b->last++ = '&';
b->last = ngx_copy(b->last, "domain=", sizeof("domain=") - 1);
if (escape_domain == 0) {
b->last = ngx_copy(b->last, domain.data, domain.len);
} else {
b->last = (u_char *) ngx_escape_uri(b->last, domain.data, domain.len,
NGX_ESCAPE_MEMCACHED);
}
if(mgcf->noverify) {
*b->last++ = '&';
b->last = ngx_copy(b->last, "noverify=1", sizeof("noverify=1") - 1);
}
if(ctx->aux_params != NULL && ctx->aux_params->nelts) {
a = ctx->aux_params->elts;
for (i = 0; i < ctx->aux_params->nelts; i++) {
*b->last++ = '&';
b->last = ngx_copy(b->last, a[i].name.data, a[i].name.len);
*b->last++ = '=';
b->last = ngx_copy(b->last, a[i].value.data, a[i].value.len);
}
}
request.data = b->pos;
request.len = b->last - b->pos;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"mogilefs request: \"%V\"", &request);
*b->last++ = CR; *b->last++ = LF;
return NGX_OK;
}
static ngx_int_t
ngx_http_mogilefs_reinit_request(ngx_http_request_t *r)
{
return NGX_OK;
}
static int ngx_libc_cdecl ngx_http_mogilefs_cmp_sources(const void *one,
const void *two)
{
ngx_http_mogilefs_src_t *first, *second;
first = (ngx_http_mogilefs_src_t *) one;
second = (ngx_http_mogilefs_src_t *) two;
return first->priority - second->priority;
}
static ngx_int_t
ngx_http_mogilefs_process_ok_response(ngx_http_request_t *r,
ngx_http_upstream_t *u, ngx_str_t *line)
{
u_char *p;
ngx_str_t param;
ngx_int_t rc;
ngx_table_elt_t *h;
ngx_http_upstream_header_t *hh;
ngx_http_upstream_main_conf_t *umcf;
ngx_http_mogilefs_loc_conf_t *mgcf;
ngx_http_variable_value_t *v;
ngx_http_mogilefs_ctx_t *ctx;
ngx_http_mogilefs_src_t *source;
ngx_uint_t i;
line->data += sizeof("OK ") - 1;
line->len -= sizeof("OK ") - 1;
p = line->data;
param.data = p;
param.len = 0;
while (*p != LF) {
if (*p == '&' || *p == CR) {
if(param.len != 0) {
rc = ngx_http_mogilefs_parse_param(r, &param);
if(rc != NGX_OK) {
return rc;
}
p++;
param.data = p;
param.len = 0;
}
if(*p == CR) {
break;
}
else {
continue;
}
}
param.len++;
p++;
}
ctx = ngx_http_get_module_ctx(r, ngx_http_mogilefs_module);
/*
* Convert ok response to delete into No content
*/
if(ctx->cmd->method & NGX_HTTP_DELETE) {
r->headers_out.content_length_n = 0;
u->headers_in.status_n = NGX_HTTP_NO_CONTENT;
u->state->status = NGX_HTTP_NO_CONTENT;
// Return no content
u->buffer.pos = u->buffer.pos;
return NGX_OK;
}
/*
* If no paths retuned, but response was ok, tell the client it's unavailable
*/
if((ctx->num_paths_returned <= 0 && (!(ctx->cmd->method & NGX_HTTP_PUT))) || ctx->sources.nelts == 0)
{
r->headers_out.content_length_n = 0;
u->headers_in.status_n = NGX_HTTP_SERVICE_UNAVAILABLE;
u->state->status = NGX_HTTP_SERVICE_UNAVAILABLE;
// Return no content
u->buffer.pos = u->buffer.pos;
return NGX_OK;
}
/*
* Sort sources and choose top source
*/
if(ctx->sources.nelts > 1) {
ngx_qsort(ctx->sources.elts, ctx->sources.nelts, sizeof(ngx_http_mogilefs_src_t),
ngx_http_mogilefs_cmp_sources);
}
mgcf = ngx_http_get_module_loc_conf(r, ngx_http_mogilefs_module);
source = ctx->sources.elts;
/*
* Save peer address, so that we contact the same host while doing create_close
*/
if(mgcf->location_type == NGX_MOGILEFS_CREATE_OPEN && ctx->cmd->method & NGX_HTTP_PUT) {
if(r->upstream->peer.sockaddr != NULL) {
ctx->peer_addr = ngx_palloc(r->main->pool, r->upstream->peer.socklen);
if(ctx->peer_addr == NULL) {
return NGX_ERROR;
}
ngx_memcpy(ctx->peer_addr, r->upstream->peer.sockaddr, r->upstream->peer.socklen);
ctx->peer_addr_len = r->upstream->peer.socklen;
}
}
/*
* Set $mogilefs_path variables
*/
for(i=0;i < ctx->sources.nelts;i++) {
v = r->variables + mgcf->index[i];
v->data = source[i].path.data;
v->len = source[i].path.len;
v->not_found = 0;
v->no_cacheable = 0;
v->valid = 1;
}
/*
* Redirect to fetch location
*/
if (ctx->cmd->method & (NGX_HTTP_GET|NGX_HTTP_HEAD) && r->upstream->headers_in.x_accel_redirect == NULL) {
umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
h = ngx_list_push(&r->upstream->headers_in.headers);
if (h == NULL) {
return NGX_ERROR;
}
h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(
ngx_hash('x', '-'), 'a'), 'c'), 'c'), 'e'), 'l'), '-'), 'r'), 'e'), 'd'), 'i'), 'r'), 'e'), 'c'), 't');
h->key.len = sizeof("X-Accel-Redirect") - 1;
h->key.data = (u_char *) "X-Accel-Redirect";
h->value = mgcf->fetch_location;
h->lowcase_key = (u_char *) "x-accel-redirect";
hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
h->lowcase_key, h->key.len);
if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
return NGX_ERROR;
}
}
r->headers_out.content_length_n = 0;
u->headers_in.status_n = 200;
u->state->status = 200;
// Return no content
u->buffer.pos = u->buffer.pos;
return NGX_OK;
}
static ngx_int_t
ngx_http_mogilefs_process_error_response(ngx_http_request_t *r,
ngx_http_upstream_t *u, ngx_str_t *line)
{
ngx_http_mogilefs_error_t *e;
ngx_http_mogilefs_ctx_t *ctx;
line->data += sizeof("ERR ") - 1;
line->len -= sizeof("ERR ") - 1;
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"mogilefs error: \"%V\"", line);
e = ngx_http_mogilefs_errors;
while(e->name.data != NULL) {
if(line->len >= e->name.len &&
ngx_strncmp(line->data, e->name.data, e->name.len) == 0)
{
break;
}
e++;
}
ctx = ngx_http_get_module_ctx(r, ngx_http_mogilefs_module);
/*
* Convert unknown_key response to delete into No content
*/
if(ctx->cmd->method & NGX_HTTP_DELETE && e->delete_ok) {
r->headers_out.content_length_n = 0;
u->headers_in.status_n = NGX_HTTP_NO_CONTENT;
u->state->status = NGX_HTTP_NO_CONTENT;
// Return no content
u->buffer.pos = u->buffer.pos;
return NGX_OK;
}
r->headers_out.content_length_n = 0;
u->headers_in.status_n = e->status;
u->state->status = e->status;
ctx->status = e->status;
// Return no content
u->buffer.pos = u->buffer.pos;
return NGX_OK;
}
static ngx_int_t
ngx_http_mogilefs_add_aux_param(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value)
{
ngx_http_mogilefs_ctx_t *ctx;
ngx_http_mogilefs_aux_param_t *p;
ctx = ngx_http_get_module_ctx(r, ngx_http_mogilefs_module);
if(ctx == NULL) {
return NGX_ERROR;
}
if(ctx->aux_params == NULL) {
ctx->aux_params = ngx_array_create(r->pool, 3, sizeof(ngx_http_mogilefs_aux_param_t));
if(ctx->aux_params == NULL) {
return NGX_ERROR;
}
}
p = ngx_array_push(ctx->aux_params);
if (p == NULL) {
return NGX_ERROR;
}
p->name = *name;
p->value = *value;
return NGX_OK;
}
static ngx_int_t
ngx_http_mogilefs_parse_param(ngx_http_request_t *r, ngx_str_t *param) {
u_char *p, *src, *dst;
ngx_str_t name;
ngx_str_t value;
ngx_http_mogilefs_ctx_t *ctx;
ngx_http_mogilefs_src_t *source;
p = (u_char *) ngx_strchr(param->data, '=');
if(p == NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"mogilefs tracker has sent invalid param: \"%V\"", param);
return NGX_ERROR;
}
name.data = param->data;
name.len = p - param->data;
value.data = p + 1;
value.len = param->len - (p - param->data) - 1;
src = dst = value.data;
ngx_unescape_uri(&dst, &src, value.len, NGX_UNESCAPE_URI);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"mogilefs param: \"%V\"=\"%V\"", &name, &value);
ctx = ngx_http_get_module_ctx(r, ngx_http_mogilefs_module);
if(name.len == sizeof("path") - 1
&& ngx_strncmp(name.data, "path", sizeof("path") - 1) == 0)
{
source = ngx_array_push(&ctx->sources);
if(source == NULL) {
return NGX_ERROR;
}
source->priority = 0;
source->path = value;
if(ngx_http_mogilefs_add_aux_param(r, &name, &value) != NGX_OK) {
return NGX_ERROR;
}
}
else if(name.len >= ctx->cmd->output_param.len
&& ngx_strncmp(name.data, ctx->cmd->output_param.data, ctx->cmd->output_param.len) == 0
&& ngx_atoi(name.data + ctx->cmd->output_param.len, name.len - ctx->cmd->output_param.len) != NGX_ERROR)
{
source = ngx_array_push(&ctx->sources);
if(source == NULL) {
return NGX_ERROR;
}
source->priority = ngx_atoi(name.data + ctx->cmd->output_param.len, name.len - ctx->cmd->output_param.len);
source->path = value;
}
else if(name.len == ctx->cmd->output_count_param.len &&
ngx_strncmp(name.data, ctx->cmd->output_count_param.data, ctx->cmd->output_count_param.len) == 0)
{
ctx->num_paths_returned = ngx_atoi(value.data, value.len);
}
else {
if(ngx_http_mogilefs_add_aux_param(r, &name, &value) != NGX_OK) {
return NGX_ERROR;
}
}
return NGX_OK;
}
static ngx_int_t
ngx_http_mogilefs_process_header(ngx_http_request_t *r)
{
u_char *p;
ngx_str_t line;
ngx_http_upstream_t *u;
u = r->upstream;
for (p = u->buffer.pos; p < u->buffer.last; p++) {
if (*p == LF) {
goto found;
}
}
return NGX_AGAIN;
found:
line.len = p - u->buffer.pos;
line.data = u->buffer.pos;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"mogilefs: \"%V\"", &line);
if (line.len >= sizeof("ERR ") - 1 &&
ngx_strncmp(line.data, "ERR ", sizeof("ERR ") - 1) == 0)
{
return ngx_http_mogilefs_process_error_response(r, u, &line);
}
if (line.len >= sizeof("OK ") - 1 &&
ngx_strncmp(line.data, "OK ", sizeof("OK ") - 1) == 0)
{
return ngx_http_mogilefs_process_ok_response(r, u, &line);
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"mogilefs tracker has sent invalid response: \"%V\"", &line);
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
static void
ngx_http_mogilefs_abort_request(ngx_http_request_t *r)
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"abort mogilefs request");
return;
}
static void
ngx_http_mogilefs_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"finalize mogilefs request");
return;
}
static ngx_int_t
ngx_http_mogilefs_filter_init(void *data)
{
return NGX_OK;
}
static ngx_int_t
ngx_http_mogilefs_filter(void *data, ssize_t bytes)
{
return NGX_OK;
}
static void *
ngx_http_mogilefs_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_mogilefs_loc_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_mogilefs_loc_conf_t));
if (conf == NULL) {
return NGX_CONF_ERROR;
}
conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;
/* 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;
conf->noverify = NGX_CONF_UNSET;
conf->methods = 0;
return conf;
}
static char *
ngx_http_mogilefs_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_mogilefs_loc_conf_t *prev = parent;
ngx_http_mogilefs_loc_conf_t *conf = child;
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->upstream.read_timeout,
prev->upstream.read_timeout, 60000);
ngx_conf_merge_size_value(conf->upstream.buffer_size,
prev->upstream.buffer_size,
(size_t) ngx_pagesize);
ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
prev->upstream.next_upstream,
(NGX_CONF_BITMASK_SET
|NGX_HTTP_UPSTREAM_FT_ERROR
|NGX_HTTP_UPSTREAM_FT_TIMEOUT));
if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {
conf->upstream.next_upstream = NGX_CONF_BITMASK_SET
|NGX_HTTP_UPSTREAM_FT_OFF;
}
if (conf->upstream.upstream == NULL) {
conf->upstream.upstream = prev->upstream.upstream;
}
ngx_conf_merge_str_value(conf->domain, prev->domain, "default");
ngx_conf_merge_value(conf->noverify, prev->noverify, 0);
ngx_conf_merge_bitmask_value(conf->methods, prev->methods,
(NGX_CONF_BITMASK_SET|NGX_HTTP_GET));
if(conf->methods & NGX_HTTP_GET) {
conf->methods |= NGX_HTTP_HEAD;
}
if(conf->class_templates == NULL) {
conf->class_templates = prev->class_templates;
}
return NGX_CONF_OK;
}
static ngx_int_t ngx_http_mogilefs_add_variables(ngx_conf_t *cf)
{
ngx_uint_t i;
ngx_http_variable_t *var, *v;
ngx_str_t name;
/*
* Add 10 instances of mogilefs_path variable with
* different names
*/
v = &ngx_http_mogilefs_path_variable_template;
for(i=0;i<NGX_MOGILEFS_MAX_PATHS;i++) {
name.data = v->name.data;
name.len = v->name.len - 1;
if(i > 0) {
name.data[name.len] = '0' + i;
name.len++;
}
var = ngx_http_add_variable(cf, &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_mogilefs_path_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_mogilefs_tracker_command(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_mogilefs_loc_conf_t *mgcf = conf;
ngx_str_t *value;
ngx_url_t u;
ngx_uint_t n;
ngx_http_script_compile_t sc;
if (mgcf->upstream.upstream || mgcf->tracker_lengths) {
return "is duplicate";
}
value = cf->args->elts;
n = ngx_http_script_variables_count(&value[1]);
if(n) {
ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
sc.cf = cf;
sc.source = &value[1];
sc.lengths = &mgcf->tracker_lengths;
sc.values = &mgcf->tracker_values;
sc.variables = n;
sc.complete_lengths = 1;
sc.complete_values = 1;
if (ngx_http_script_compile(&sc) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
ngx_memzero(&u, sizeof(ngx_url_t));
u.url = value[1];
u.no_resolve = 1;
u.default_port = 6001;
mgcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
if (mgcf->upstream.upstream == NULL) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static char *
ngx_http_mogilefs_class_command(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_mogilefs_loc_conf_t *mgcf = conf;
ngx_http_mogilefs_class_template_t *t;
ngx_str_t *value;
ngx_uint_t i, n;
ngx_http_script_compile_t sc;
if(mgcf->class_templates == NULL) {
mgcf->class_templates = ngx_array_create(cf->pool, cf->args->nelts,
sizeof(ngx_http_mogilefs_class_template_t));
if(mgcf->class_templates == NULL) {
return NGX_CONF_ERROR;
}
}
value = cf->args->elts;
for(i = 1;i < cf->args->nelts;i++) {
t = ngx_array_push(mgcf->class_templates);
if(t == NULL) {
return NGX_CONF_ERROR;
}
t->source = value[i];
t->lengths = NULL;
t->values = NULL;
n = ngx_http_script_variables_count(&t->source);
if(n) {
ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
sc.cf = cf;
sc.source = &value[i];
sc.lengths = &t->lengths;
sc.values = &t->values;
sc.variables = n;
sc.complete_lengths = 1;
sc.complete_values = 1;
if (ngx_http_script_compile(&sc) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
}
return NGX_CONF_OK;
}
static char*
ngx_http_mogilefs_create_spare_location(ngx_conf_t *cf, ngx_http_conf_ctx_t **octx, ngx_str_t *name,
ngx_http_mogilefs_location_type_t location_type)
{
ngx_http_mogilefs_loc_conf_t *mgcf, *pmgcf;
ngx_http_conf_ctx_t *ctx, *pctx = cf->ctx;
ngx_uint_t i;
ngx_http_module_t *module;
void *mconf;
ngx_http_core_loc_conf_t *clcf, *pclcf, *rclcf;
ngx_http_core_srv_conf_t *cscf;
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
if (ctx == NULL) {
return NGX_CONF_ERROR;
}
pctx = cf->ctx;
ctx->main_conf = pctx->main_conf;
ctx->srv_conf = pctx->srv_conf;
ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
if (ctx->loc_conf == NULL) {
return NGX_CONF_ERROR;
}
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->type != NGX_HTTP_MODULE) {
continue;
}
module = ngx_modules[i]->ctx;
if (module->create_loc_conf) {
mconf = module->create_loc_conf(cf);
if (mconf == NULL) {
return NGX_CONF_ERROR;
}
ctx->loc_conf[ngx_modules[i]->ctx_index] = mconf;
}
}
pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index];
clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];
mgcf = ctx->loc_conf[ngx_http_mogilefs_module.ctx_index];
mgcf->location_type = location_type;
if(location_type != NGX_MOGILEFS_FETCH) {
pmgcf = pctx->loc_conf[ngx_http_mogilefs_module.ctx_index];
mgcf->methods = NGX_HTTP_PUT;
/*
* Copy tracker configuration
*/
mgcf->tracker_lengths = pmgcf->tracker_lengths;
mgcf->tracker_values = pmgcf->tracker_values;
mgcf->parent = pmgcf;
ngx_memcpy(&mgcf->upstream, &pmgcf->upstream, sizeof(ngx_http_upstream_conf_t));
ngx_memcpy(mgcf->index, pmgcf->index, NGX_MOGILEFS_MAX_PATHS * sizeof(ngx_int_t));
clcf->handler = ngx_http_mogilefs_handler;
}
name->len = sizeof("/mogstored_spare_") - 1 + NGX_OFF_T_LEN + 1;
name->data = ngx_palloc(cf->pool, name->len);
if(name->data == NULL) {
return NGX_CONF_ERROR;
}
name->len = ngx_sprintf(name->data, "/mogstored_spare_%O/", (off_t)(uintptr_t)clcf) - name->data;
clcf->loc_conf = ctx->loc_conf;
clcf->name = *name;
clcf->exact_match = 0;
clcf->noname = 0;
clcf->internal = 1;
clcf->noregex = 1;
cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module);
rclcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index];
if (ngx_http_add_location(cf, &rclcf->locations, clcf) != NGX_OK) {
return NGX_CONF_ERROR;
}
if(octx != NULL) {
*octx = ctx;
}
return NGX_CONF_OK;
}
static char *
ngx_http_mogilefs_pass_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_mogilefs_loc_conf_t *pmgcf = conf;
ngx_http_core_loc_conf_t *pclcf;
ngx_http_conf_ctx_t *ctx;
char *rv;
ngx_str_t *value;
ngx_conf_t save;
ngx_http_script_compile_t sc;
ngx_uint_t n, i;
char *rc;
ngx_str_t name;
if (pmgcf->fetch_location.len != 0) {
return "is duplicate";
}
if (pmgcf->upstream.upstream == 0 && pmgcf->tracker_lengths == NULL) {
return "no tracker defined";
}
for(i=0;i < NGX_MOGILEFS_MAX_PATHS;i++) {
name.data = ngx_http_mogilefs_path.data;
name.len = ngx_http_mogilefs_path.len - 1;
if(i > 0) {
name.data[name.len] = '0' + i;
name.len++;
}
pmgcf->index[i] = ngx_http_get_variable_index(cf, &name);
if (pmgcf->index[i] == NGX_ERROR) {
return NGX_CONF_ERROR;
}
}
rc = ngx_http_mogilefs_create_spare_location(cf, NULL, &pmgcf->create_open_spare_location,
NGX_MOGILEFS_CREATE_OPEN);
if(rc != NGX_CONF_OK) {
return rc;
}
rc = ngx_http_mogilefs_create_spare_location(cf, &ctx, &pmgcf->fetch_location,
NGX_MOGILEFS_FETCH);
if(rc != NGX_CONF_OK) {
return rc;
}
rc = ngx_http_mogilefs_create_spare_location(cf, NULL, &pmgcf->create_close_spare_location,
NGX_MOGILEFS_CREATE_CLOSE);
if(rc != NGX_CONF_OK) {
return rc;
}
pmgcf->location_type = NGX_MOGILEFS_MAIN;
pclcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
pclcf->handler = ngx_http_mogilefs_handler;
if(cf->args->nelts > 1) {
value = cf->args->elts;
pmgcf->key = value[1];
n = ngx_http_script_variables_count(&pmgcf->key);
ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
sc.cf = cf;
sc.source = &pmgcf->key;
sc.lengths = &pmgcf->key_lengths;
sc.values = &pmgcf->key_values;
sc.variables = n;
sc.complete_lengths = 1;
sc.complete_values = 1;
if (ngx_http_script_compile(&sc) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
save = *cf;
cf->ctx = ctx;
cf->cmd_type = NGX_HTTP_LOC_CONF;
rv = ngx_conf_parse(cf, NULL);
*cf = save;
return rv;
}
static ngx_int_t
ngx_http_mogilefs_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_mogilefs_put_handler;
return NGX_OK;
}