diff --git a/.gitmodules b/.gitmodules
index 59e8a3c..514fbd3 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,3 @@
[submodule "mongo-c-driver"]
path = mongo-c-driver
- url = git://github.com/mongodb/mongo-c-driver.git
+ url = git://github.com/eagleas/mongo-c-driver.git
diff --git a/README.rst b/README.rst
index 4669fc8..d39899a 100644
--- a/README.rst
+++ b/README.rst
@@ -5,6 +5,9 @@ nginx-gridfs
Chris Triolo,
and everyone listed in the Credits section below
+Note from Mike: I'm no longer actively maintaining this project. If you're
+interested in becoming the new maintainer please shoot me an email.
+
About
=====
**nginx-gridfs** is an `Nginx `_ module to serve
diff --git a/config b/config
index c6921f8..3ae61d7 100644
--- a/config
+++ b/config
@@ -1,5 +1,12 @@
ngx_addon_name=ngx_http_gridfs_module
HTTP_MODULES="$HTTP_MODULES ngx_http_gridfs_module"
+
+case "$NGX_PLATFORM" in
+ Linux:*)
+ /bin/cp -f $ngx_addon_dir/mongo-c-driver/src/platform/linux/net.* $ngx_addon_dir/mongo-c-driver/src/
+ ;;
+esac
+
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_gridfs_module.c $ngx_addon_dir/mongo-c-driver/src/*.c"
NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/mongo-c-driver/src/*.h"
-CFLAGS="$CFLAGS --std=c99 -Isrc"
\ No newline at end of file
+CFLAGS="$CFLAGS -Wno-unused-function -Wno-missing-field-initializers -D_POSIX_C_SOURCE=200112L --std=c99 -Isrc"
diff --git a/mongo-c-driver b/mongo-c-driver
index 1380839..4e6bf61 160000
--- a/mongo-c-driver
+++ b/mongo-c-driver
@@ -1 +1 @@
-Subproject commit 13808396bd1b128dfa03ea3ba4d3cdccb9d990c7
+Subproject commit 4e6bf61f16fa40a591b9377555423a2b25738c8f
diff --git a/ngx_http_gridfs_module.c b/ngx_http_gridfs_module.c
index 77ef430..f7dc7bb 100644
--- a/ngx_http_gridfs_module.c
+++ b/ngx_http_gridfs_module.c
@@ -232,7 +232,7 @@ static char* ngx_http_gridfs(ngx_conf_t* cf, ngx_command_t* command, void* void_
/* Parse the parameters */
for (i = 2; i < cf->args->nelts; i++) {
- if (ngx_strncmp(value[i].data, "root_collection=", 16) == 0) {
+ if (ngx_strncmp(value[i].data, "root_collection=", 16) == 0) {
gridfs_loc_conf->root_collection.data = (u_char *) &value[i].data[16];
gridfs_loc_conf->root_collection.len = ngx_strlen(&value[i].data[16]);
continue;
@@ -254,7 +254,7 @@ static char* ngx_http_gridfs(ngx_conf_t* cf, ngx_command_t* command, void* void_
continue;
}
- if (ngx_strncmp(value[i].data, "type=", 5) == 0) {
+ if (ngx_strncmp(value[i].data, "type=", 5) == 0) {
type = (ngx_str_t) ngx_string(&value[i].data[5]);
/* Currently only support for "objectid", "string", and "int" */
@@ -275,7 +275,7 @@ static char* ngx_http_gridfs(ngx_conf_t* cf, ngx_command_t* command, void* void_
continue;
}
- if (ngx_strncmp(value[i].data, "user=", 5) == 0) {
+ if (ngx_strncmp(value[i].data, "user=", 5) == 0) {
gridfs_loc_conf->user.data = (u_char *) &value[i].data[5];
gridfs_loc_conf->user.len = ngx_strlen(&value[i].data[5]);
continue;
@@ -431,14 +431,14 @@ static ngx_int_t ngx_http_mongo_authenticate(ngx_log_t *log, ngx_http_gridfs_loc
// Authenticate
if (gridfs_loc_conf->user.data != NULL && gridfs_loc_conf->pass.data != NULL) {
- if (mongo_cmd_authenticate( &mongo_conn->conn,
- (const char*)gridfs_loc_conf->db.data,
- (const char*)gridfs_loc_conf->user.data,
+ if (mongo_cmd_authenticate( &mongo_conn->conn,
+ (const char*)gridfs_loc_conf->db.data,
+ (const char*)gridfs_loc_conf->user.data,
(const char*)gridfs_loc_conf->pass.data )
!= MONGO_OK) {
ngx_log_error(NGX_LOG_ERR, log, 0,
- "Invalid mongo user/pass: %s/%s",
- gridfs_loc_conf->user.data,
+ "Invalid mongo user/pass: %s/%s",
+ gridfs_loc_conf->user.data,
gridfs_loc_conf->pass.data);
return NGX_ERROR;
}
@@ -490,18 +490,18 @@ static ngx_int_t ngx_http_mongo_add_connection(ngx_cycle_t* cycle, ngx_http_grid
if ( gridfs_loc_conf->mongods->nelts == 1 ) {
ngx_cpystrn( host, mongods[0].host.data, mongods[0].host.len + 1 );
- status = mongo_connect( &mongo_conn->conn, (const char*)host, mongods[0].port );
+ status = mongo_client( &mongo_conn->conn, (const char*)host, mongods[0].port );
} else if ( gridfs_loc_conf->mongods->nelts >= 2 && gridfs_loc_conf->mongods->nelts < 9 ) {
/* Initiate replica set connection. */
- mongo_replset_init( &mongo_conn->conn, (const char *)gridfs_loc_conf->replset.data );
+ mongo_replica_set_init( &mongo_conn->conn, (const char *)gridfs_loc_conf->replset.data );
/* Add replica set seeds. */
for( i=0; imongods->nelts; ++i ) {
ngx_cpystrn( host, mongods[i].host.data, mongods[i].host.len + 1 );
- mongo_replset_add_seed( &mongo_conn->conn, (const char *)host, mongods[i].port );
+ mongo_replica_set_add_seed( &mongo_conn->conn, (const char *)host, mongods[i].port );
}
- status = mongo_replset_connect( &mongo_conn->conn );
+ status = mongo_replica_set_client( &mongo_conn->conn );
} else {
ngx_log_error(NGX_LOG_ERR, cycle->log, 0,
"Mongo Nginx Exception: Too many strings provided in 'mongo' directive.");
@@ -557,10 +557,10 @@ static ngx_int_t ngx_http_gridfs_init_worker(ngx_cycle_t* cycle) {
for (i = 0; i < gridfs_main_conf->loc_confs.nelts; i++) {
if (ngx_http_mongo_add_connection(cycle, gridfs_loc_confs[i]) == NGX_ERROR) {
- return NGX_ERROR;
+ return NGX_OK;
}
if (ngx_http_mongo_authenticate(cycle->log, gridfs_loc_confs[i]) == NGX_ERROR) {
- return NGX_ERROR;
+ return NGX_OK;
}
}
@@ -570,7 +570,7 @@ static ngx_int_t ngx_http_gridfs_init_worker(ngx_cycle_t* cycle) {
static ngx_int_t ngx_http_mongo_reconnect(ngx_log_t *log, ngx_http_mongo_connection_t *mongo_conn) {
volatile int status = MONGO_CONN_FAIL;
- if (&mongo_conn->conn.connected) {
+ if (&mongo_conn->conn.connected) {
mongo_disconnect(&mongo_conn->conn);
ngx_msleep(MONGO_RECONNECT_WAITTIME);
status = mongo_reconnect(&mongo_conn->conn);
@@ -604,7 +604,7 @@ static ngx_int_t ngx_http_mongo_reconnect(ngx_log_t *log, ngx_http_mongo_connect
"Mongo Exception: Unknown Error");
return NGX_ERROR;
}
-
+
return NGX_OK;
}
@@ -615,19 +615,19 @@ static ngx_int_t ngx_http_mongo_reauth(ngx_log_t *log, ngx_http_mongo_connection
auths = mongo_conn->auths->elts;
for (i = 0; i < mongo_conn->auths->nelts; i++) {
- status = mongo_cmd_authenticate( &mongo_conn->conn,
- (const char*)auths[i].db.data,
- (const char*)auths[i].user.data,
+ status = mongo_cmd_authenticate( &mongo_conn->conn,
+ (const char*)auths[i].db.data,
+ (const char*)auths[i].user.data,
(const char*)auths[i].pass.data );
if (status != MONGO_OK) {
ngx_log_error(NGX_LOG_ERR, log, 0,
- "Invalid mongo user/pass: %s/%s, during reauth",
- auths[i].user.data,
- auths[i].pass.data);
+ "Invalid mongo user/pass: %s/%s, during reauth",
+ auths[i].user.data,
+ auths[i].pass.data);
return NGX_ERROR;
}
}
-
+
return NGX_OK;
}
@@ -667,6 +667,136 @@ static int url_decode(char * filename) {
return 1;
}
+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) {
+ u_char *p, *last;
+ off_t start, end;
+ ngx_uint_t bad;
+ enum {
+ sw_start = 0,
+ sw_first_byte_pos,
+ sw_first_byte_pos_n,
+ sw_last_byte_pos,
+ sw_last_byte_pos_n,
+ sw_done
+ } state = 0;
+
+ p = (u_char *) ngx_strnstr(range_str->data, "bytes=", range_str->len);
+
+ if (p == NULL) {
+ return;
+ }
+
+ p += sizeof("bytes=") - 1;
+ last = range_str->data + range_str->len;
+
+ /*
+ * bytes= contain ranges compatible with RFC 2616, "14.35.1 Byte Ranges",
+ * but no whitespaces permitted
+ */
+
+ bad = 0;
+ start = 0;
+ end = 0;
+
+ while (p < last) {
+
+ switch (state) {
+
+ case sw_start:
+ case sw_first_byte_pos:
+ if (*p == '-') {
+ p++;
+ state = sw_last_byte_pos;
+ break;
+ }
+ start = 0;
+ state = sw_first_byte_pos_n;
+
+ /* fall through */
+
+ case sw_first_byte_pos_n:
+ if (*p == '-') {
+ p++;
+ state = sw_last_byte_pos;
+ break;
+ }
+ if (*p < '0' || *p > '9') {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "bytes header filter: unexpected char '%c'"
+ " (expected first-byte-pos)", *p);
+ bad = 1;
+ break;
+ }
+ start = start * 10 + *p - '0';
+ p++;
+ break;
+
+ case sw_last_byte_pos:
+ if (*p == ',' || *p == '&' || *p == ';') {
+ /* no last byte pos, assume end of file */
+ end = content_length - 1;
+ state = sw_done;
+ break;
+ }
+ end = 0;
+ state = sw_last_byte_pos_n;
+
+ /* fall though */
+
+ case sw_last_byte_pos_n:
+ if (*p == ',' || *p == '&' || *p == ';') {
+ state = sw_done;
+ break;
+ }
+ if (*p < '0' || *p > '9') {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "bytes header filter: unexpected char '%c'"
+ " (expected last-byte-pos)", *p);
+ bad = 1;
+ break;
+ }
+ end = end * 10 + *p - '0';
+ p++;
+ break;
+
+ case sw_done:
+ *range_start = start;
+ *range_end = end;
+
+ break;
+ }
+
+ if (bad) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "bytes header filter: invalid range specification");
+ return;
+ }
+ }
+
+ switch (state) {
+
+ case sw_last_byte_pos:
+ end = content_length - 1;
+
+ case sw_last_byte_pos_n:
+ if (start > end) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "bytes header filter: invalid range specification");
+ return;
+ }
+
+ *range_start = start;
+ *range_end = end;
+ break;
+
+ default:
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "bytes header filter: invalid range specification");
+ return;
+
+ }
+}
+
static ngx_int_t ngx_http_gridfs_handler(ngx_http_request_t* request) {
ngx_http_gridfs_loc_conf_t* gridfs_conf;
ngx_http_core_loc_conf_t* core_conf;
@@ -696,8 +826,11 @@ static ngx_int_t ngx_http_gridfs_handler(ngx_http_request_t* request) {
ngx_pool_cleanup_t* gridfs_cln;
ngx_http_gridfs_cleanup_t* gridfs_clndata;
int status;
- volatile ngx_uint_t e = FALSE;
+ volatile ngx_uint_t e = FALSE;
volatile ngx_uint_t ecounter = 0;
+ uint64_t range_start = 0;
+ uint64_t range_end = 0;
+ uint64_t current_buf_pos = 0;
gridfs_conf = ngx_http_get_module_loc_conf(request, ngx_http_gridfs_module);
core_conf = ngx_http_get_module_loc_conf(request, ngx_http_core_module);
@@ -710,14 +843,20 @@ static ngx_int_t ngx_http_gridfs_handler(ngx_http_request_t* request) {
"Mongo Connection not found: \"%V\"", &gridfs_conf->mongo);
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
-
- if ( !(&mongo_conn->conn.connected)
- && (ngx_http_mongo_reconnect(request->connection->log, mongo_conn) == NGX_ERROR
- || ngx_http_mongo_reauth(request->connection->log, mongo_conn) == NGX_ERROR)) {
- ngx_log_error(NGX_LOG_ERR, request->connection->log, 0,
- "Could not connect to mongo: \"%V\"", &gridfs_conf->mongo);
- if(&mongo_conn->conn.connected) { mongo_disconnect(&mongo_conn->conn); }
- return NGX_HTTP_SERVICE_UNAVAILABLE;
+
+ if (mongo_conn->conn.connected == 0) {
+ if (ngx_http_mongo_reconnect(request->connection->log, mongo_conn) == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_ERR, request->connection->log, 0,
+ "Could not connect to mongo: \"%V\"", &gridfs_conf->mongo);
+ if(mongo_conn->conn.connected) { mongo_disconnect(&mongo_conn->conn); }
+ return NGX_HTTP_SERVICE_UNAVAILABLE;
+ }
+ if (ngx_http_mongo_reauth(request->connection->log, mongo_conn) == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_ERR, request->connection->log, 0,
+ "Failed to reauth to mongo: \"%V\"", &gridfs_conf->mongo);
+ if(mongo_conn->conn.connected) { mongo_disconnect(&mongo_conn->conn); }
+ return NGX_HTTP_SERVICE_UNAVAILABLE;
+ }
}
// ---------- RETRIEVE KEY ---------- //
@@ -751,17 +890,18 @@ static ngx_int_t ngx_http_gridfs_handler(ngx_http_request_t* request) {
do {
e = FALSE;
- if (gridfs_init(&mongo_conn->conn,
- (const char*)gridfs_conf->db.data,
- (const char*)gridfs_conf->root_collection.data,
- &gfs) != MONGO_OK) {
+ status = gridfs_init(&mongo_conn->conn,
+ (const char*)gridfs_conf->db.data,
+ (const char*)gridfs_conf->root_collection.data,
+ &gfs);
+ if (status != MONGO_OK) {
e = TRUE; ecounter++;
if (ecounter > MONGO_MAX_RETRIES_PER_REQUEST
|| ngx_http_mongo_reconnect(request->connection->log, mongo_conn) == NGX_ERROR
|| ngx_http_mongo_reauth(request->connection->log, mongo_conn) == NGX_ERROR) {
ngx_log_error(NGX_LOG_ERR, request->connection->log, 0,
"Mongo connection dropped, could not reconnect");
- if(&mongo_conn->conn.connected) { mongo_disconnect(&mongo_conn->conn); }
+ if(mongo_conn->conn.connected) { mongo_disconnect(&mongo_conn->conn); }
free(value);
return NGX_HTTP_SERVICE_UNAVAILABLE;
}
@@ -784,7 +924,7 @@ static ngx_int_t ngx_http_gridfs_handler(ngx_http_request_t* request) {
bson_finish(&query);
status = gridfs_find_query(&gfs, &query, &gfile);
-
+
bson_destroy(&query);
free(value);
@@ -796,15 +936,65 @@ static ngx_int_t ngx_http_gridfs_handler(ngx_http_request_t* request) {
/* Get information about the file */
length = gridfile_get_contentlength(&gfile);
numchunks = gridfile_get_numchunks(&gfile);
+
+ // NaN workaround
+ if (numchunks > INT_MAX)
+ {
+ gridfile_destroy(&gfile);
+ gridfs_destroy(&gfs);
+ return NGX_HTTP_NOT_FOUND;
+ }
+
contenttype = (char*)gridfile_get_contenttype(&gfile);
md5 = (char*)gridfile_get_md5(&gfile);
last_modified = gridfile_get_uploaddate(&gfile);
+ // ---------- Partial Range
+ // set follow-fork-mode child
+ // attach (pid)
+ // break ngx_http_gridfs_module.c:959
+
+ if (request->headers_in.range) {
+ gridfs_parse_range(request, &request->headers_in.range->value, &range_start, &range_end, length);
+ }
+
// ---------- SEND THE HEADERS ---------- //
- request->headers_out.status = NGX_HTTP_OK;
- request->headers_out.content_length_n = length;
+ if (range_start == 0 && range_end == 0) {
+ request->headers_out.status = NGX_HTTP_OK;
+ request->headers_out.content_length_n = length;
+ } else {
+ request->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
+ request->headers_out.content_length_n = length;
+ //request->headers_out.content_range = range_end - range_start + 1;
+
+ ngx_table_elt_t *content_range;
+
+ content_range = ngx_list_push(&request->headers_out.headers);
+ if (content_range == NULL) {
+ return NGX_ERROR;
+ }
+
+ request->headers_out.content_range = content_range;
+
+ content_range->hash = 1;
+ ngx_str_set(&content_range->key, "Content-Range");
+
+ content_range->value.data = ngx_pnalloc(request->pool,sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN);
+ if (content_range->value.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ /* "Content-Range: bytes SSSS-EEEE/TTTT" header */
+ content_range->value.len = ngx_sprintf(content_range->value.data,
+ "bytes %O-%O/%O",
+ range_start, range_end,
+ request->headers_out.content_length_n)
+ - content_range->value.data;
+
+ request->headers_out.content_length_n = range_end - range_start + 1;
+ }
if (contenttype != NULL) {
request->headers_out.content_type.len = strlen(contenttype);
request->headers_out.content_type.data = (u_char*)contenttype;
@@ -818,13 +1008,13 @@ static ngx_int_t ngx_http_gridfs_handler(ngx_http_request_t* request) {
request->headers_out.etag->key.len = sizeof("ETag") - 1;
request->headers_out.etag->key.data = (u_char*)"ETag";
- ngx_buf_t *b;
- b = ngx_create_temp_buf(request->pool, strlen(md5) + 2);
+ ngx_buf_t *b;
+ b = ngx_create_temp_buf(request->pool, strlen(md5) + 2);
b->last = ngx_sprintf(b->last, "\"%s\"", md5);
request->headers_out.etag->value.len = strlen(md5) + 2;
request->headers_out.etag->value.data = b->start;
}
-
+
// use uploadDate field as last_modified if possible
if (last_modified) {
request->headers_out.last_modified_time = (time_t)(last_modified/1000);
@@ -874,14 +1064,14 @@ static ngx_int_t ngx_http_gridfs_handler(ngx_http_request_t* request) {
return ngx_http_output_filter(request, &out);
}
-
+
cursors = (mongo_cursor **)ngx_pcalloc(request->pool, sizeof(mongo_cursor *) * numchunks);
if (cursors == NULL) {
gridfile_destroy(&gfile);
gridfs_destroy(&gfs);
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
-
+
ngx_memzero( cursors, sizeof(mongo_cursor *) * numchunks);
/* Hook in the cleanup function */
@@ -915,12 +1105,12 @@ static ngx_int_t ngx_http_gridfs_handler(ngx_http_request_t* request) {
cursors[i] = gridfile_get_chunks(&gfile, i, 1);
if (!(cursors[i] && mongo_cursor_next(cursors[i]) == MONGO_OK)) {
e = TRUE; ecounter++;
- if (ecounter > MONGO_MAX_RETRIES_PER_REQUEST
+ if (ecounter > MONGO_MAX_RETRIES_PER_REQUEST
|| ngx_http_mongo_reconnect(request->connection->log, mongo_conn) == NGX_ERROR
|| ngx_http_mongo_reauth(request->connection->log, mongo_conn) == NGX_ERROR) {
ngx_log_error(NGX_LOG_ERR, request->connection->log, 0,
"Mongo connection dropped, could not reconnect");
- if(&mongo_conn->conn.connected) { mongo_disconnect(&mongo_conn->conn); }
+ if(mongo_conn->conn.connected) { mongo_disconnect(&mongo_conn->conn); }
gridfile_destroy(&gfile);
gridfs_destroy(&gfs);
return NGX_HTTP_SERVICE_UNAVAILABLE;
@@ -930,19 +1120,58 @@ static ngx_int_t ngx_http_gridfs_handler(ngx_http_request_t* request) {
chunk = cursors[i]->current;
bson_find(&it, &chunk, "data");
- chunk_len = bson_iterator_bin_len( &it );
+ chunk_len = bson_iterator_bin_len( &it ); // break ngx_http_gridfs_module.c:1099
chunk_data = bson_iterator_bin_data( &it );
- /* Set up the buffer chain */
- buffer->pos = (u_char*)chunk_data;
- buffer->last = (u_char*)chunk_data + chunk_len;
- buffer->memory = 1;
- buffer->last_buf = (i == numchunks-1);
- out.buf = buffer;
- out.next = NULL;
+ if (range_start == 0 && range_end == 0) {
+ /* <> */
+ /* Set up the buffer chain */
+ buffer->pos = (u_char*)chunk_data;
+ buffer->last = (u_char*)chunk_data + chunk_len;
+ buffer->memory = 1;
+ buffer->last_buf = (i == numchunks-1);
+ out.buf = buffer;
+ out.next = NULL;
+
+ /* Serve the Chunk */
+ rc = ngx_http_output_filter(request, &out);
+ } else {
+ /* <> */
+ if ( range_start >= (current_buf_pos+chunk_len) ||
+ range_end <= current_buf_pos) {
+ /* no output */
+ ngx_pfree(request->pool, buffer);
+ } else {
+ if (range_start <= current_buf_pos) {
+ buffer->pos = (u_char*)chunk_data;
+ } else {
+ buffer->pos = (u_char*)chunk_data + (range_start - current_buf_pos);
+ }
+ if (range_end < (current_buf_pos+chunk_len)) {
+ buffer->last = (u_char*)chunk_data + (range_end - current_buf_pos + 1);
+ } else {
+ buffer->last = (u_char*)chunk_data + chunk_len;
+ }
+ if (buffer->pos == buffer->last) {
+ ngx_log_error(NGX_LOG_ALERT, request->connection->log, 0,
+ "zero size buf in writer "
+ "range_start:%d range_end:%d "
+ "current_buf_pos:%d chunk_len:%d i:%d numchunk:%d",
+ range_start,range_end,
+ current_buf_pos, chunk_len,
+ i,numchunks);
+ }
+ buffer->memory = 1;
+ buffer->last_buf = (i == numchunks-1) || (range_end < (current_buf_pos+chunk_len));
+ out.buf = buffer;
+ out.next = NULL;
+
+ /* Serve the Chunk */
+ rc = ngx_http_output_filter(request, &out);
+ }
+ }
- /* Serve the Chunk */
- rc = ngx_http_output_filter(request, &out);
+ current_buf_pos += chunk_len;
/* TODO: More Codes to Catch? */
if (rc == NGX_ERROR) {