1 /*
2 * Copyright 2009-2010 Michael Dirolf
3 *
4 * Dual Licensed under the Apache License, Version 2.0 and the GNU
5 * General Public License, version 2 or (at your option) any later
6 * version.
7 *
8 * -- Apache License
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 *
21 * -- GNU GPL
22 * This program is free software; you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation; either version 2 of the License, or
25 * (at your option) any later version.
26 *
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public License along
33 * with this program; if not, write to the Free Software Foundation, Inc.,
34 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
35 */
36 /*
37 * TODO range support http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35
38 */
39
40 #include <ngx_config.h>
41 #include <ngx_core.h>
42 #include <ngx_http.h>
43 #include "mongo-c-driver/src/mongo.h"
44 #include "mongo-c-driver/src/gridfs.h"
45 #include <signal.h>
46 #include <stdio.h>
47
48 #define MONGO_MAX_RETRIES_PER_REQUEST 1
49 #define MONGO_RECONNECT_WAITTIME 500 //ms
50 #define TRUE 1
51 #define FALSE 0
52
53 /* Parse config directive */
54 static char *ngx_http_mongo(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy);
55
56 /* Parse config directive */
57 static char *ngx_http_gridfs(ngx_conf_t *directive, ngx_command_t *command, void *gridfs_conf);
58
59 static void *ngx_http_gridfs_create_main_conf(ngx_conf_t *directive);
60
61 static void *ngx_http_gridfs_create_loc_conf(ngx_conf_t *directive);
62
63 static char *ngx_http_gridfs_merge_loc_conf(ngx_conf_t *directive, void *parent, void *child);
64
65 static ngx_int_t ngx_http_gridfs_init_worker(ngx_cycle_t *cycle);
66
67 static ngx_int_t ngx_http_gridfs_handler(ngx_http_request_t *request);
68
69 static void ngx_http_gridfs_cleanup(void *data);
70
71 typedef struct {
72 ngx_str_t db;
73 ngx_str_t root_collection;
74 ngx_str_t field;
75 ngx_uint_t type;
76 ngx_str_t user;
77 ngx_str_t pass;
78 ngx_str_t mongo;
79 ngx_array_t *mongods; /* ngx_http_mongod_server_t */
80 ngx_str_t replset; /* Name of the replica set, if connecting. */
81 } ngx_http_gridfs_loc_conf_t;
82
83 typedef struct {
84 ngx_str_t db;
85 ngx_str_t user;
86 ngx_str_t pass;
87 } ngx_http_mongo_auth_t;
88
89 typedef struct {
90 ngx_str_t name;
91 mongo conn;
92 ngx_array_t *auths; /* ngx_http_mongo_auth_t */
93 } ngx_http_mongo_connection_t;
94
95 /* Maybe we should store a list of addresses instead. */
96 typedef struct {
97 ngx_str_t host;
98 in_port_t port;
99 } ngx_http_mongod_server_t;
100
101 typedef struct {
102 ngx_array_t loc_confs; /* ngx_http_gridfs_loc_conf_t */
103 } ngx_http_gridfs_main_conf_t;
104
105 typedef struct {
106 mongo_cursor **cursors;
107 ngx_uint_t numchunks;
108 } ngx_http_gridfs_cleanup_t;
109
110 /* Array specifying how to handle configuration directives. */
111 static ngx_command_t ngx_http_gridfs_commands[] = {
112
113 {
114 ngx_string("mongo"),
115 NGX_HTTP_LOC_CONF | NGX_CONF_1MORE,
116 ngx_http_mongo,
117 NGX_HTTP_LOC_CONF_OFFSET,
118 0,
119 NULL
120 },
121
122 {
123 ngx_string("gridfs"),
124 NGX_HTTP_LOC_CONF | NGX_CONF_1MORE,
125 ngx_http_gridfs,
126 NGX_HTTP_LOC_CONF_OFFSET,
127 0,
128 NULL
129 },
130
131 ngx_null_command
132 };
133
134 /* Module context. */
135 static ngx_http_module_t ngx_http_gridfs_module_ctx = {
136 NULL, /* preconfiguration */
137 NULL, /* postconfiguration */
138 ngx_http_gridfs_create_main_conf,
139 NULL, /* init main configuration */
140 NULL, /* create server configuration */
141 NULL, /* init serever configuration */
142 ngx_http_gridfs_create_loc_conf,
143 ngx_http_gridfs_merge_loc_conf
144 };
145
146 /* Module definition. */
147 ngx_module_t ngx_http_gridfs_module = {
148 NGX_MODULE_V1,
149 &ngx_http_gridfs_module_ctx,
150 ngx_http_gridfs_commands,
151 NGX_HTTP_MODULE,
152 NULL,
153 NULL,
154 ngx_http_gridfs_init_worker,
155 NULL,
156 NULL,
157 NULL,
158 NULL,
159 NGX_MODULE_V1_PADDING
160 };
161
162 ngx_array_t ngx_http_mongo_connections;
163
164 /* Parse the 'mongo' directive. */
ngx_http_mongo(ngx_conf_t * cf,ngx_command_t * cmd,void * void_conf)165 static char *ngx_http_mongo(ngx_conf_t *cf, ngx_command_t *cmd, void *void_conf) {
166 ngx_str_t *value;
167 ngx_url_t u;
168 ngx_uint_t i;
169 ngx_uint_t start;
170 ngx_http_mongod_server_t *mongod_server;
171 ngx_http_gridfs_loc_conf_t *gridfs_loc_conf;
172
173 gridfs_loc_conf = void_conf;
174
175 value = cf->args->elts;
176 gridfs_loc_conf->mongo = value[1];
177 gridfs_loc_conf->mongods = ngx_array_create(cf->pool, 7,
178 sizeof(ngx_http_mongod_server_t));
179 if (gridfs_loc_conf->mongods == NULL) {
180 return NULL;
181 }
182
183 /* If nelts is greater than 3, then the user has specified more than one
184 * setting in the 'mongo' directive. So we assume that we're connecting
185 * to a replica set and that the first string of the directive is the replica
186 * set name. We also start looking for host-port pairs at position 2; otherwise,
187 * we start at position 1.
188 */
189 if( cf->args->nelts >= 3 ) {
190 gridfs_loc_conf->replset.len = strlen( (char *)(value + 1)->data );
191 gridfs_loc_conf->replset.data = ngx_pstrdup( cf->pool, value + 1 );
192 start = 2;
193 } else
194 start = 1;
195
196 for (i = start; i < cf->args->nelts; i++) {
197
198 ngx_memzero(&u, sizeof(ngx_url_t));
199
200 u.url = value[i];
201 u.default_port = 27017;
202
203 if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
204 if (u.err) {
205 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
206 "%s in mongo \"%V\"", u.err, &u.url);
207 }
208 return NGX_CONF_ERROR;
209 }
210
211 mongod_server = ngx_array_push(gridfs_loc_conf->mongods);
212 mongod_server->host = u.host;
213 mongod_server->port = u.port;
214
215 }
216
217 return NGX_CONF_OK;
218 }
219
220 /* Parse the 'gridfs' directive. */
ngx_http_gridfs(ngx_conf_t * cf,ngx_command_t * command,void * void_conf)221 static char *ngx_http_gridfs(ngx_conf_t *cf, ngx_command_t *command, void *void_conf) {
222 ngx_http_gridfs_loc_conf_t *gridfs_loc_conf = void_conf;
223 ngx_http_core_loc_conf_t *core_conf;
224 ngx_str_t *value, type;
225 volatile ngx_uint_t i;
226
227 core_conf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
228 core_conf-> handler = ngx_http_gridfs_handler;
229
230 value = cf->args->elts;
231 gridfs_loc_conf->db = value[1];
232
233 /* Parse the parameters */
234 for (i = 2; i < cf->args->nelts; i++) {
235 if (ngx_strncmp(value[i].data, "root_collection=", 16) == 0) {
236 gridfs_loc_conf->root_collection.data = (u_char *) &value[i].data[16];
237 gridfs_loc_conf->root_collection.len = ngx_strlen(&value[i].data[16]);
238 continue;
239 }
240
241 if (ngx_strncmp(value[i].data, "field=", 6) == 0) {
242 gridfs_loc_conf->field.data = (u_char *) &value[i].data[6];
243 gridfs_loc_conf->field.len = ngx_strlen(&value[i].data[6]);
244
245 /* Currently only support for "_id", "filename" and "md5" */
246 if (gridfs_loc_conf->field.data != NULL
247 && ngx_strcmp(gridfs_loc_conf->field.data, "filename") != 0
248 && ngx_strcmp(gridfs_loc_conf->field.data, "_id") != 0
249 && ngx_strcmp(gridfs_loc_conf->field.data, "md5") != 0) {
250 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
251 "Unsupported Field: %s", gridfs_loc_conf->field.data);
252 return NGX_CONF_ERROR;
253 }
254
255 continue;
256 }
257
258 if (ngx_strncmp(value[i].data, "type=", 5) == 0) {
259 type = (ngx_str_t) ngx_string(&value[i].data[5]);
260
261 /* Currently only support for "objectid", "string", and "int" */
262 if (type.len == 0) {
263 gridfs_loc_conf->type = NGX_CONF_UNSET_UINT;
264 } else if (ngx_strcasecmp(type.data, (u_char *)"objectid") == 0) {
265 gridfs_loc_conf->type = BSON_OID;
266 } else if (ngx_strcasecmp(type.data, (u_char *)"string") == 0) {
267 gridfs_loc_conf->type = BSON_STRING;
268 } else if (ngx_strcasecmp(type.data, (u_char *)"int") == 0) {
269 gridfs_loc_conf->type = BSON_INT;
270 } else {
271 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
272 "Unsupported Type: %s", (char *)value[i].data);
273 return NGX_CONF_ERROR;
274 }
275
276 continue;
277 }
278
279 if (ngx_strncmp(value[i].data, "user=", 5) == 0) {
280 gridfs_loc_conf->user.data = (u_char *) &value[i].data[5];
281 gridfs_loc_conf->user.len = ngx_strlen(&value[i].data[5]);
282 continue;
283 }
284
285 if (ngx_strncmp(value[i].data, "pass=", 5) == 0) {
286 gridfs_loc_conf->pass.data = (u_char *) &value[i].data[5];
287 gridfs_loc_conf->pass.len = ngx_strlen(&value[i].data[5]);
288 continue;
289 }
290
291 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
292 "invalid parameter \"%V\"", &value[i]);
293 return NGX_CONF_ERROR;
294 }
295
296 if (gridfs_loc_conf->field.data != NULL
297 && ngx_strcmp(gridfs_loc_conf->field.data, "filename") == 0
298 && gridfs_loc_conf->type != BSON_STRING) {
299 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
300 "Field: filename, must be of Type: string");
301 return NGX_CONF_ERROR;
302 }
303
304 if (gridfs_loc_conf->field.data != NULL
305 && ngx_strcmp(gridfs_loc_conf->field.data, "md5") == 0
306 && gridfs_loc_conf->type != BSON_STRING) {
307 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
308 "Field: md5, must be of Type: string");
309 return NGX_CONF_ERROR;
310 }
311
312 if ((gridfs_loc_conf->user.data == NULL || gridfs_loc_conf->user.len == 0)
313 && !(gridfs_loc_conf->pass.data == NULL || gridfs_loc_conf->pass.len == 0)) {
314 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
315 "Password without username");
316 return NGX_CONF_ERROR;
317 }
318
319 if (!(gridfs_loc_conf->user.data == NULL || gridfs_loc_conf->user.len == 0)
320 && (gridfs_loc_conf->pass.data == NULL || gridfs_loc_conf->pass.len == 0)) {
321 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
322 "Username without password");
323 return NGX_CONF_ERROR;
324 }
325
326 return NGX_CONF_OK;
327 }
328
ngx_http_gridfs_create_main_conf(ngx_conf_t * cf)329 static void *ngx_http_gridfs_create_main_conf(ngx_conf_t *cf) {
330 ngx_http_gridfs_main_conf_t *gridfs_main_conf;
331
332 gridfs_main_conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_gridfs_main_conf_t));
333 if (gridfs_main_conf == NULL) {
334 return NULL;
335 }
336
337 if (ngx_array_init(&gridfs_main_conf->loc_confs, cf->pool, 4,
338 sizeof(ngx_http_gridfs_loc_conf_t *))
339 != NGX_OK) {
340 return NULL;
341 }
342
343 return gridfs_main_conf;
344 }
345
ngx_http_gridfs_create_loc_conf(ngx_conf_t * directive)346 static void *ngx_http_gridfs_create_loc_conf(ngx_conf_t *directive) {
347 ngx_http_gridfs_loc_conf_t *gridfs_conf;
348
349 gridfs_conf = ngx_pcalloc(directive->pool, sizeof(ngx_http_gridfs_loc_conf_t));
350 if (gridfs_conf == NULL) {
351 ngx_conf_log_error(NGX_LOG_EMERG, directive, 0,
352 "Failed to allocate memory for GridFS Location Config.");
353 return NGX_CONF_ERROR;
354 }
355
356 gridfs_conf->db.data = NULL;
357 gridfs_conf->db.len = 0;
358 gridfs_conf->root_collection.data = NULL;
359 gridfs_conf->root_collection.len = 0;
360 gridfs_conf->field.data = NULL;
361 gridfs_conf->field.len = 0;
362 gridfs_conf->type = NGX_CONF_UNSET_UINT;
363 gridfs_conf->user.data = NULL;
364 gridfs_conf->user.len = 0;
365 gridfs_conf->pass.data = NULL;
366 gridfs_conf->pass.len = 0;
367 gridfs_conf->mongo.data = NULL;
368 gridfs_conf->mongo.len = 0;
369 gridfs_conf->mongods = NGX_CONF_UNSET_PTR;
370
371 return gridfs_conf;
372 }
373
ngx_http_gridfs_merge_loc_conf(ngx_conf_t * cf,void * void_parent,void * void_child)374 static char *ngx_http_gridfs_merge_loc_conf(ngx_conf_t *cf, void *void_parent, void *void_child) {
375 ngx_http_gridfs_loc_conf_t *parent = void_parent;
376 ngx_http_gridfs_loc_conf_t *child = void_child;
377 ngx_http_gridfs_main_conf_t *gridfs_main_conf = ngx_http_conf_get_module_main_conf(cf, ngx_http_gridfs_module);
378 ngx_http_gridfs_loc_conf_t **gridfs_loc_conf;
379 ngx_http_mongod_server_t *mongod_server;
380
381 ngx_conf_merge_str_value(child->db, parent->db, NULL);
382 ngx_conf_merge_str_value(child->root_collection, parent->root_collection, "fs");
383 ngx_conf_merge_str_value(child->field, parent->field, "_id");
384 ngx_conf_merge_uint_value(child->type, parent->type, BSON_OID);
385 ngx_conf_merge_str_value(child->user, parent->user, NULL);
386 ngx_conf_merge_str_value(child->pass, parent->pass, NULL);
387 ngx_conf_merge_str_value(child->mongo, parent->mongo, "127.0.0.1:27017");
388
389 if (child->mongods == NGX_CONF_UNSET_PTR) {
390 if (parent->mongods != NGX_CONF_UNSET_PTR) {
391 child->mongods = parent->mongods;
392 } else {
393 child->mongods = ngx_array_create(cf->pool, 4,
394 sizeof(ngx_http_mongod_server_t));
395 mongod_server = ngx_array_push(child->mongods);
396 mongod_server->host.data = (u_char *)"127.0.0.1";
397 mongod_server->host.len = sizeof("127.0.0.1") - 1;
398 mongod_server->port = 27017;
399 }
400 }
401
402 // Add the local gridfs conf to the main gridfs conf
403 if (child->db.data) {
404 gridfs_loc_conf = ngx_array_push(&gridfs_main_conf->loc_confs);
405 *gridfs_loc_conf = child;
406 }
407
408 return NGX_CONF_OK;
409 }
410
ngx_http_get_mongo_connection(ngx_str_t name)411 ngx_http_mongo_connection_t *ngx_http_get_mongo_connection( ngx_str_t name ) {
412 ngx_http_mongo_connection_t *mongo_conns;
413 ngx_uint_t i;
414
415 mongo_conns = ngx_http_mongo_connections.elts;
416
417 for ( i = 0; i < ngx_http_mongo_connections.nelts; i++ ) {
418 if ( name.len == mongo_conns[i].name.len
419 && ngx_strncmp(name.data, mongo_conns[i].name.data, name.len) == 0 ) {
420 return &mongo_conns[i];
421 }
422 }
423
424 return NULL;
425 }
426
ngx_http_mongo_authenticate(ngx_log_t * log,ngx_http_gridfs_loc_conf_t * gridfs_loc_conf)427 static ngx_int_t ngx_http_mongo_authenticate(ngx_log_t *log, ngx_http_gridfs_loc_conf_t *gridfs_loc_conf) {
428 ngx_http_mongo_connection_t *mongo_conn;
429 ngx_http_mongo_auth_t *mongo_auth;
430 mongo_cursor *cursor = NULL;
431 bson empty;
432 char *test;
433 int error;
434
435 mongo_conn = ngx_http_get_mongo_connection( gridfs_loc_conf->mongo );
436 if (mongo_conn == NULL) {
437 ngx_log_error(NGX_LOG_ERR, log, 0,
438 "Mongo Connection not found: \"%V\"", &gridfs_loc_conf->mongo);
439 }
440
441 // Authenticate
442 if (gridfs_loc_conf->user.data != NULL && gridfs_loc_conf->pass.data != NULL) {
443 if (mongo_cmd_authenticate( &mongo_conn->conn,
444 (const char*)gridfs_loc_conf->db.data,
445 (const char*)gridfs_loc_conf->user.data,
446 (const char*)gridfs_loc_conf->pass.data )
447 != MONGO_OK) {
448 ngx_log_error(NGX_LOG_ERR, log, 0,
449 "Invalid mongo user/pass: %s/%s",
450 gridfs_loc_conf->user.data,
451 gridfs_loc_conf->pass.data);
452 return NGX_ERROR;
453 }
454
455 mongo_auth = ngx_array_push(mongo_conn->auths);
456 mongo_auth->db = gridfs_loc_conf->db;
457 mongo_auth->user = gridfs_loc_conf->user;
458 mongo_auth->pass = gridfs_loc_conf->pass;
459 }
460
461 // Run a test command to test authentication.
462 test = (char*)malloc( gridfs_loc_conf->db.len + sizeof(".test"));
463 ngx_cpystrn((u_char*)test, (u_char*)gridfs_loc_conf->db.data, gridfs_loc_conf->db.len+1);
464 ngx_cpystrn((u_char*)(test+gridfs_loc_conf->db.len),(u_char*)".test", sizeof(".test"));
465 bson_empty(&empty);
466 cursor = mongo_find(&mongo_conn->conn, test, &empty, NULL, 0, 0, 0);
467 error = mongo_cmd_get_last_error(&mongo_conn->conn, (char*)gridfs_loc_conf->db.data, NULL);
468 free(test);
469 mongo_cursor_destroy(cursor);
470 if (error) {
471 ngx_log_error(NGX_LOG_ERR, log, 0, "Authentication Required");
472 return NGX_ERROR;
473 }
474
475 return NGX_OK;
476 }
477
ngx_http_mongo_add_connection(ngx_cycle_t * cycle,ngx_http_gridfs_loc_conf_t * gridfs_loc_conf)478 static ngx_int_t ngx_http_mongo_add_connection(ngx_cycle_t *cycle, ngx_http_gridfs_loc_conf_t *gridfs_loc_conf) {
479 ngx_http_mongo_connection_t *mongo_conn;
480 int status;
481 ngx_http_mongod_server_t *mongods;
482 volatile ngx_uint_t i;
483 u_char host[255];
484
485 mongods = gridfs_loc_conf->mongods->elts;
486
487 mongo_conn = ngx_http_get_mongo_connection( gridfs_loc_conf->mongo );
488 if (mongo_conn != NULL) {
489 return NGX_OK;
490 }
491
492 mongo_conn = ngx_array_push(&ngx_http_mongo_connections);
493 if (mongo_conn == NULL) {
494 return NGX_ERROR;
495 }
496
497 mongo_conn->name = gridfs_loc_conf->mongo;
498 mongo_conn->auths = ngx_array_create(cycle->pool, 4, sizeof(ngx_http_mongo_auth_t));
499
500 if ( gridfs_loc_conf->mongods->nelts == 1 ) {
501 ngx_cpystrn( host, mongods[0].host.data, mongods[0].host.len + 1 );
502 status = mongo_connect( &mongo_conn->conn, (const char*)host, mongods[0].port );
503 } else if ( gridfs_loc_conf->mongods->nelts >= 2 && gridfs_loc_conf->mongods->nelts < 9 ) {
504
505 /* Initiate replica set connection. */
506 mongo_replset_init( &mongo_conn->conn, (const char *)gridfs_loc_conf->replset.data );
507
508 /* Add replica set seeds. */
509 for( i=0; i<gridfs_loc_conf->mongods->nelts; ++i ) {
510 ngx_cpystrn( host, mongods[i].host.data, mongods[i].host.len + 1 );
511 mongo_replset_add_seed( &mongo_conn->conn, (const char *)host, mongods[i].port );
512 }
513 status = mongo_replset_connect( &mongo_conn->conn );
514 } else {
515 ngx_log_error(NGX_LOG_ERR, cycle->log, 0,
516 "Mongo Nginx Exception: Too many strings provided in 'mongo' directive.");
517 return NGX_ERROR;
518 }
519
520 switch (mongo_conn->conn.err) {
521 case MONGO_CONN_SUCCESS:
522 break;
523 case MONGO_CONN_NO_SOCKET:
524 ngx_log_error(NGX_LOG_ERR, cycle->log, 0,
525 "Mongo Exception: No Socket");
526 return NGX_ERROR;
527 case MONGO_CONN_FAIL:
528 ngx_log_error(NGX_LOG_ERR, cycle->log, 0,
529 "Mongo Exception: Connection Failure.");
530 return NGX_ERROR;
531 case MONGO_CONN_ADDR_FAIL:
532 ngx_log_error(NGX_LOG_ERR, cycle->log, 0,
533 "Mongo Exception: getaddrinfo Failure.");
534 return NGX_ERROR;
535 case MONGO_CONN_NOT_MASTER:
536 ngx_log_error(NGX_LOG_ERR, cycle->log, 0,
537 "Mongo Exception: Not Master");
538 return NGX_ERROR;
539 case MONGO_CONN_BAD_SET_NAME:
540 ngx_log_error(NGX_LOG_ERR, cycle->log, 0,
541 "Mongo Exception: Replica set name %s does not match.", gridfs_loc_conf->replset.data);
542 return NGX_ERROR;
543 case MONGO_CONN_NO_PRIMARY:
544 ngx_log_error(NGX_LOG_ERR, cycle->log, 0,
545 "Mongo Exception: Cannot connect to primary node.");
546 return NGX_ERROR;
547 default:
548 ngx_log_error(NGX_LOG_ERR, cycle->log, 0,
549 "Mongo Exception: Unknown Error");
550 return NGX_ERROR;
551 }
552
553 return NGX_OK;
554 }
555
ngx_http_gridfs_init_worker(ngx_cycle_t * cycle)556 static ngx_int_t ngx_http_gridfs_init_worker(ngx_cycle_t *cycle) {
557 ngx_http_gridfs_main_conf_t *gridfs_main_conf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_gridfs_module);
558 ngx_http_gridfs_loc_conf_t **gridfs_loc_confs;
559 ngx_uint_t i;
560
561 signal(SIGPIPE, SIG_IGN);
562
563 gridfs_loc_confs = gridfs_main_conf->loc_confs.elts;
564
565 ngx_array_init(&ngx_http_mongo_connections, cycle->pool, 4, sizeof(ngx_http_mongo_connection_t));
566
567 for (i = 0; i < gridfs_main_conf->loc_confs.nelts; i++) {
568 if (ngx_http_mongo_add_connection(cycle, gridfs_loc_confs[i]) == NGX_ERROR) {
569 return NGX_ERROR;
570 }
571 if (ngx_http_mongo_authenticate(cycle->log, gridfs_loc_confs[i]) == NGX_ERROR) {
572 return NGX_ERROR;
573 }
574 }
575
576 return NGX_OK;
577 }
578
ngx_http_mongo_reconnect(ngx_log_t * log,ngx_http_mongo_connection_t * mongo_conn)579 static ngx_int_t ngx_http_mongo_reconnect(ngx_log_t *log, ngx_http_mongo_connection_t *mongo_conn) {
580 volatile int status = MONGO_CONN_FAIL;
581
582 if (&mongo_conn->conn.connected) {
583 mongo_disconnect(&mongo_conn->conn);
584 ngx_msleep(MONGO_RECONNECT_WAITTIME);
585 status = mongo_reconnect(&mongo_conn->conn);
586 } else {
587 ngx_log_error(NGX_LOG_ERR, log, 0,
588 "Mongo Nginx Exception: Not Connected");
589 return NGX_ERROR;
590 }
591
592 switch (mongo_conn->conn.err) {
593 case MONGO_CONN_SUCCESS:
594 break;
595 case MONGO_CONN_NO_SOCKET:
596 ngx_log_error(NGX_LOG_ERR, log, 0,
597 "Mongo Exception: No Socket");
598 return NGX_ERROR;
599 case MONGO_CONN_FAIL:
600 ngx_log_error(NGX_LOG_ERR, log, 0,
601 "Mongo Exception: Connection Failure %s:%i;",
602 mongo_conn->conn.primary->host,
603 mongo_conn->conn.primary->port);
604 return NGX_ERROR;
605 case MONGO_CONN_ADDR_FAIL:
606 ngx_log_error(NGX_LOG_ERR, log, 0,
607 "Mongo Exception: getaddrinfo Failure");
608 return NGX_ERROR;
609 case MONGO_CONN_NOT_MASTER:
610 ngx_log_error(NGX_LOG_ERR, log, 0,
611 "Mongo Exception: Not Master");
612 return NGX_ERROR;
613 default:
614 ngx_log_error(NGX_LOG_ERR, log, 0,
615 "Mongo Exception: Unknown Error");
616 return NGX_ERROR;
617 }
618
619 return NGX_OK;
620 }
621
ngx_http_mongo_reauth(ngx_log_t * log,ngx_http_mongo_connection_t * mongo_conn)622 static ngx_int_t ngx_http_mongo_reauth(ngx_log_t *log, ngx_http_mongo_connection_t *mongo_conn) {
623 ngx_http_mongo_auth_t *auths;
624 volatile ngx_uint_t i;
625 volatile ngx_int_t status = 0;
626 auths = mongo_conn->auths->elts;
627
628 for (i = 0; i < mongo_conn->auths->nelts; i++) {
629 status = mongo_cmd_authenticate( &mongo_conn->conn,
630 (const char*)auths[i].db.data,
631 (const char*)auths[i].user.data,
632 (const char*)auths[i].pass.data );
633 if (status != MONGO_OK) {
634 ngx_log_error(NGX_LOG_ERR, log, 0,
635 "Invalid mongo user/pass: %s/%s, during reauth",
636 auths[i].user.data,
637 auths[i].pass.data);
638 return NGX_ERROR;
639 }
640 }
641
642 return NGX_OK;
643 }
644
h_digit(char hex)645 static char h_digit(char hex) {
646 return (hex >= '0' && hex <= '9') ? hex - '0': ngx_tolower(hex)-'a'+10;
647 }
648
htoi(char * h)649 static int htoi(char *h) {
650 char ok[] = "0123456789AaBbCcDdEeFf";
651
652 if (ngx_strchr(ok, h[0]) == NULL || ngx_strchr(ok,h[1]) == NULL) { return -1; }
653 return h_digit(h[0])*16 + h_digit(h[1]);
654 }
655
url_decode(char * filename)656 static int url_decode(char *filename) {
657 char *read = filename;
658 char *write = filename;
659 char hex[3];
660 int c;
661
662 hex[2] = '\0';
663 while (*read != '\0'){
664 if (*read == '%') {
665 hex[0] = *(++read);
666 if (hex[0] == '\0') return 0;
667 hex[1] = *(++read);
668 if (hex[1] == '\0') return 0;
669 c = htoi(hex);
670 if (c == -1) return 0;
671 *write = (char)c;
672 }
673 else *write = *read;
674 read++;
675 write++;
676 }
677 *write = '\0';
678 return 1;
679 }
680
gridfs_parse_range(ngx_http_request_t * r,ngx_str_t * range_str,uint64_t * range_start,uint64_t * range_end,gridfs_offset content_length)681 static void gridfs_parse_range(ngx_http_request_t *r, ngx_str_t *range_str, uint64_t *range_start, uint64_t *range_end, gridfs_offset content_length) {
682 u_char *p, *last;
683 off_t start, end;
684 ngx_uint_t bad;
685 enum {
686 sw_start = 0,
687 sw_first_byte_pos,
688 sw_first_byte_pos_n,
689 sw_last_byte_pos,
690 sw_last_byte_pos_n,
691 sw_done
692 } state = 0;
693
694 p = (u_char *) ngx_strnstr(range_str->data, "bytes=", range_str->len);
695
696 if (p == NULL) {
697 return;
698 }
699
700 p += sizeof("bytes=") - 1;
701 last = range_str->data + range_str->len;
702
703 /*
704 * bytes= contain ranges compatible with RFC 2616, "14.35.1 Byte Ranges",
705 * but no whitespaces permitted
706 */
707
708 bad = 0;
709 start = 0;
710 end = 0;
711
712 while (p < last) {
713
714 switch (state) {
715
716 case sw_start:
717 case sw_first_byte_pos:
718 if (*p == '-') {
719 p++;
720 state = sw_last_byte_pos;
721 break;
722 }
723 start = 0;
724 state = sw_first_byte_pos_n;
725
726 /* fall through */
727
728 case sw_first_byte_pos_n:
729 if (*p == '-') {
730 p++;
731 state = sw_last_byte_pos;
732 break;
733 }
734 if (*p < '0' || *p > '9') {
735 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
736 "bytes header filter: unexpected char '%c'"
737 " (expected first-byte-pos)", *p);
738 bad = 1;
739 break;
740 }
741 start = start * 10 + *p - '0';
742 p++;
743 break;
744
745 case sw_last_byte_pos:
746 if (*p == ',' || *p == '&' || *p == ';') {
747 /* no last byte pos, assume end of file */
748 end = content_length - 1;
749 state = sw_done;
750 break;
751 }
752 end = 0;
753 state = sw_last_byte_pos_n;
754
755 /* fall though */
756
757 case sw_last_byte_pos_n:
758 if (*p == ',' || *p == '&' || *p == ';') {
759 state = sw_done;
760 break;
761 }
762 if (*p < '0' || *p > '9') {
763 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
764 "bytes header filter: unexpected char '%c'"
765 " (expected last-byte-pos)", *p);
766 bad = 1;
767 break;
768 }
769 end = end * 10 + *p - '0';
770 p++;
771 break;
772
773 case sw_done:
774 *range_start = start;
775 *range_end = end;
776
777 break;
778 }
779
780 if (bad) {
781 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
782 "bytes header filter: invalid range specification");
783 return;
784 }
785 }
786
787 switch (state) {
788
789 case sw_last_byte_pos:
790 end = content_length - 1;
791
792 case sw_last_byte_pos_n:
793 if (start > end) {
794 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
795 "bytes header filter: invalid range specification");
796 return;
797 }
798
799 *range_start = start;
800 *range_end = end;
801 break;
802
803 default:
804 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
805 "bytes header filter: invalid range specification");
806 return;
807
808 }
809 }
810
ngx_http_gridfs_handler(ngx_http_request_t * request)811 static ngx_int_t ngx_http_gridfs_handler(ngx_http_request_t *request) {
812 ngx_http_gridfs_loc_conf_t *gridfs_conf;
813 ngx_http_core_loc_conf_t *core_conf;
814 ngx_buf_t *buffer;
815 ngx_chain_t out;
816 ngx_str_t location_name;
817 ngx_str_t full_uri;
818 char *value;
819 ngx_http_mongo_connection_t *mongo_conn;
820 gridfs gfs;
821 gridfile gfile;
822 gridfs_offset length;
823 ngx_uint_t numchunks;
824 char *contenttype;
825 char *md5;
826 bson_date_t last_modified;
827
828 volatile ngx_uint_t i;
829 ngx_int_t rc = NGX_OK;
830 bson query;
831 bson_oid_t oid;
832 mongo_cursor **cursors;
833 gridfs_offset chunk_len;
834 const char *chunk_data;
835 bson_iterator it;
836 bson chunk;
837 ngx_pool_cleanup_t *gridfs_cln;
838 ngx_http_gridfs_cleanup_t *gridfs_clndata;
839 int status;
840 volatile ngx_uint_t e = FALSE;
841 volatile ngx_uint_t ecounter = 0;
842 uint64_t range_start = 0;
843 uint64_t range_end = 0;
844 uint64_t current_buf_pos = 0;
845
846 gridfs_conf = ngx_http_get_module_loc_conf(request, ngx_http_gridfs_module);
847 core_conf = ngx_http_get_module_loc_conf(request, ngx_http_core_module);
848
849 // ---------- ENSURE MONGO CONNECTION ---------- //
850
851 mongo_conn = ngx_http_get_mongo_connection( gridfs_conf->mongo );
852 if (mongo_conn == NULL) {
853 ngx_log_error(NGX_LOG_ERR, request->connection->log, 0,
854 "Mongo Connection not found: \"%V\"", &gridfs_conf->mongo);
855 return NGX_HTTP_INTERNAL_SERVER_ERROR;
856 }
857
858 if (mongo_conn->conn.connected == 0) {
859 if (ngx_http_mongo_reconnect(request->connection->log, mongo_conn) == NGX_ERROR) {
860 ngx_log_error(NGX_LOG_ERR, request->connection->log, 0,
861 "Could not connect to mongo: \"%V\"", &gridfs_conf->mongo);
862 if(mongo_conn->conn.connected) { mongo_disconnect(&mongo_conn->conn); }
863 return NGX_HTTP_SERVICE_UNAVAILABLE;
864 }
865 if (ngx_http_mongo_reauth(request->connection->log, mongo_conn) == NGX_ERROR) {
866 ngx_log_error(NGX_LOG_ERR, request->connection->log, 0,
867 "Failed to reauth to mongo: \"%V\"", &gridfs_conf->mongo);
868 if(mongo_conn->conn.connected) { mongo_disconnect(&mongo_conn->conn); }
869 return NGX_HTTP_SERVICE_UNAVAILABLE;
870 }
871 }
872
873 // ---------- RETRIEVE KEY ---------- //
874
875 location_name = core_conf->name;
876 full_uri = request->uri;
877
878 if (full_uri.len < location_name.len) {
879 ngx_log_error(NGX_LOG_ERR, request->connection->log, 0,
880 "Invalid location name or uri.");
881 return NGX_HTTP_INTERNAL_SERVER_ERROR;
882 }
883
884 value = (char*)malloc(sizeof(char) * (full_uri.len - location_name.len + 1));
885 if (value == NULL) {
886 ngx_log_error(NGX_LOG_ERR, request->connection->log, 0,
887 "Failed to allocate memory for value buffer.");
888 return NGX_HTTP_INTERNAL_SERVER_ERROR;
889 }
890 memcpy(value, full_uri.data + location_name.len, full_uri.len - location_name.len);
891 value[full_uri.len - location_name.len] = '\0';
892
893 if (!url_decode(value)) {
894 ngx_log_error(NGX_LOG_ERR, request->connection->log, 0,
895 "Malformed request.");
896 free(value);
897 return NGX_HTTP_BAD_REQUEST;
898 }
899
900 // ---------- RETRIEVE GRIDFILE ---------- //
901
902 do {
903 e = FALSE;
904 status = gridfs_init(&mongo_conn->conn,
905 (const char*)gridfs_conf->db.data,
906 (const char*)gridfs_conf->root_collection.data,
907 &gfs);
908 if (status != MONGO_OK) {
909 e = TRUE; ecounter++;
910 if (ecounter > MONGO_MAX_RETRIES_PER_REQUEST
911 || ngx_http_mongo_reconnect(request->connection->log, mongo_conn) == NGX_ERROR
912 || ngx_http_mongo_reauth(request->connection->log, mongo_conn) == NGX_ERROR) {
913 ngx_log_error(NGX_LOG_ERR, request->connection->log, 0,
914 "Mongo connection dropped, could not reconnect");
915 if(mongo_conn->conn.connected) { mongo_disconnect(&mongo_conn->conn); }
916 free(value);
917 return NGX_HTTP_SERVICE_UNAVAILABLE;
918 }
919 }
920 } while (e);
921
922 bson_init(&query);
923 switch (gridfs_conf->type) {
924 case BSON_OID:
925 bson_oid_from_string(&oid, value);
926 bson_append_oid(&query, (char*)gridfs_conf->field.data, &oid);
927 break;
928 case BSON_INT:
929 bson_append_int(&query, (char*)gridfs_conf->field.data, ngx_atoi((u_char*)value, strlen(value)));
930 break;
931 case BSON_STRING:
932 bson_append_string(&query, (char*)gridfs_conf->field.data, value);
933 break;
934 }
935 bson_finish(&query);
936
937 status = gridfs_find_query(&gfs, &query, &gfile);
938
939 bson_destroy(&query);
940 free(value);
941
942 if(status == MONGO_ERROR) {
943 gridfs_destroy(&gfs);
944 return NGX_HTTP_NOT_FOUND;
945 }
946
947 /* Get information about the file */
948 length = gridfile_get_contentlength(&gfile);
949 numchunks = gridfile_get_numchunks(&gfile);
950
951 // NaN workaround
952 if (numchunks > INT_MAX)
953 {
954 gridfile_destroy(&gfile);
955 gridfs_destroy(&gfs);
956 return NGX_HTTP_NOT_FOUND;
957 }
958
959 contenttype = (char*)gridfile_get_contenttype(&gfile);
960 md5 = (char*)gridfile_get_md5(&gfile);
961 last_modified = gridfile_get_uploaddate(&gfile);
962
963 if (request->headers_in.range) {
964 gridfs_parse_range(request, &request->headers_in.range->value, &range_start, &range_end, length);
965 }
966
967 // ---------- SEND THE HEADERS ---------- //
968
969 if (range_start == 0 && range_end == 0) {
970 request->headers_out.status = NGX_HTTP_OK;
971 request->headers_out.content_length_n = length;
972 } else {
973 request->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
974 request->headers_out.content_length_n = length;
975
976 ngx_table_elt_t *content_range;
977
978 content_range = ngx_list_push(&request->headers_out.headers);
979 if (content_range == NULL) {
980 return NGX_ERROR;
981 }
982
983 request->headers_out.content_range = content_range;
984
985 content_range->hash = 1;
986 ngx_str_set(&content_range->key, "Content-Range");
987
988 content_range->value.data = ngx_pnalloc(request->pool,sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN);
989 if (content_range->value.data == NULL) {
990 return NGX_ERROR;
991 }
992
993 /* "Content-Range: bytes SSSS-EEEE/TTTT" header */
994 content_range->value.len = ngx_sprintf(content_range->value.data,
995 "bytes %O-%O/%O",
996 range_start, range_end,
997 request->headers_out.content_length_n)
998 - content_range->value.data;
999
1000 request->headers_out.content_length_n = range_end - range_start + 1;
1001 }
1002 if (contenttype != NULL) {
1003 request->headers_out.content_type.len = strlen(contenttype);
1004 request->headers_out.content_type.data = (u_char*)contenttype;
1005 }
1006 else ngx_http_set_content_type(request);
1007
1008 // use md5 field as ETag if possible
1009 if (md5 != NULL) {
1010 request->headers_out.etag = ngx_list_push(&request->headers_out.headers);
1011 request->headers_out.etag->hash = 1;
1012 request->headers_out.etag->key.len = sizeof("ETag") - 1;
1013 request->headers_out.etag->key.data = (u_char*)"ETag";
1014
1015 ngx_buf_t *b;
1016 b = ngx_create_temp_buf(request->pool, strlen(md5) + 2);
1017 b->last = ngx_sprintf(b->last, "\"%s\"", md5);
1018 request->headers_out.etag->value.len = strlen(md5) + 2;
1019 request->headers_out.etag->value.data = b->start;
1020 }
1021
1022 // use uploadDate field as last_modified if possible
1023 if (last_modified) {
1024 request->headers_out.last_modified_time = (time_t)(last_modified/1000);
1025 }
1026
1027 /* Determine if content is gzipped, set headers accordingly */
1028 if ( gridfile_get_boolean(&gfile,"gzipped") ) {
1029 ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, gridfile_get_field(&gfile,"gzipped") );
1030 request->headers_out.content_encoding = ngx_list_push(&request->headers_out.headers);
1031 if (request->headers_out.content_encoding == NULL) {
1032 gridfile_destroy(&gfile);
1033 gridfs_destroy(&gfs);
1034 return NGX_ERROR;
1035 }
1036 request->headers_out.content_encoding->hash = 1;
1037 request->headers_out.content_encoding->key.len = sizeof("Content-Encoding") - 1;
1038 request->headers_out.content_encoding->key.data = (u_char *) "Content-Encoding";
1039 request->headers_out.content_encoding->value.len = sizeof("gzip") - 1;
1040 request->headers_out.content_encoding->value.data = (u_char *) "gzip";
1041 }
1042
1043 ngx_http_send_header(request);
1044
1045 if (request->method == NGX_HTTP_HEAD) {
1046 gridfile_destroy(&gfile);
1047 gridfs_destroy(&gfs);
1048 request->header_only = 1;
1049 return NGX_OK;
1050 }
1051
1052 // ---------- SEND THE BODY ---------- //
1053
1054 /* Empty file */
1055 if (numchunks == 0) {
1056 /* Allocate space for the response buffer */
1057 buffer = ngx_pcalloc(request->pool, sizeof(ngx_buf_t));
1058 if (buffer == NULL) {
1059 ngx_log_error(NGX_LOG_ERR, request->connection->log, 0,
1060 "Failed to allocate response buffer");
1061 gridfile_destroy(&gfile);
1062 gridfs_destroy(&gfs);
1063 return NGX_HTTP_INTERNAL_SERVER_ERROR;
1064 }
1065
1066 buffer->pos = NULL;
1067 buffer->last = NULL;
1068 buffer->memory = 1;
1069 buffer->last_buf = 1;
1070 out.buf = buffer;
1071 out.next = NULL;
1072
1073 gridfile_destroy(&gfile);
1074 gridfs_destroy(&gfs);
1075
1076 return ngx_http_output_filter(request, &out);
1077 }
1078
1079 cursors = (mongo_cursor **)ngx_pcalloc(request->pool, sizeof(mongo_cursor *) * numchunks);
1080 if (cursors == NULL) {
1081 gridfile_destroy(&gfile);
1082 gridfs_destroy(&gfs);
1083 return NGX_HTTP_INTERNAL_SERVER_ERROR;
1084 }
1085
1086 ngx_memzero( cursors, sizeof(mongo_cursor *) * numchunks);
1087
1088 /* Hook in the cleanup function */
1089 gridfs_cln = ngx_pool_cleanup_add(request->pool, sizeof(ngx_http_gridfs_cleanup_t));
1090 if (gridfs_cln == NULL) {
1091 gridfile_destroy(&gfile);
1092 gridfs_destroy(&gfs);
1093 return NGX_HTTP_INTERNAL_SERVER_ERROR;
1094 }
1095 gridfs_cln->handler = ngx_http_gridfs_cleanup;
1096 gridfs_clndata = gridfs_cln->data;
1097 gridfs_clndata->cursors = cursors;
1098 gridfs_clndata->numchunks = numchunks;
1099
1100 /* Read and serve chunk by chunk */
1101 for (i = 0; i < numchunks; i++) {
1102
1103 /* Allocate space for the response buffer */
1104 buffer = ngx_pcalloc(request->pool, sizeof(ngx_buf_t));
1105 if (buffer == NULL) {
1106 ngx_log_error(NGX_LOG_ERR, request->connection->log, 0,
1107 "Failed to allocate response buffer");
1108 gridfile_destroy(&gfile);
1109 gridfs_destroy(&gfs);
1110 return NGX_HTTP_INTERNAL_SERVER_ERROR;
1111 }
1112
1113 /* Fetch the chunk from mongo */
1114 do {
1115 e = FALSE;
1116 cursors[i] = gridfile_get_chunks(&gfile, i, 1);
1117 if (!(cursors[i] && mongo_cursor_next(cursors[i]) == MONGO_OK)) {
1118 e = TRUE; ecounter++;
1119 if (ecounter > MONGO_MAX_RETRIES_PER_REQUEST
1120 || ngx_http_mongo_reconnect(request->connection->log, mongo_conn) == NGX_ERROR
1121 || ngx_http_mongo_reauth(request->connection->log, mongo_conn) == NGX_ERROR) {
1122 ngx_log_error(NGX_LOG_ERR, request->connection->log, 0,
1123 "Mongo connection dropped, could not reconnect");
1124 if(mongo_conn->conn.connected) { mongo_disconnect(&mongo_conn->conn); }
1125 gridfile_destroy(&gfile);
1126 gridfs_destroy(&gfs);
1127 return NGX_HTTP_SERVICE_UNAVAILABLE;
1128 }
1129 }
1130 } while (e);
1131
1132 chunk = cursors[i]->current;
1133 bson_find(&it, &chunk, "data");
1134 chunk_len = bson_iterator_bin_len( &it );
1135 chunk_data = bson_iterator_bin_data( &it );
1136
1137 if (range_start == 0 && range_end == 0) {
1138 /* <<no range request>> */
1139 /* Set up the buffer chain */
1140 buffer->pos = (u_char*)chunk_data;
1141 buffer->last = (u_char*)chunk_data + chunk_len;
1142 buffer->memory = 1;
1143 buffer->last_buf = (i == numchunks-1);
1144 out.buf = buffer;
1145 out.next = NULL;
1146
1147 /* Serve the Chunk */
1148 rc = ngx_http_output_filter(request, &out);
1149 } else {
1150 /* <<range request>> */
1151 if ( range_start >= (current_buf_pos+chunk_len) ||
1152 range_end <= current_buf_pos) {
1153 /* no output */
1154 ngx_pfree(request->pool, buffer);
1155 } else {
1156 if (range_start <= current_buf_pos) {
1157 buffer->pos = (u_char*)chunk_data;
1158 } else {
1159 buffer->pos = (u_char*)chunk_data + (range_start - current_buf_pos);
1160 }
1161 if (range_end < (current_buf_pos+chunk_len)) {
1162 buffer->last = (u_char*)chunk_data + (range_end - current_buf_pos + 1);
1163 } else {
1164 buffer->last = (u_char*)chunk_data + chunk_len;
1165 }
1166 if (buffer->pos == buffer->last) {
1167 ngx_log_error(NGX_LOG_ALERT, request->connection->log, 0,
1168 "zero size buf in writer "
1169 "range_start:%d range_end:%d "
1170 "current_buf_pos:%d chunk_len:%d i:%d numchunk:%d",
1171 range_start,range_end,
1172 current_buf_pos, chunk_len,
1173 i,numchunks);
1174 }
1175 buffer->memory = 1;
1176 buffer->last_buf = (i == numchunks-1) || (range_end < (current_buf_pos+chunk_len));
1177 out.buf = buffer;
1178 out.next = NULL;
1179
1180 /* Serve the Chunk */
1181 rc = ngx_http_output_filter(request, &out);
1182 }
1183 }
1184
1185 current_buf_pos += chunk_len;
1186
1187 /* TODO: More Codes to Catch? */
1188 if (rc == NGX_ERROR) {
1189 gridfile_destroy(&gfile);
1190 gridfs_destroy(&gfs);
1191 return NGX_ERROR;
1192 }
1193 }
1194
1195 gridfile_destroy(&gfile);
1196 gridfs_destroy(&gfs);
1197
1198 return rc;
1199 }
1200
1201
ngx_http_gridfs_cleanup(void * data)1202 static void ngx_http_gridfs_cleanup(void *data) {
1203 ngx_http_gridfs_cleanup_t *gridfs_clndata;
1204 volatile ngx_uint_t i;
1205
1206 gridfs_clndata = data;
1207
1208 for (i = 0; i < gridfs_clndata->numchunks; i++) {
1209 mongo_cursor_destroy(gridfs_clndata->cursors[i]);
1210 }
1211 }
1212