1 
2 /*
3  * Copyright (C) 2009 Valery Kholodkov
4  */
5 
6 
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_http.h>
10 #include <nginx.h>
11 
12 /*
13  * NOTE: Once you change the value of this macro to >10,
14  * you need to do adapt the code accordingly
15  */
16 #define NGX_MOGILEFS_MAX_PATHS  10
17 
18 typedef enum {
19     NGX_MOGILEFS_MAIN,
20     NGX_MOGILEFS_CREATE_OPEN,
21     NGX_MOGILEFS_CREATE_CLOSE,
22     NGX_MOGILEFS_FETCH,
23 } ngx_http_mogilefs_location_type_t;
24 
25 typedef struct {
26     ngx_int_t                status;
27     ngx_str_t                name;
28     ngx_flag_t               delete_ok;
29 } ngx_http_mogilefs_error_t;
30 
31 typedef struct {
32     ngx_uint_t               method;
33     ngx_str_t                name;
34     ngx_str_t                output_param;
35     ngx_str_t                output_count_param;
36 } ngx_http_mogilefs_cmd_t;
37 
38 typedef struct {
39     ngx_str_t                   source;
40     ngx_array_t                *lengths;
41     ngx_array_t                *values;
42 } ngx_http_mogilefs_class_template_t;
43 
44 typedef struct ngx_http_mogilefs_loc_conf_s {
45     struct ngx_http_mogilefs_loc_conf_s *parent;
46     ngx_uint_t                 methods;
47     ngx_str_t                  key;
48     ngx_array_t                *key_lengths;
49     ngx_array_t                *key_values;
50     ngx_int_t                  index[NGX_MOGILEFS_MAX_PATHS];
51     ngx_http_upstream_conf_t   upstream;
52     ngx_array_t                *tracker_lengths;
53     ngx_array_t                *tracker_values;
54     ngx_str_t                  domain;
55     ngx_array_t                *class_templates;
56     ngx_str_t                  fetch_location;
57     ngx_flag_t                 noverify;
58     ngx_http_mogilefs_location_type_t location_type;
59     ngx_str_t                  create_open_spare_location;
60     ngx_str_t                  create_close_spare_location;
61 } ngx_http_mogilefs_loc_conf_t;
62 
63 typedef struct {
64     ngx_str_t                 name, value;
65 } ngx_http_mogilefs_aux_param_t;
66 
67 typedef struct {
68     ngx_http_mogilefs_cmd_t  *cmd;
69     ngx_array_t               sources;
70     ssize_t                   num_paths_returned;
71     ngx_array_t              *aux_params;
72     ngx_str_t                 key;
73     ngx_int_t                 status;
74 
75     struct sockaddr          *peer_addr;
76     socklen_t                 peer_addr_len;
77 } ngx_http_mogilefs_ctx_t;
78 
79 typedef enum {
80     START,
81     CREATE_OPEN,
82     FETCH,
83     CREATE_CLOSE,
84 } ngx_http_mogilefs_put_state_t;
85 
86 typedef struct {
87     ngx_http_post_subrequest_t      *psr;
88     ngx_http_mogilefs_put_state_t    state;
89     ngx_int_t                        status;
90     ngx_http_mogilefs_ctx_t         *create_open_ctx;
91     ngx_str_t                        key;
92 
93     ngx_uint_t                       num_successful_stores;
94 } ngx_http_mogilefs_put_ctx_t;
95 
96 typedef struct {
97     ssize_t                   priority;
98     ngx_str_t                 path;
99 } ngx_http_mogilefs_src_t;
100 
101 static ngx_int_t ngx_http_mogilefs_put_handler(ngx_http_request_t *r);
102 static ngx_int_t ngx_http_mogilefs_finish_phase_handler(ngx_http_request_t *r, void *data, ngx_int_t rc);
103 
104 static ngx_int_t ngx_http_mogilefs_eval_tracker(ngx_http_request_t *r, ngx_http_mogilefs_loc_conf_t *mgcf);
105 static ngx_int_t ngx_http_mogilefs_eval_class(ngx_http_request_t *r, ngx_http_mogilefs_loc_conf_t *mgcf);
106 static ngx_int_t ngx_http_mogilefs_eval_key(ngx_http_request_t *r, ngx_str_t *key);
107 static ngx_int_t ngx_http_mogilefs_set_cmd(ngx_http_request_t *r, ngx_http_mogilefs_ctx_t *ctx);
108 
109 static ngx_int_t ngx_http_mogilefs_create_request(ngx_http_request_t *r);
110 static ngx_int_t ngx_http_mogilefs_reinit_request(ngx_http_request_t *r);
111 static ngx_int_t ngx_http_mogilefs_process_header(ngx_http_request_t *r);
112 static void ngx_http_mogilefs_abort_request(ngx_http_request_t *r);
113 static void ngx_http_mogilefs_finalize_request(ngx_http_request_t *r, ngx_int_t rc);
114 
115 static ngx_int_t ngx_http_mogilefs_filter_init(void *data);
116 static ngx_int_t ngx_http_mogilefs_filter(void *data, ssize_t bytes);
117 
118 static ngx_int_t ngx_http_mogilefs_parse_param(ngx_http_request_t *r, ngx_str_t *param);
119 static ngx_int_t ngx_http_mogilefs_add_aux_param(ngx_http_request_t *r, ngx_str_t *name,
120     ngx_str_t *value);
121 
122 static ngx_int_t ngx_http_mogilefs_path_variable(ngx_http_request_t *r,
123     ngx_http_variable_value_t *v, uintptr_t data);
124 
125 static void *ngx_http_mogilefs_create_loc_conf(ngx_conf_t *cf);
126 static char *ngx_http_mogilefs_merge_loc_conf(ngx_conf_t *cf, void *parent,
127     void *child);
128 static ngx_int_t ngx_http_mogilefs_add_variables(ngx_conf_t *cf);
129 
130 static char *
131 ngx_http_mogilefs_tracker_command(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
132 static char *
133 ngx_http_mogilefs_class_command(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
134 static char *
135 ngx_http_mogilefs_pass_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
136 
137 static ngx_int_t ngx_http_mogilefs_init(ngx_conf_t *cf);
138 
139 static ngx_http_mogilefs_error_t ngx_http_mogilefs_errors[] = {
140     {NGX_HTTP_NOT_FOUND,                ngx_string("unknown_key"), 1},
141     {NGX_HTTP_NOT_FOUND,                ngx_string("domain_not_found"), 0},
142     {NGX_HTTP_SERVICE_UNAVAILABLE,      ngx_string("no_devices"), 0},
143     {NGX_HTTP_BAD_REQUEST,              ngx_string("no_key"), 0},
144     {NGX_HTTP_BAD_REQUEST,              ngx_string("unreg_class"), 0},
145 
146     {NGX_HTTP_INTERNAL_SERVER_ERROR,    ngx_null_string, 0},
147 };
148 
149 static ngx_http_mogilefs_cmd_t ngx_http_mogilefs_cmds[] = {
150     {NGX_HTTP_GET,                      ngx_string("get_paths"),            ngx_string("path"),         ngx_string("paths") },
151     {NGX_HTTP_HEAD,                     ngx_string("get_paths"),            ngx_string("path"),         ngx_string("paths") },
152     {NGX_HTTP_PUT,                      ngx_string("create_open"),          ngx_string("path_"),        ngx_string("dev_count") },
153     {NGX_HTTP_DELETE,                   ngx_string("delete"),               ngx_null_string,            ngx_null_string },
154 
155     {0,                                 ngx_null_string,                    ngx_null_string,            ngx_null_string },
156 };
157 
158 static ngx_conf_bitmask_t  ngx_http_mogilefs_methods_mask[] = {
159     { ngx_string("get"), NGX_HTTP_GET },
160     { ngx_string("put"), NGX_HTTP_PUT },
161     { ngx_string("delete"), NGX_HTTP_DELETE },
162     { ngx_null_string, 0 }
163 };
164 
165 static ngx_str_t  ngx_http_mogilefs_put_method = { 3, (u_char *) "PUT " };
166 
167 static ngx_command_t  ngx_http_mogilefs_commands[] = {
168 
169     { ngx_string("mogilefs_pass"),
170       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1|NGX_CONF_BLOCK,
171       ngx_http_mogilefs_pass_block,
172       NGX_HTTP_LOC_CONF_OFFSET,
173       0,
174       NULL },
175 
176     { ngx_string("mogilefs_tracker"),
177       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
178       ngx_http_mogilefs_tracker_command,
179       NGX_HTTP_LOC_CONF_OFFSET,
180       0,
181       NULL },
182 
183     { ngx_string("mogilefs_domain"),
184       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
185       ngx_conf_set_str_slot,
186       NGX_HTTP_LOC_CONF_OFFSET,
187       offsetof(ngx_http_mogilefs_loc_conf_t, domain),
188       NULL },
189 
190     { ngx_string("mogilefs_connect_timeout"),
191       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
192       ngx_conf_set_msec_slot,
193       NGX_HTTP_LOC_CONF_OFFSET,
194       offsetof(ngx_http_mogilefs_loc_conf_t, upstream.connect_timeout),
195       NULL },
196 
197     { ngx_string("mogilefs_send_timeout"),
198       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
199       ngx_conf_set_msec_slot,
200       NGX_HTTP_LOC_CONF_OFFSET,
201       offsetof(ngx_http_mogilefs_loc_conf_t, upstream.send_timeout),
202       NULL },
203 
204     { ngx_string("mogilefs_read_timeout"),
205       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
206       ngx_conf_set_msec_slot,
207       NGX_HTTP_LOC_CONF_OFFSET,
208       offsetof(ngx_http_mogilefs_loc_conf_t, upstream.read_timeout),
209       NULL },
210 
211     { ngx_string("mogilefs_noverify"),
212       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
213       ngx_conf_set_flag_slot,
214       NGX_HTTP_LOC_CONF_OFFSET,
215       offsetof(ngx_http_mogilefs_loc_conf_t, noverify),
216       NULL },
217 
218     { ngx_string("mogilefs_methods"),
219       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
220       ngx_conf_set_bitmask_slot,
221       NGX_HTTP_LOC_CONF_OFFSET,
222       offsetof(ngx_http_mogilefs_loc_conf_t, methods),
223       &ngx_http_mogilefs_methods_mask },
224 
225     { ngx_string("mogilefs_class"),
226       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
227       ngx_http_mogilefs_class_command,
228       NGX_HTTP_LOC_CONF_OFFSET,
229       offsetof(ngx_http_mogilefs_loc_conf_t, class_templates),
230       NULL },
231 
232       ngx_null_command
233 };
234 
235 static ngx_http_module_t  ngx_http_mogilefs_module_ctx = {
236     ngx_http_mogilefs_add_variables,       /* preconfiguration */
237     ngx_http_mogilefs_init,                /* postconfiguration */
238 
239     NULL,                                  /* create main configuration */
240     NULL,                                  /* init main configuration */
241 
242     NULL,                                  /* create server configuration */
243     NULL,                                  /* merge server configuration */
244 
245     ngx_http_mogilefs_create_loc_conf,     /* create location configuration */
246     ngx_http_mogilefs_merge_loc_conf       /* merge location configuration */
247 };
248 
249 ngx_module_t  ngx_http_mogilefs_module = {
250     NGX_MODULE_V1,
251     &ngx_http_mogilefs_module_ctx,         /* module context */
252     ngx_http_mogilefs_commands,            /* module directives */
253     NGX_HTTP_MODULE,                       /* module type */
254     NULL,                                  /* init master */
255     NULL,                                  /* init module */
256     NULL,                                  /* init process */
257     NULL,                                  /* init thread */
258     NULL,                                  /* exit thread */
259     NULL,                                  /* exit process */
260     NULL,                                  /* exit master */
261     NGX_MODULE_V1_PADDING
262 };
263 
264 static u_char     ngx_http_mogilefs_path_str[] = "mogilefs_path#";
265 static ngx_str_t  ngx_http_mogilefs_path = ngx_string(ngx_http_mogilefs_path_str);
266 
267 static ngx_http_variable_t  ngx_http_mogilefs_path_variable_template = { /* {{{ */
268     ngx_string(ngx_http_mogilefs_path_str), NULL, ngx_http_mogilefs_path_variable,
269       (uintptr_t) offsetof(ngx_http_mogilefs_ctx_t, sources),
270       NGX_HTTP_VAR_CHANGEABLE, 0
271 }; /* }}} */
272 static ngx_str_t  ngx_http_mogilefs_class = ngx_string("class");
273 static ngx_str_t  ngx_http_mogilefs_size = ngx_string("size");
274 
275 static ngx_int_t
ngx_http_mogilefs_handler(ngx_http_request_t * r)276 ngx_http_mogilefs_handler(ngx_http_request_t *r)
277 {
278     ngx_int_t                       rc;
279     ngx_http_upstream_t            *u;
280     ngx_http_mogilefs_ctx_t        *ctx;
281     ngx_http_mogilefs_loc_conf_t   *mgcf;
282 
283     mgcf = ngx_http_get_module_loc_conf(r, ngx_http_mogilefs_module);
284 
285     if (mgcf->location_type == NGX_MOGILEFS_MAIN) {
286         if(!(r->method & mgcf->methods)) {
287             return NGX_HTTP_NOT_ALLOWED;
288         }
289 
290         if(r->method & NGX_HTTP_PUT) {
291             return NGX_DECLINED;
292         }
293     }
294 
295     switch(r->method) {
296         case NGX_HTTP_GET:
297             if (ngx_http_set_content_type(r) != NGX_OK) {
298                 return NGX_HTTP_INTERNAL_SERVER_ERROR;
299             }
300 
301         case NGX_HTTP_DELETE:
302             rc = ngx_http_discard_request_body(r);
303 
304             if (rc != NGX_OK) {
305                 return rc;
306             }
307             break;
308         default:
309             break;
310     }
311 
312     u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t));
313     if (u == NULL) {
314         return NGX_HTTP_INTERNAL_SERVER_ERROR;
315     }
316 
317     u->peer.log = r->connection->log;
318     u->peer.log_error = NGX_ERROR_ERR;
319 
320     u->output.tag = (ngx_buf_tag_t) &ngx_http_mogilefs_module;
321 
322     u->conf = &mgcf->upstream;
323 
324     u->create_request = ngx_http_mogilefs_create_request;
325     u->reinit_request = ngx_http_mogilefs_reinit_request;
326     u->process_header = ngx_http_mogilefs_process_header;
327     u->abort_request = ngx_http_mogilefs_abort_request;
328     u->finalize_request = ngx_http_mogilefs_finalize_request;
329 
330     r->upstream = u;
331 
332     ctx = ngx_http_get_module_ctx(r, ngx_http_mogilefs_module);
333 
334     if(ctx == NULL) {
335         ctx = ngx_palloc(r->pool, sizeof(ngx_http_mogilefs_ctx_t));
336         if (ctx == NULL) {
337             return NGX_HTTP_INTERNAL_SERVER_ERROR;
338         }
339 
340         ctx->peer_addr = NULL;
341         ctx->peer_addr_len = 0;
342 
343         ctx->num_paths_returned = -1;
344         ctx->aux_params = NULL;
345         ctx->status = 0;
346 
347         ngx_array_init(&ctx->sources, r->pool, 1, sizeof(ngx_http_mogilefs_src_t));
348 
349         if(ngx_http_mogilefs_eval_key(r, &ctx->key) != NGX_OK) {
350             return NGX_HTTP_INTERNAL_SERVER_ERROR;
351         }
352 
353         ngx_http_set_ctx(r, ctx, ngx_http_mogilefs_module);
354     }
355 
356     u->input_filter_init = ngx_http_mogilefs_filter_init;
357     u->input_filter = ngx_http_mogilefs_filter;
358     u->input_filter_ctx = ctx;
359 
360     if (mgcf->tracker_lengths != 0) {
361         if (ngx_http_mogilefs_eval_tracker(r, mgcf) != NGX_OK) {
362             return NGX_HTTP_INTERNAL_SERVER_ERROR;
363         }
364     }
365 
366     if(ngx_http_mogilefs_set_cmd(r, ctx) != NGX_OK) {
367         return NGX_ERROR;
368     }
369 
370 #if defined nginx_version && nginx_version >= 8011
371     r->main->count++;
372 #endif
373 
374     ngx_http_upstream_init(r);
375 
376     return NGX_DONE;
377 }
378 
379 static void
ngx_http_mogilefs_body_handler(ngx_http_request_t * r)380 ngx_http_mogilefs_body_handler(ngx_http_request_t *r)
381 {
382     ngx_int_t                           rc;
383 
384     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
385                    "mogilefs body handler");
386 
387     rc = ngx_http_mogilefs_put_handler(r);
388 
389     if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
390         ngx_http_finalize_request(r, rc);
391     }
392 }
393 
394 static ngx_int_t
ngx_http_mogilefs_put_handler(ngx_http_request_t * r)395 ngx_http_mogilefs_put_handler(ngx_http_request_t *r)
396 {
397     ngx_http_mogilefs_put_ctx_t        *ctx;
398     ngx_str_t                           args;
399     ngx_uint_t                          flags;
400     ngx_http_request_t                 *sr;
401     ngx_str_t                           spare_location = ngx_null_string, uri, value;
402     ngx_int_t                           rc;
403     u_char                             *p;
404     ngx_http_core_loc_conf_t           *clcf;
405     ngx_http_mogilefs_loc_conf_t       *mgcf;
406 
407     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
408                    "mogilefs put handler");
409 
410     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
411     mgcf = ngx_http_get_module_loc_conf(r, ngx_http_mogilefs_module);
412 
413     if (clcf->handler != ngx_http_mogilefs_handler ||
414         (mgcf->location_type == NGX_MOGILEFS_MAIN && !(r->method & mgcf->methods)))
415     {
416         return NGX_DECLINED;
417     }
418 
419     ctx = ngx_http_get_module_ctx(r, ngx_http_mogilefs_module);
420 
421     if(ctx == NULL) {
422         ctx = ngx_palloc(r->pool, sizeof(ngx_http_mogilefs_put_ctx_t));
423         if (ctx == NULL) {
424             return NGX_ERROR;
425         }
426 
427         ctx->psr = NULL;
428         ctx->state = START;
429         ctx->status = 0;
430         ctx->create_open_ctx = NULL;
431 
432         if(ngx_http_mogilefs_eval_key(r, &ctx->key) != NGX_OK) {
433             return NGX_HTTP_INTERNAL_SERVER_ERROR;
434         }
435 
436         ngx_http_set_ctx(r, ctx, ngx_http_mogilefs_module);
437     }
438 
439     if(ctx->psr == NULL) {
440         ctx->psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
441         if (ctx->psr == NULL) {
442             return NGX_ERROR;
443         }
444     }
445 
446     if(r->request_body == NULL) {
447         rc = ngx_http_read_client_request_body(r, ngx_http_mogilefs_body_handler);
448 
449         if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
450             return rc;
451         }
452 
453         return NGX_DONE;
454     }
455 
456     // Still receiving body?
457     if(r->request_body->rest) {
458         return NGX_DONE;
459     }
460 
461     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
462                    "mogilefs put handler state: %ui, status: %i", ctx->state, ctx->status);
463 
464     if(ctx->state == CREATE_OPEN || ctx->state == FETCH || ctx->state == CREATE_CLOSE) {
465         if(ctx->status != NGX_OK && ctx->status != NGX_HTTP_CREATED && ctx->status != NGX_HTTP_NO_CONTENT) {
466             return (ctx->status >= NGX_HTTP_SPECIAL_RESPONSE) ?
467                 ctx->status : NGX_HTTP_INTERNAL_SERVER_ERROR;
468         }
469     }
470 
471     switch(ctx->state) {
472         case START:
473             spare_location = mgcf->create_open_spare_location;
474             ctx->state = CREATE_OPEN;
475             break;
476         case CREATE_OPEN:
477             spare_location = mgcf->fetch_location;
478             ctx->state = FETCH;
479             break;
480         case FETCH:
481             spare_location = mgcf->create_close_spare_location;
482             ctx->state = CREATE_CLOSE;
483             break;
484         case CREATE_CLOSE:
485             r->headers_out.content_length_n = 0;
486             r->headers_out.status = NGX_HTTP_CREATED;
487 
488             r->header_only = 1;
489 
490             return ngx_http_send_header(r);
491     }
492 
493     uri.len = spare_location.len + ctx->key.len;
494 
495     uri.data = ngx_palloc(r->pool, uri.len);
496 
497     p = ngx_cpymem(uri.data, spare_location.data, spare_location.len);
498 
499     p = ngx_cpymem(p, ctx->key.data, ctx->key.len);
500 
501     args.len = 0;
502     args.data = NULL;
503     flags = 0;
504 
505     if (ngx_http_parse_unsafe_uri(r, &uri, &args, &flags) != NGX_OK) {
506         return NGX_ERROR;
507     }
508 
509     ctx->psr->handler = ngx_http_mogilefs_finish_phase_handler;
510     ctx->psr->data = ctx;
511 
512     flags |= NGX_HTTP_SUBREQUEST_WAITED;
513 
514     if(ctx->state == FETCH) {
515         flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY;
516     }
517 
518     rc = ngx_http_subrequest(r, &uri, &args, &sr, ctx->psr, flags);
519 
520     if (rc == NGX_ERROR) {
521         return rc;
522     }
523 
524     if(ctx->state == CREATE_CLOSE) {
525         ngx_http_set_ctx(sr, ctx->create_open_ctx, ngx_http_mogilefs_module);
526 
527         value.data = ngx_palloc(r->pool, NGX_OFF_T_LEN);
528 
529         if(value.data == NULL) {
530             return NGX_ERROR;
531         }
532 
533         value.len = ngx_sprintf(value.data, "%O", r->headers_in.content_length_n)
534             - value.data;
535 
536         if(ngx_http_mogilefs_add_aux_param(sr, &ngx_http_mogilefs_size, &value) != NGX_OK) {
537             return NGX_ERROR;
538         }
539     }
540 
541     /*
542      * Nginx closes temporary file with buffered body
543      * whenever it starts sending reponse from upstream
544      * and it is not doing subrequest in memory.
545      *
546      * Since the request body in create_open subrequest is
547      * inherited from main request, it is necessary to prevent
548      * nginx from closing the temporary file with request body,
549      * before it could be passed to the storage node on fetch/store
550      * stage.
551      *
552      * We do it by "hiding" the request body from nginx internals.
553      */
554     if(ctx->state == CREATE_OPEN) {
555         sr->request_body = NULL;
556     }
557 
558     sr->method = NGX_HTTP_PUT;
559     sr->method_name = ngx_http_mogilefs_put_method;
560 
561     /*
562      * Wait for subrequest to complete
563      */
564     return NGX_DONE;
565 }
566 
567 static ngx_int_t
ngx_http_mogilefs_finish_phase_handler(ngx_http_request_t * r,void * data,ngx_int_t rc)568 ngx_http_mogilefs_finish_phase_handler(ngx_http_request_t *r, void *data, ngx_int_t rc)
569 {
570     ngx_http_mogilefs_put_ctx_t *ctx = data;
571     ngx_http_mogilefs_ctx_t     *subrequest_ctx;
572 
573     subrequest_ctx = ngx_http_get_module_ctx(r, ngx_http_mogilefs_module);
574 
575     if(ctx->state == CREATE_OPEN) {
576         ctx->create_open_ctx = subrequest_ctx;
577     }
578 
579     ctx->status = (subrequest_ctx != NULL && subrequest_ctx->status >= NGX_HTTP_SPECIAL_RESPONSE)
580         ? subrequest_ctx->status : rc;
581 
582     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
583                    "mogilefs finish phase handler: state=%ui, status=%i", ctx->state, ctx->status);
584 
585     return NGX_OK;
586 }
587 
588 static ngx_int_t
ngx_http_mogilefs_eval_tracker(ngx_http_request_t * r,ngx_http_mogilefs_loc_conf_t * mgcf)589 ngx_http_mogilefs_eval_tracker(ngx_http_request_t *r, ngx_http_mogilefs_loc_conf_t *mgcf)
590 {
591     ngx_str_t             tracker;
592     ngx_http_upstream_t  *u;
593     ngx_http_mogilefs_ctx_t *ctx;
594 
595     ctx = ngx_http_get_module_ctx(r, ngx_http_mogilefs_module);
596 
597     if (ngx_http_script_run(r, &tracker, mgcf->tracker_lengths->elts, 0,
598                             mgcf->tracker_values->elts)
599         == NULL)
600     {
601         return NGX_ERROR;
602     }
603 
604     u = r->upstream;
605 
606     u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));
607     if (u->resolved == NULL) {
608         return NGX_ERROR;
609     }
610 
611     if(ctx->peer_addr == NULL) {
612         u->resolved->host = tracker;
613         u->resolved->no_port = 1;
614     }
615     else {
616         u->resolved->sockaddr = ctx->peer_addr;
617         u->resolved->socklen = ctx->peer_addr_len;
618         u->resolved->naddrs = 1;
619         u->resolved->host = tracker;
620     }
621 
622     return NGX_OK;
623 }
624 
625 static ngx_int_t
ngx_http_mogilefs_eval_class(ngx_http_request_t * r,ngx_http_mogilefs_loc_conf_t * mgcf)626 ngx_http_mogilefs_eval_class(ngx_http_request_t *r, ngx_http_mogilefs_loc_conf_t *mgcf)
627 {
628     ngx_uint_t                           i;
629     ngx_http_mogilefs_class_template_t  *t;
630     ngx_str_t                            class;
631 
632     if(mgcf->class_templates == NULL) {
633         return NGX_DECLINED;
634     }
635 
636     t = mgcf->class_templates->elts;
637 
638     for(i = 0;i < mgcf->class_templates->nelts;i++) {
639         if(t->lengths != NULL && t->values != NULL) {
640             if(ngx_http_script_run(r, &class, t->lengths->elts, 0,
641                                     t->values->elts)
642                 == NULL)
643             {
644                 return NGX_ERROR;
645             }
646         }
647         else {
648             if(ngx_http_mogilefs_add_aux_param(r, &ngx_http_mogilefs_class, &t->source) != NGX_OK) {
649                 return NGX_ERROR;
650             }
651 
652             return NGX_OK;
653         }
654 
655         if(class.len) {
656             if(ngx_http_mogilefs_add_aux_param(r, &ngx_http_mogilefs_class, &class) != NGX_OK) {
657                 return NGX_ERROR;
658             }
659 
660             return NGX_OK;
661         }
662 
663         t++;
664     }
665 
666     return NGX_DECLINED;
667 }
668 static ngx_int_t
ngx_http_mogilefs_eval_key(ngx_http_request_t * r,ngx_str_t * key)669 ngx_http_mogilefs_eval_key(ngx_http_request_t *r, ngx_str_t *key)
670 {
671     size_t                          loc_len;
672     ngx_http_mogilefs_loc_conf_t   *mgcf;
673     ngx_http_core_loc_conf_t       *clcf;
674 
675     mgcf = ngx_http_get_module_loc_conf(r, ngx_http_mogilefs_module);
676     /*
677      * If key is empty take the remaining part of request URI,
678      * otherwise run script to obtain key
679      */
680     if(mgcf->key.len == 0) {
681         clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
682 
683         loc_len = r->valid_location ? clcf->name.len : 0;
684 
685         key->data = r->uri.data + loc_len;
686         key->len = r->uri.len - loc_len;
687     }
688     else {
689         if(mgcf->key_lengths != NULL) {
690             if (ngx_http_script_run(r, key, mgcf->key_lengths->elts, 0,
691                                     mgcf->key_values->elts)
692                 == NULL)
693             {
694                 return NGX_ERROR;
695             }
696         }
697         else {
698             *key = mgcf->key;
699         }
700     }
701 
702     return NGX_OK;
703 }
704 
705 static ngx_int_t
ngx_http_mogilefs_set_cmd(ngx_http_request_t * r,ngx_http_mogilefs_ctx_t * ctx)706 ngx_http_mogilefs_set_cmd(ngx_http_request_t *r, ngx_http_mogilefs_ctx_t *ctx)
707 {
708     ngx_http_mogilefs_cmd_t *c;
709 
710     c = ngx_http_mogilefs_cmds;
711 
712     while(c->name.data != NULL) {
713         if(c->method & r->method)
714             break;
715 
716         c++;
717     }
718 
719     if(c->name.data != NULL) {
720         ctx->cmd = c;
721         return NGX_OK;
722     }
723 
724     return NGX_ERROR;
725 }
726 
727 static ngx_int_t
ngx_http_mogilefs_create_request(ngx_http_request_t * r)728 ngx_http_mogilefs_create_request(ngx_http_request_t *r)
729 {
730     size_t                          len;
731     uintptr_t                       escape_domain, escape_key;
732     ngx_str_t                       cmd;
733     ngx_buf_t                      *b;
734     ngx_chain_t                    *cl;
735     ngx_http_mogilefs_loc_conf_t   *mgcf;
736     ngx_str_t                       request, domain;
737     ngx_http_mogilefs_ctx_t        *ctx;
738     ngx_http_mogilefs_aux_param_t  *a;
739     ngx_uint_t                      i;
740     ngx_int_t                       rc;
741 
742     mgcf = ngx_http_get_module_loc_conf(r, ngx_http_mogilefs_module);
743 
744     ctx = ngx_http_get_module_ctx(r, ngx_http_mogilefs_module);
745 
746     /*
747      * Save peer address, so that we contact the same host while doing create_close
748      */
749     if(mgcf->location_type == NGX_MOGILEFS_CREATE_OPEN && ctx->cmd->method & NGX_HTTP_PUT) {
750         if(r->upstream->peer.sockaddr != NULL) {
751             ctx->peer_addr = ngx_palloc(r->main->pool, r->upstream->peer.socklen);
752 
753             if(ctx->peer_addr == NULL) {
754                 return NGX_ERROR;
755             }
756 
757             ngx_memcpy(ctx->peer_addr, r->upstream->peer.sockaddr, r->upstream->peer.socklen);
758 
759             ctx->peer_addr_len = r->upstream->peer.socklen;
760         }
761     }
762 
763     cmd = ctx->cmd->name;
764 
765     if(mgcf->location_type == NGX_MOGILEFS_CREATE_CLOSE && ctx->cmd->method & NGX_HTTP_PUT) {
766         cmd.data = (u_char*)"create_close";
767         cmd.len = sizeof("create_close") - 1;
768     }
769 
770     if(ctx->key.len == 0) {
771         return NGX_HTTP_BAD_REQUEST;
772     }
773 
774     if(mgcf->parent != NULL) {
775         domain.len = mgcf->parent->domain.len;
776         domain.data = mgcf->parent->domain.data;
777     }
778     else {
779         domain.len = mgcf->domain.len;
780         domain.data = mgcf->domain.data;
781     }
782 
783     rc = ngx_http_mogilefs_eval_class(r, mgcf->parent != NULL ? mgcf->parent : mgcf);
784 
785     if(rc == NGX_ERROR) {
786         return rc;
787     }
788 
789     escape_domain = 2 * ngx_escape_uri(NULL, domain.data, domain.len, NGX_ESCAPE_MEMCACHED);
790     escape_key = 2 * ngx_escape_uri(NULL, ctx->key.data, ctx->key.len, NGX_ESCAPE_MEMCACHED);
791 
792     len = cmd.len + 1 + sizeof("key=") - 1 + ctx->key.len + escape_key + 1 +
793         sizeof("domain=") - 1 + domain.len + escape_domain + sizeof(CRLF) - 1 +
794         (mgcf->noverify ? 1 + sizeof("noverify=1") - 1 : 0);
795 
796     if(ctx->aux_params != NULL && ctx->aux_params->nelts) {
797         a = ctx->aux_params->elts;
798         for (i = 0; i < ctx->aux_params->nelts; i++) {
799             len += a[i].name.len + 1 + 1 + a[i].value.len;
800         }
801     }
802 
803     b = ngx_create_temp_buf(r->pool, len);
804     if (b == NULL) {
805         return NGX_ERROR;
806     }
807 
808     cl = ngx_alloc_chain_link(r->pool);
809     if (cl == NULL) {
810         return NGX_ERROR;
811     }
812 
813     cl->buf = b;
814     cl->next = NULL;
815 
816     r->upstream->request_bufs = cl;
817 
818     b->last = ngx_copy(b->last, cmd.data, cmd.len);
819 
820     *b->last++ = ' ';
821 
822     b->last = ngx_copy(b->last, "key=", sizeof("key=") - 1);
823 
824     if (escape_key == 0) {
825         b->last = ngx_copy(b->last, ctx->key.data, ctx->key.len);
826 
827     } else {
828         b->last = (u_char *) ngx_escape_uri(b->last, ctx->key.data, ctx->key.len,
829                                             NGX_ESCAPE_MEMCACHED);
830     }
831 
832     *b->last++ = '&';
833 
834     b->last = ngx_copy(b->last, "domain=", sizeof("domain=") - 1);
835 
836     if (escape_domain == 0) {
837         b->last = ngx_copy(b->last, domain.data, domain.len);
838 
839     } else {
840         b->last = (u_char *) ngx_escape_uri(b->last, domain.data, domain.len,
841                                             NGX_ESCAPE_MEMCACHED);
842     }
843 
844     if(mgcf->noverify) {
845         *b->last++ = '&';
846 
847         b->last = ngx_copy(b->last, "noverify=1", sizeof("noverify=1") - 1);
848     }
849 
850     if(ctx->aux_params != NULL && ctx->aux_params->nelts) {
851         a = ctx->aux_params->elts;
852         for (i = 0; i < ctx->aux_params->nelts; i++) {
853             *b->last++ = '&';
854 
855             b->last = ngx_copy(b->last, a[i].name.data, a[i].name.len);
856 
857             *b->last++ = '=';
858 
859             b->last = ngx_copy(b->last, a[i].value.data, a[i].value.len);
860         }
861     }
862 
863     request.data = b->pos;
864     request.len = b->last - b->pos;
865 
866     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
867                    "mogilefs request: \"%V\"", &request);
868 
869     *b->last++ = CR; *b->last++ = LF;
870 
871     return NGX_OK;
872 }
873 
874 static ngx_int_t
ngx_http_mogilefs_reinit_request(ngx_http_request_t * r)875 ngx_http_mogilefs_reinit_request(ngx_http_request_t *r)
876 {
877     return NGX_OK;
878 }
879 
ngx_http_mogilefs_cmp_sources(const void * one,const void * two)880 static int ngx_libc_cdecl ngx_http_mogilefs_cmp_sources(const void *one,
881     const void *two)
882 {
883     ngx_http_mogilefs_src_t *first, *second;
884 
885     first = (ngx_http_mogilefs_src_t *) one;
886     second = (ngx_http_mogilefs_src_t *) two;
887 
888     return first->priority - second->priority;
889 }
890 
891 static ngx_int_t
ngx_http_mogilefs_process_ok_response(ngx_http_request_t * r,ngx_http_upstream_t * u,ngx_str_t * line)892 ngx_http_mogilefs_process_ok_response(ngx_http_request_t *r,
893     ngx_http_upstream_t *u, ngx_str_t *line)
894 {
895     u_char                          *p;
896     ngx_str_t                        param;
897     ngx_int_t                        rc;
898 
899     ngx_table_elt_t                *h;
900     ngx_http_upstream_header_t     *hh;
901     ngx_http_upstream_main_conf_t  *umcf;
902     ngx_http_mogilefs_loc_conf_t   *mgcf;
903     ngx_http_variable_value_t      *v;
904     ngx_http_mogilefs_ctx_t        *ctx;
905     ngx_http_mogilefs_src_t        *source;
906     ngx_uint_t                     i;
907 
908     line->data += sizeof("OK ") - 1;
909     line->len -= sizeof("OK ") - 1;
910 
911     p = line->data;
912 
913     param.data = p;
914     param.len = 0;
915 
916     while (*p != LF) {
917         if (*p == '&' || *p == CR) {
918             if(param.len != 0) {
919                 rc = ngx_http_mogilefs_parse_param(r, &param);
920 
921                 if(rc != NGX_OK) {
922                     return rc;
923                 }
924 
925                 p++;
926 
927                 param.data = p;
928                 param.len = 0;
929             }
930 
931             if(*p == CR) {
932                 break;
933             }
934             else {
935                 continue;
936             }
937         }
938 
939         param.len++;
940         p++;
941     }
942 
943     ctx = ngx_http_get_module_ctx(r, ngx_http_mogilefs_module);
944 
945     /*
946      * Convert ok response to delete into No content
947      */
948     if(ctx->cmd->method & NGX_HTTP_DELETE) {
949         r->headers_out.content_length_n = 0;
950         u->headers_in.status_n = NGX_HTTP_NO_CONTENT;
951         u->state->status = NGX_HTTP_NO_CONTENT;
952 
953         // Return no content
954         u->buffer.pos = u->buffer.pos;
955 
956         return NGX_OK;
957     }
958 
959     /*
960      * If no paths retuned, but response was ok, tell the client it's unavailable
961      */
962     if((ctx->num_paths_returned <= 0 && (!(ctx->cmd->method & NGX_HTTP_PUT))) || ctx->sources.nelts == 0)
963     {
964         r->headers_out.content_length_n = 0;
965         u->headers_in.status_n = NGX_HTTP_SERVICE_UNAVAILABLE;
966         u->state->status = NGX_HTTP_SERVICE_UNAVAILABLE;
967 
968         // Return no content
969         u->buffer.pos = u->buffer.pos;
970 
971         return NGX_OK;
972     }
973 
974     /*
975      * Sort sources and choose top source
976      */
977     if(ctx->sources.nelts > 1) {
978         ngx_qsort(ctx->sources.elts, ctx->sources.nelts, sizeof(ngx_http_mogilefs_src_t),
979             ngx_http_mogilefs_cmp_sources);
980     }
981 
982     mgcf = ngx_http_get_module_loc_conf(r, ngx_http_mogilefs_module);
983 
984     source = ctx->sources.elts;
985 
986     /*
987      * Save peer address, so that we contact the same host while doing create_close
988      */
989     if(mgcf->location_type == NGX_MOGILEFS_CREATE_OPEN && ctx->cmd->method & NGX_HTTP_PUT) {
990         if(r->upstream->peer.sockaddr != NULL) {
991             ctx->peer_addr = ngx_palloc(r->main->pool, r->upstream->peer.socklen);
992 
993             if(ctx->peer_addr == NULL) {
994                 return NGX_ERROR;
995             }
996 
997             ngx_memcpy(ctx->peer_addr, r->upstream->peer.sockaddr, r->upstream->peer.socklen);
998 
999             ctx->peer_addr_len = r->upstream->peer.socklen;
1000         }
1001     }
1002 
1003     /*
1004      * Set $mogilefs_path variables
1005      */
1006     for(i=0;i < ctx->sources.nelts;i++) {
1007         v = r->variables + mgcf->index[i];
1008 
1009         v->data = source[i].path.data;
1010         v->len = source[i].path.len;
1011 
1012         v->not_found = 0;
1013         v->no_cacheable = 0;
1014         v->valid = 1;
1015     }
1016 
1017     /*
1018      * Redirect to fetch location
1019      */
1020     if (ctx->cmd->method & (NGX_HTTP_GET|NGX_HTTP_HEAD) && r->upstream->headers_in.x_accel_redirect == NULL) {
1021 
1022         umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
1023 
1024         h = ngx_list_push(&r->upstream->headers_in.headers);
1025         if (h == NULL) {
1026             return NGX_ERROR;
1027         }
1028 
1029         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(
1030                             ngx_hash('x', '-'), 'a'), 'c'), 'c'), 'e'), 'l'), '-'), 'r'), 'e'), 'd'), 'i'), 'r'), 'e'), 'c'), 't');
1031 
1032         h->key.len = sizeof("X-Accel-Redirect") - 1;
1033         h->key.data = (u_char *) "X-Accel-Redirect";
1034         h->value = mgcf->fetch_location;
1035         h->lowcase_key = (u_char *) "x-accel-redirect";
1036 
1037         hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
1038                            h->lowcase_key, h->key.len);
1039 
1040         if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
1041             return NGX_ERROR;
1042         }
1043     }
1044 
1045     r->headers_out.content_length_n = 0;
1046     u->headers_in.status_n = 200;
1047     u->state->status = 200;
1048 
1049     // Return no content
1050     u->buffer.pos = u->buffer.pos;
1051 
1052     return NGX_OK;
1053 }
1054 
1055 static ngx_int_t
ngx_http_mogilefs_process_error_response(ngx_http_request_t * r,ngx_http_upstream_t * u,ngx_str_t * line)1056 ngx_http_mogilefs_process_error_response(ngx_http_request_t *r,
1057     ngx_http_upstream_t *u, ngx_str_t *line)
1058 {
1059     ngx_http_mogilefs_error_t *e;
1060     ngx_http_mogilefs_ctx_t   *ctx;
1061 
1062     line->data += sizeof("ERR ") - 1;
1063     line->len -= sizeof("ERR ") - 1;
1064 
1065     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1066                   "mogilefs error: \"%V\"", line);
1067 
1068     e = ngx_http_mogilefs_errors;
1069 
1070     while(e->name.data != NULL) {
1071         if(line->len >= e->name.len &&
1072             ngx_strncmp(line->data, e->name.data, e->name.len) == 0)
1073         {
1074             break;
1075         }
1076 
1077         e++;
1078     }
1079 
1080     ctx = ngx_http_get_module_ctx(r, ngx_http_mogilefs_module);
1081 
1082     /*
1083      * Convert unknown_key response to delete into No content
1084      */
1085     if(ctx->cmd->method & NGX_HTTP_DELETE && e->delete_ok) {
1086         r->headers_out.content_length_n = 0;
1087         u->headers_in.status_n = NGX_HTTP_NO_CONTENT;
1088         u->state->status = NGX_HTTP_NO_CONTENT;
1089 
1090         // Return no content
1091         u->buffer.pos = u->buffer.pos;
1092 
1093         return NGX_OK;
1094     }
1095 
1096     r->headers_out.content_length_n = 0;
1097     u->headers_in.status_n = e->status;
1098     u->state->status = e->status;
1099     ctx->status = e->status;
1100 
1101     // Return no content
1102     u->buffer.pos = u->buffer.pos;
1103 
1104     return NGX_OK;
1105 }
1106 
1107 static ngx_int_t
ngx_http_mogilefs_add_aux_param(ngx_http_request_t * r,ngx_str_t * name,ngx_str_t * value)1108 ngx_http_mogilefs_add_aux_param(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value)
1109 {
1110     ngx_http_mogilefs_ctx_t         *ctx;
1111     ngx_http_mogilefs_aux_param_t   *p;
1112 
1113     ctx = ngx_http_get_module_ctx(r, ngx_http_mogilefs_module);
1114     if(ctx == NULL) {
1115         return NGX_ERROR;
1116     }
1117 
1118     if(ctx->aux_params == NULL) {
1119         ctx->aux_params = ngx_array_create(r->pool, 3, sizeof(ngx_http_mogilefs_aux_param_t));
1120 
1121         if(ctx->aux_params == NULL) {
1122             return NGX_ERROR;
1123         }
1124     }
1125 
1126     p = ngx_array_push(ctx->aux_params);
1127     if (p == NULL) {
1128         return NGX_ERROR;
1129     }
1130 
1131     p->name = *name;
1132     p->value = *value;
1133 
1134     return NGX_OK;
1135 }
1136 
1137 static ngx_int_t
ngx_http_mogilefs_parse_param(ngx_http_request_t * r,ngx_str_t * param)1138 ngx_http_mogilefs_parse_param(ngx_http_request_t *r, ngx_str_t *param) {
1139     u_char                    *p, *src, *dst;
1140 
1141     ngx_str_t                  name;
1142     ngx_str_t                  value;
1143 
1144     ngx_http_mogilefs_ctx_t   *ctx;
1145     ngx_http_mogilefs_src_t   *source;
1146 
1147     p = (u_char *) ngx_strchr(param->data, '=');
1148 
1149     if(p == NULL) {
1150         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1151                       "mogilefs tracker has sent invalid param: \"%V\"", param);
1152         return NGX_ERROR;
1153     }
1154 
1155     name.data = param->data;
1156     name.len = p - param->data;
1157 
1158     value.data = p + 1;
1159     value.len = param->len - (p - param->data) - 1;
1160 
1161     src = dst = value.data;
1162 
1163     ngx_unescape_uri(&dst, &src, value.len, NGX_UNESCAPE_URI);
1164 
1165     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1166                    "mogilefs param: \"%V\"=\"%V\"", &name, &value);
1167 
1168     ctx = ngx_http_get_module_ctx(r, ngx_http_mogilefs_module);
1169 
1170     if(name.len == sizeof("path") - 1
1171         && ngx_strncmp(name.data, "path", sizeof("path") - 1) == 0)
1172     {
1173         source = ngx_array_push(&ctx->sources);
1174 
1175         if(source == NULL) {
1176             return NGX_ERROR;
1177         }
1178 
1179         source->priority = 0;
1180         source->path = value;
1181 
1182         if(ngx_http_mogilefs_add_aux_param(r, &name, &value) != NGX_OK) {
1183             return NGX_ERROR;
1184         }
1185     }
1186     else if(name.len >= ctx->cmd->output_param.len
1187         && ngx_strncmp(name.data, ctx->cmd->output_param.data, ctx->cmd->output_param.len) == 0
1188         && ngx_atoi(name.data + ctx->cmd->output_param.len, name.len - ctx->cmd->output_param.len) != NGX_ERROR)
1189     {
1190         source = ngx_array_push(&ctx->sources);
1191 
1192         if(source == NULL) {
1193             return NGX_ERROR;
1194         }
1195 
1196         source->priority = ngx_atoi(name.data + ctx->cmd->output_param.len, name.len - ctx->cmd->output_param.len);
1197         source->path = value;
1198     }
1199     else if(name.len == ctx->cmd->output_count_param.len &&
1200         ngx_strncmp(name.data, ctx->cmd->output_count_param.data, ctx->cmd->output_count_param.len) == 0)
1201     {
1202         ctx->num_paths_returned = ngx_atoi(value.data, value.len);
1203     }
1204     else {
1205         if(ngx_http_mogilefs_add_aux_param(r, &name, &value) != NGX_OK) {
1206             return NGX_ERROR;
1207         }
1208     }
1209 
1210     return NGX_OK;
1211 }
1212 
1213 static ngx_int_t
ngx_http_mogilefs_process_header(ngx_http_request_t * r)1214 ngx_http_mogilefs_process_header(ngx_http_request_t *r)
1215 {
1216     u_char                    *p;
1217     ngx_str_t                  line;
1218     ngx_http_upstream_t       *u;
1219 
1220     u = r->upstream;
1221 
1222     for (p = u->buffer.pos; p < u->buffer.last; p++) {
1223         if (*p == LF) {
1224             goto found;
1225         }
1226     }
1227 
1228     return NGX_AGAIN;
1229 found:
1230 
1231     line.len = p - u->buffer.pos;
1232     line.data = u->buffer.pos;
1233 
1234     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1235                    "mogilefs: \"%V\"", &line);
1236 
1237     if (line.len >= sizeof("ERR ") - 1 &&
1238         ngx_strncmp(line.data, "ERR ", sizeof("ERR ") - 1) == 0)
1239     {
1240         return ngx_http_mogilefs_process_error_response(r, u, &line);
1241     }
1242 
1243     if (line.len >= sizeof("OK ") - 1 &&
1244         ngx_strncmp(line.data, "OK ", sizeof("OK ") - 1) == 0)
1245     {
1246         return ngx_http_mogilefs_process_ok_response(r, u, &line);
1247     }
1248 
1249     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1250                   "mogilefs tracker has sent invalid response: \"%V\"", &line);
1251 
1252     return NGX_HTTP_UPSTREAM_INVALID_HEADER;
1253 }
1254 
1255 static void
ngx_http_mogilefs_abort_request(ngx_http_request_t * r)1256 ngx_http_mogilefs_abort_request(ngx_http_request_t *r)
1257 {
1258     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1259                    "abort mogilefs request");
1260     return;
1261 }
1262 
1263 static void
ngx_http_mogilefs_finalize_request(ngx_http_request_t * r,ngx_int_t rc)1264 ngx_http_mogilefs_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
1265 {
1266     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1267                    "finalize mogilefs request");
1268     return;
1269 }
1270 
1271 static ngx_int_t
ngx_http_mogilefs_filter_init(void * data)1272 ngx_http_mogilefs_filter_init(void *data)
1273 {
1274     return NGX_OK;
1275 }
1276 
1277 static ngx_int_t
ngx_http_mogilefs_filter(void * data,ssize_t bytes)1278 ngx_http_mogilefs_filter(void *data, ssize_t bytes)
1279 {
1280     return NGX_OK;
1281 }
1282 
1283 static void *
ngx_http_mogilefs_create_loc_conf(ngx_conf_t * cf)1284 ngx_http_mogilefs_create_loc_conf(ngx_conf_t *cf)
1285 {
1286     ngx_http_mogilefs_loc_conf_t  *conf;
1287 
1288     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_mogilefs_loc_conf_t));
1289 
1290     if (conf == NULL) {
1291         return NGX_CONF_ERROR;
1292     }
1293 
1294     conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
1295     conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
1296     conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
1297 
1298     conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;
1299 
1300     /* the hardcoded values */
1301     conf->upstream.cyclic_temp_file = 0;
1302     conf->upstream.buffering = 0;
1303     conf->upstream.ignore_client_abort = 0;
1304     conf->upstream.send_lowat = 0;
1305     conf->upstream.bufs.num = 0;
1306     conf->upstream.busy_buffers_size = 0;
1307     conf->upstream.max_temp_file_size = 0;
1308     conf->upstream.temp_file_write_size = 0;
1309     conf->upstream.intercept_errors = 1;
1310     conf->upstream.intercept_404 = 1;
1311     conf->upstream.pass_request_headers = 0;
1312     conf->upstream.pass_request_body = 0;
1313 
1314     conf->noverify = NGX_CONF_UNSET;
1315     conf->methods = 0;
1316 
1317     return conf;
1318 }
1319 
1320 static char *
ngx_http_mogilefs_merge_loc_conf(ngx_conf_t * cf,void * parent,void * child)1321 ngx_http_mogilefs_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
1322 {
1323     ngx_http_mogilefs_loc_conf_t *prev = parent;
1324     ngx_http_mogilefs_loc_conf_t *conf = child;
1325 
1326     ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
1327                               prev->upstream.connect_timeout, 60000);
1328 
1329     ngx_conf_merge_msec_value(conf->upstream.send_timeout,
1330                               prev->upstream.send_timeout, 60000);
1331 
1332     ngx_conf_merge_msec_value(conf->upstream.read_timeout,
1333                               prev->upstream.read_timeout, 60000);
1334 
1335     ngx_conf_merge_size_value(conf->upstream.buffer_size,
1336                               prev->upstream.buffer_size,
1337                               (size_t) ngx_pagesize);
1338 
1339     ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
1340                               prev->upstream.next_upstream,
1341                               (NGX_CONF_BITMASK_SET
1342                                |NGX_HTTP_UPSTREAM_FT_ERROR
1343                                |NGX_HTTP_UPSTREAM_FT_TIMEOUT));
1344 
1345     if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {
1346         conf->upstream.next_upstream = NGX_CONF_BITMASK_SET
1347                                        |NGX_HTTP_UPSTREAM_FT_OFF;
1348     }
1349 
1350     if (conf->upstream.upstream == NULL) {
1351         conf->upstream.upstream = prev->upstream.upstream;
1352     }
1353 
1354     ngx_conf_merge_str_value(conf->domain, prev->domain, "default");
1355 
1356     ngx_conf_merge_value(conf->noverify, prev->noverify, 0);
1357 
1358     ngx_conf_merge_bitmask_value(conf->methods, prev->methods,
1359                          (NGX_CONF_BITMASK_SET|NGX_HTTP_GET));
1360 
1361     if(conf->methods & NGX_HTTP_GET) {
1362         conf->methods |= NGX_HTTP_HEAD;
1363     }
1364 
1365     if(conf->class_templates == NULL) {
1366         conf->class_templates = prev->class_templates;
1367     }
1368 
1369     return NGX_CONF_OK;
1370 }
1371 
ngx_http_mogilefs_add_variables(ngx_conf_t * cf)1372 static ngx_int_t ngx_http_mogilefs_add_variables(ngx_conf_t *cf)
1373 {
1374     ngx_uint_t           i;
1375     ngx_http_variable_t  *var, *v;
1376     ngx_str_t            name;
1377 
1378     /*
1379      * Add 10 instances of mogilefs_path variable with
1380      * different names
1381      */
1382     v = &ngx_http_mogilefs_path_variable_template;
1383 
1384     for(i=0;i<NGX_MOGILEFS_MAX_PATHS;i++) {
1385         name.data = v->name.data;
1386         name.len = v->name.len - 1;
1387 
1388         if(i > 0) {
1389             name.data[name.len] = '0' + i;
1390             name.len++;
1391         }
1392 
1393         var = ngx_http_add_variable(cf, &name, v->flags);
1394         if (var == NULL) {
1395             return NGX_ERROR;
1396         }
1397 
1398         var->get_handler = v->get_handler;
1399         var->data = v->data;
1400     }
1401 
1402     return NGX_OK;
1403 }
1404 
ngx_http_mogilefs_path_variable(ngx_http_request_t * r,ngx_http_variable_value_t * v,uintptr_t data)1405 static ngx_int_t ngx_http_mogilefs_path_variable(ngx_http_request_t *r,
1406     ngx_http_variable_value_t *v, uintptr_t data)
1407 {
1408     v->valid = 1;
1409     v->no_cacheable = 0;
1410     v->not_found = 0;
1411 
1412     v->len = 0;
1413     v->data = (u_char*)"";
1414 
1415     return NGX_OK;
1416 }
1417 
1418 static char *
ngx_http_mogilefs_tracker_command(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)1419 ngx_http_mogilefs_tracker_command(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1420 {
1421     ngx_http_mogilefs_loc_conf_t    *mgcf = conf;
1422     ngx_str_t                       *value;
1423     ngx_url_t                        u;
1424     ngx_uint_t                       n;
1425     ngx_http_script_compile_t        sc;
1426 
1427     if (mgcf->upstream.upstream || mgcf->tracker_lengths) {
1428         return "is duplicate";
1429     }
1430 
1431     value = cf->args->elts;
1432 
1433     n = ngx_http_script_variables_count(&value[1]);
1434 
1435     if(n) {
1436         ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
1437 
1438         sc.cf = cf;
1439         sc.source = &value[1];
1440         sc.lengths = &mgcf->tracker_lengths;
1441         sc.values = &mgcf->tracker_values;
1442         sc.variables = n;
1443         sc.complete_lengths = 1;
1444         sc.complete_values = 1;
1445 
1446         if (ngx_http_script_compile(&sc) != NGX_OK) {
1447             return NGX_CONF_ERROR;
1448         }
1449 
1450         return NGX_CONF_OK;
1451     }
1452 
1453     ngx_memzero(&u, sizeof(ngx_url_t));
1454 
1455     u.url = value[1];
1456     u.no_resolve = 1;
1457     u.default_port = 6001;
1458 
1459     mgcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
1460     if (mgcf->upstream.upstream == NULL) {
1461         return NGX_CONF_ERROR;
1462     }
1463 
1464     return NGX_CONF_OK;
1465 }
1466 
1467 static char *
ngx_http_mogilefs_class_command(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)1468 ngx_http_mogilefs_class_command(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1469 {
1470     ngx_http_mogilefs_loc_conf_t        *mgcf = conf;
1471     ngx_http_mogilefs_class_template_t  *t;
1472     ngx_str_t                           *value;
1473     ngx_uint_t                           i, n;
1474     ngx_http_script_compile_t            sc;
1475 
1476     if(mgcf->class_templates == NULL) {
1477         mgcf->class_templates = ngx_array_create(cf->pool, cf->args->nelts,
1478             sizeof(ngx_http_mogilefs_class_template_t));
1479 
1480         if(mgcf->class_templates == NULL) {
1481             return NGX_CONF_ERROR;
1482         }
1483     }
1484 
1485     value = cf->args->elts;
1486 
1487     for(i = 1;i < cf->args->nelts;i++) {
1488         t = ngx_array_push(mgcf->class_templates);
1489 
1490         if(t == NULL) {
1491             return NGX_CONF_ERROR;
1492         }
1493 
1494         t->source = value[i];
1495         t->lengths = NULL;
1496         t->values = NULL;
1497 
1498         n = ngx_http_script_variables_count(&t->source);
1499 
1500         if(n) {
1501             ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
1502 
1503             sc.cf = cf;
1504             sc.source = &value[i];
1505             sc.lengths = &t->lengths;
1506             sc.values = &t->values;
1507             sc.variables = n;
1508             sc.complete_lengths = 1;
1509             sc.complete_values = 1;
1510 
1511             if (ngx_http_script_compile(&sc) != NGX_OK) {
1512                 return NGX_CONF_ERROR;
1513             }
1514         }
1515     }
1516 
1517     return NGX_CONF_OK;
1518 }
1519 
1520 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)1521 ngx_http_mogilefs_create_spare_location(ngx_conf_t *cf, ngx_http_conf_ctx_t **octx, ngx_str_t *name,
1522     ngx_http_mogilefs_location_type_t location_type)
1523 {
1524     ngx_http_mogilefs_loc_conf_t *mgcf, *pmgcf;
1525     ngx_http_conf_ctx_t       *ctx, *pctx = cf->ctx;
1526     ngx_uint_t                 i;
1527     ngx_http_module_t         *module;
1528     void                      *mconf;
1529     ngx_http_core_loc_conf_t  *clcf, *pclcf, *rclcf;
1530     ngx_http_core_srv_conf_t  *cscf;
1531 
1532     ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
1533     if (ctx == NULL) {
1534         return NGX_CONF_ERROR;
1535     }
1536 
1537     pctx = cf->ctx;
1538     ctx->main_conf = pctx->main_conf;
1539     ctx->srv_conf = pctx->srv_conf;
1540 
1541     ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
1542     if (ctx->loc_conf == NULL) {
1543         return NGX_CONF_ERROR;
1544     }
1545 
1546     for (i = 0; ngx_modules[i]; i++) {
1547         if (ngx_modules[i]->type != NGX_HTTP_MODULE) {
1548             continue;
1549         }
1550 
1551         module = ngx_modules[i]->ctx;
1552 
1553         if (module->create_loc_conf) {
1554 
1555             mconf = module->create_loc_conf(cf);
1556             if (mconf == NULL) {
1557                  return NGX_CONF_ERROR;
1558             }
1559 
1560             ctx->loc_conf[ngx_modules[i]->ctx_index] = mconf;
1561         }
1562     }
1563 
1564     pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index];
1565 
1566     clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];
1567 
1568     mgcf = ctx->loc_conf[ngx_http_mogilefs_module.ctx_index];
1569 
1570     mgcf->location_type = location_type;
1571 
1572     if(location_type != NGX_MOGILEFS_FETCH) {
1573         pmgcf = pctx->loc_conf[ngx_http_mogilefs_module.ctx_index];
1574 
1575         mgcf->methods = NGX_HTTP_PUT;
1576 
1577         /*
1578          * Copy tracker configuration
1579          */
1580         mgcf->tracker_lengths = pmgcf->tracker_lengths;
1581         mgcf->tracker_values = pmgcf->tracker_values;
1582 
1583         mgcf->parent = pmgcf;
1584 
1585         ngx_memcpy(&mgcf->upstream, &pmgcf->upstream, sizeof(ngx_http_upstream_conf_t));
1586 
1587         ngx_memcpy(mgcf->index, pmgcf->index, NGX_MOGILEFS_MAX_PATHS * sizeof(ngx_int_t));
1588 
1589         clcf->handler = ngx_http_mogilefs_handler;
1590     }
1591 
1592     name->len = sizeof("/mogstored_spare_") - 1 + NGX_OFF_T_LEN + 1;
1593 
1594     name->data = ngx_palloc(cf->pool, name->len);
1595 
1596     if(name->data == NULL) {
1597         return NGX_CONF_ERROR;
1598     }
1599 
1600     name->len = ngx_sprintf(name->data, "/mogstored_spare_%O/", (off_t)(uintptr_t)clcf) - name->data;
1601 
1602     clcf->loc_conf = ctx->loc_conf;
1603     clcf->name = *name;
1604     clcf->exact_match = 0;
1605     clcf->noname = 0;
1606     clcf->internal = 1;
1607     clcf->noregex = 1;
1608 
1609     cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module);
1610 
1611     rclcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index];
1612 
1613     if (ngx_http_add_location(cf, &rclcf->locations, clcf) != NGX_OK) {
1614         return NGX_CONF_ERROR;
1615     }
1616 
1617     if(octx != NULL) {
1618         *octx = ctx;
1619     }
1620 
1621     return NGX_CONF_OK;
1622 }
1623 
1624 static char *
ngx_http_mogilefs_pass_block(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)1625 ngx_http_mogilefs_pass_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1626 {
1627     ngx_http_mogilefs_loc_conf_t *pmgcf = conf;
1628     ngx_http_core_loc_conf_t  *pclcf;
1629     ngx_http_conf_ctx_t       *ctx;
1630     char                      *rv;
1631     ngx_str_t                 *value;
1632     ngx_conf_t                 save;
1633     ngx_http_script_compile_t  sc;
1634     ngx_uint_t                 n, i;
1635     char                      *rc;
1636     ngx_str_t                  name;
1637 
1638     if (pmgcf->fetch_location.len != 0) {
1639         return "is duplicate";
1640     }
1641 
1642     if (pmgcf->upstream.upstream == 0 && pmgcf->tracker_lengths == NULL) {
1643         return "no tracker defined";
1644     }
1645 
1646     for(i=0;i < NGX_MOGILEFS_MAX_PATHS;i++) {
1647         name.data = ngx_http_mogilefs_path.data;
1648         name.len = ngx_http_mogilefs_path.len - 1;
1649 
1650         if(i > 0) {
1651             name.data[name.len] = '0' + i;
1652             name.len++;
1653         }
1654 
1655         pmgcf->index[i] = ngx_http_get_variable_index(cf, &name);
1656 
1657         if (pmgcf->index[i] == NGX_ERROR) {
1658             return NGX_CONF_ERROR;
1659         }
1660     }
1661 
1662     rc = ngx_http_mogilefs_create_spare_location(cf, NULL, &pmgcf->create_open_spare_location,
1663         NGX_MOGILEFS_CREATE_OPEN);
1664 
1665     if(rc != NGX_CONF_OK) {
1666         return rc;
1667     }
1668 
1669     rc = ngx_http_mogilefs_create_spare_location(cf, &ctx, &pmgcf->fetch_location,
1670         NGX_MOGILEFS_FETCH);
1671 
1672     if(rc != NGX_CONF_OK) {
1673         return rc;
1674     }
1675 
1676     rc = ngx_http_mogilefs_create_spare_location(cf, NULL, &pmgcf->create_close_spare_location,
1677         NGX_MOGILEFS_CREATE_CLOSE);
1678 
1679     if(rc != NGX_CONF_OK) {
1680         return rc;
1681     }
1682 
1683     pmgcf->location_type = NGX_MOGILEFS_MAIN;
1684 
1685     pclcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
1686     pclcf->handler = ngx_http_mogilefs_handler;
1687 
1688     if(cf->args->nelts > 1) {
1689         value = cf->args->elts;
1690 
1691         pmgcf->key = value[1];
1692 
1693         n = ngx_http_script_variables_count(&pmgcf->key);
1694 
1695         ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
1696 
1697         sc.cf = cf;
1698         sc.source = &pmgcf->key;
1699         sc.lengths = &pmgcf->key_lengths;
1700         sc.values = &pmgcf->key_values;
1701         sc.variables = n;
1702         sc.complete_lengths = 1;
1703         sc.complete_values = 1;
1704 
1705         if (ngx_http_script_compile(&sc) != NGX_OK) {
1706             return NGX_CONF_ERROR;
1707         }
1708     }
1709 
1710     save = *cf;
1711     cf->ctx = ctx;
1712     cf->cmd_type = NGX_HTTP_LOC_CONF;
1713 
1714     rv = ngx_conf_parse(cf, NULL);
1715 
1716     *cf = save;
1717 
1718     return rv;
1719 }
1720 
1721 static ngx_int_t
ngx_http_mogilefs_init(ngx_conf_t * cf)1722 ngx_http_mogilefs_init(ngx_conf_t *cf)
1723 {
1724     ngx_http_handler_pt        *h;
1725     ngx_http_core_main_conf_t  *cmcf;
1726 
1727     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
1728 
1729     h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
1730     if (h == NULL) {
1731         return NGX_ERROR;
1732     }
1733 
1734     *h = ngx_http_mogilefs_put_handler;
1735 
1736     return NGX_OK;
1737 }
1738