std::string collection::insert(nlohmann::json::object_t const& document) throw(std::runtime_error) { bson_oid_t oid; std::shared_ptr<bson> bson_doc = convert_to_bson(document); if(document.find("_id") != document.end()) { std::string const &id = document.find("_id")->second; if(!ejdbisvalidoidstr(id.c_str())) { throw ejdb_exception(JBEINVALIDBSONPK); } bson_oid_from_string(&oid, id.c_str()); std::shared_ptr<bson> bson_oid(bson_create(), bson_del); bson_init(bson_oid.get()); bson_append_oid(bson_oid.get(), "_id", &oid); bson_finish(bson_oid.get()); std::shared_ptr<bson> bson_doc_with_oid(bson_create(), bson_del); bson_init(bson_doc_with_oid.get()); bson_merge(bson_oid.get(), bson_doc.get(), true, bson_doc_with_oid.get()); bson_finish(bson_doc_with_oid.get()); bson_doc.swap(bson_doc_with_oid); } if(!ejdbsavebson(_coll.get(), bson_doc.get(), &oid)) { throw_last_ejdb_exception(); } std::string id(24, '\0'); bson_oid_to_string(&oid, &id[0]); document_added(id, document); return id; }
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; ngx_buf_t* buffer; ngx_chain_t out; ngx_str_t location_name; ngx_str_t full_uri; char* value; ngx_http_mongo_connection_t *mongo_conn; gridfs gfs; gridfile gfile; gridfs_offset length; ngx_uint_t numchunks; char* contenttype; char* md5; bson_date_t last_modified; volatile ngx_uint_t i; ngx_int_t rc = NGX_OK; bson query; bson_oid_t oid; mongo_cursor ** cursors; gridfs_offset chunk_len; const char * chunk_data; bson_iterator it; bson chunk; 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 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); // ---------- ENSURE MONGO CONNECTION ---------- // mongo_conn = ngx_http_get_mongo_connection( gridfs_conf->mongo ); if (mongo_conn == NULL) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Mongo Connection not found: \"%V\"", &gridfs_conf->mongo); return NGX_HTTP_INTERNAL_SERVER_ERROR; } 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 ---------- // location_name = core_conf->name; full_uri = request->uri; if (full_uri.len < location_name.len) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Invalid location name or uri."); return NGX_HTTP_INTERNAL_SERVER_ERROR; } value = (char*)malloc(sizeof(char) * (full_uri.len - location_name.len + 1)); if (value == NULL) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Failed to allocate memory for value buffer."); return NGX_HTTP_INTERNAL_SERVER_ERROR; } memcpy(value, full_uri.data + location_name.len, full_uri.len - location_name.len); value[full_uri.len - location_name.len] = '\0'; if (!url_decode(value)) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Malformed request."); free(value); return NGX_HTTP_BAD_REQUEST; } // ---------- RETRIEVE GRIDFILE ---------- // bson_init(&query); switch (gridfs_conf->type) { case BSON_OID: bson_oid_from_string(&oid, value); bson_append_oid(&query, (char*)gridfs_conf->field.data, &oid); break; case BSON_INT: bson_append_int(&query, (char*)gridfs_conf->field.data, ngx_atoi((u_char*)value, strlen(value))); break; case BSON_STRING: bson_append_string(&query, (char*)gridfs_conf->field.data, value); break; } bson_finish(&query); 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_find_query(&gfs, &query, &gfile) == MONGO_ERROR)) { 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); } bson_destroy(&query); free(value); return NGX_HTTP_SERVICE_UNAVAILABLE; } } } while (e); bson_destroy(&query); free(value); /* 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 ---------- // 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; } else ngx_http_set_content_type(request); // use md5 field as ETag if possible if (md5 != NULL) { request->headers_out.etag = ngx_list_push(&request->headers_out.headers); request->headers_out.etag->hash = 1; 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); 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); } /* Determine if content is gzipped, set headers accordingly */ if ( gridfile_get_boolean(&gfile,"gzipped") ) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, gridfile_get_field(&gfile,"gzipped") ); request->headers_out.content_encoding = ngx_list_push(&request->headers_out.headers); if (request->headers_out.content_encoding == NULL) { gridfile_destroy(&gfile); gridfs_destroy(&gfs); return NGX_ERROR; } request->headers_out.content_encoding->hash = 1; request->headers_out.content_encoding->key.len = sizeof("Content-Encoding") - 1; request->headers_out.content_encoding->key.data = (u_char *) "Content-Encoding"; request->headers_out.content_encoding->value.len = sizeof("gzip") - 1; request->headers_out.content_encoding->value.data = (u_char *) "gzip"; } ngx_http_send_header(request); // ---------- SEND THE BODY ---------- // /* Empty file */ if (numchunks == 0) { /* Allocate space for the response buffer */ buffer = ngx_pcalloc(request->pool, sizeof(ngx_buf_t)); if (buffer == NULL) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Failed to allocate response buffer"); gridfile_destroy(&gfile); gridfs_destroy(&gfs); return NGX_HTTP_INTERNAL_SERVER_ERROR; } buffer->pos = NULL; buffer->last = NULL; buffer->memory = 1; buffer->last_buf = 1; out.buf = buffer; out.next = NULL; gridfile_destroy(&gfile); gridfs_destroy(&gfs); 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 */ gridfs_cln = ngx_pool_cleanup_add(request->pool, sizeof(ngx_http_gridfs_cleanup_t)); if (gridfs_cln == NULL) { gridfile_destroy(&gfile); gridfs_destroy(&gfs); return NGX_HTTP_INTERNAL_SERVER_ERROR; } gridfs_cln->handler = ngx_http_gridfs_cleanup; gridfs_clndata = gridfs_cln->data; gridfs_clndata->cursors = cursors; gridfs_clndata->numchunks = numchunks; /* Read and serve chunk by chunk */ for (i = 0; i < numchunks; i++) { /* Allocate space for the response buffer */ buffer = ngx_pcalloc(request->pool, sizeof(ngx_buf_t)); if (buffer == NULL) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Failed to allocate response buffer"); gridfile_destroy(&gfile); gridfs_destroy(&gfs); return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* Fetch the chunk from mongo */ do { e = FALSE; 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 || 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); } gridfile_destroy(&gfile); gridfs_destroy(&gfs); return NGX_HTTP_SERVICE_UNAVAILABLE; } } } while (e); chunk = cursors[i]->current; bson_find(&it, &chunk, "data"); chunk_len = bson_iterator_bin_len( &it ); // break ngx_http_gridfs_module.c:1099 chunk_data = bson_iterator_bin_data( &it ); if (range_start == 0 && range_end == 0) { /* <<no range request>> */ /* 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 { /* <<range request>> */ 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); } } current_buf_pos += chunk_len; /* TODO: More Codes to Catch? */ if (rc == NGX_ERROR) { gridfile_destroy(&gfile); gridfs_destroy(&gfs); return NGX_ERROR; } } gridfile_destroy(&gfile); gridfs_destroy(&gfs); return rc; }
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; ngx_buf_t* buffer; ngx_chain_t out; ngx_str_t location_name; ngx_str_t full_uri; // --------------- xulin add start ------------------- char* ml_args; char* arg; unsigned int add_arg; unsigned int add_len; // --------------- xulin add end ------------------- char* value; ngx_http_mongo_connection_t *mongo_conn; gridfs gfs; gridfile gfile; gridfs_offset length; ngx_uint_t numchunks; char* contenttype; char* md5; bson_date_t last_modified; volatile ngx_uint_t i; ngx_int_t rc = NGX_OK; bson query; bson_oid_t oid; mongo_cursor ** cursors; gridfs_offset chunk_len; const char * chunk_data; bson_iterator it; bson chunk; 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 ecounter = 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); // ---------- ENSURE MONGO CONNECTION ---------- // mongo_conn = ngx_http_get_mongo_connection( gridfs_conf->mongo ); if (mongo_conn == NULL) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "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; } // ---------- RETRIEVE KEY ---------- // location_name = core_conf->name; full_uri = request->uri; if (full_uri.len < location_name.len) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Invalid location name or uri."); return NGX_HTTP_INTERNAL_SERVER_ERROR; } value = (char*)malloc(sizeof(char) * (full_uri.len - location_name.len + 1 + request->args.len + 1)); if (value == NULL) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Failed to allocate memory for value buffer."); return NGX_HTTP_INTERNAL_SERVER_ERROR; } memcpy(value, full_uri.data + location_name.len, full_uri.len - location_name.len); // ------------------------------------ xulin add start -------------------------------------- if (request->args.len > 0) { ml_args = (char*)malloc(sizeof(char) * (request->args.len + 1)); memcpy(ml_args, request->args.data, request->args.len); ml_args[request->args.len] = '\0'; add_len = full_uri.len - location_name.len; memcpy(value + add_len, "?", 1); add_len += 1; arg = strtok(ml_args, "&"); while (arg != NULL) { add_arg = 1; if (strstr(arg, "xc_md5") != NULL) { add_arg = 0; } else if (strstr(arg, "_xingcloud_t") != NULL) { add_arg = 0; } if (add_arg == 1) { memcpy(value + add_len, arg, strlen(arg)); add_len += strlen(arg); memcpy(value + add_len, "&", 1); add_len += 1; } arg = strtok(NULL, "&"); } free(ml_args); if (value[add_len - 1] == '?' || value[add_len - 1] == '&') { value[add_len - 1] = '\0'; } else { value[add_len] = '\0'; } } ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "ml_url = [%s]", value); // ------------------------------------ xulin add end -------------------------------------- if (!url_decode(value)) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Malformed request."); free(value); return NGX_HTTP_BAD_REQUEST; } // ---------- RETRIEVE GRIDFILE ---------- // 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) { 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); } free(value); return NGX_HTTP_SERVICE_UNAVAILABLE; } } } while (e); bson_init(&query); switch (gridfs_conf->type) { case BSON_OID: bson_oid_from_string(&oid, value); bson_append_oid(&query, (char*)gridfs_conf->field.data, &oid); break; case BSON_INT: bson_append_int(&query, (char*)gridfs_conf->field.data, ngx_atoi((u_char*)value, strlen(value))); break; case BSON_STRING: bson_append_string(&query, (char*)gridfs_conf->field.data, value); break; } bson_finish(&query); status = gridfs_find_query(&gfs, &query, &gfile); bson_destroy(&query); free(value); if(status == MONGO_ERROR) { gridfs_destroy(&gfs); return NGX_HTTP_NOT_FOUND; } /* Get information about the file */ length = gridfile_get_contentlength(&gfile); numchunks = gridfile_get_numchunks(&gfile); contenttype = (char*)gridfile_get_contenttype(&gfile); md5 = (char*)gridfile_get_md5(&gfile); last_modified = gridfile_get_uploaddate(&gfile); // ---------- SEND THE HEADERS ---------- // request->headers_out.status = NGX_HTTP_OK; request->headers_out.content_length_n = length; if (contenttype != NULL) { request->headers_out.content_type.len = strlen(contenttype); request->headers_out.content_type.data = (u_char*)contenttype; } else ngx_http_set_content_type(request); // use md5 field as ETag if possible if (md5 != NULL) { request->headers_out.etag = ngx_list_push(&request->headers_out.headers); request->headers_out.etag->hash = 1; 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); 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); } /* Determine if content is gzipped, set headers accordingly */ if ( gridfile_get_boolean(&gfile,"gzipped") ) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, gridfile_get_field(&gfile,"gzipped") ); request->headers_out.content_encoding = ngx_list_push(&request->headers_out.headers); if (request->headers_out.content_encoding == NULL) { gridfile_destroy(&gfile); gridfs_destroy(&gfs); return NGX_ERROR; } request->headers_out.content_encoding->hash = 1; request->headers_out.content_encoding->key.len = sizeof("Content-Encoding") - 1; request->headers_out.content_encoding->key.data = (u_char *) "Content-Encoding"; request->headers_out.content_encoding->value.len = sizeof("gzip") - 1; request->headers_out.content_encoding->value.data = (u_char *) "gzip"; } ngx_http_send_header(request); // ---------- SEND THE BODY ---------- // /* Empty file */ if (numchunks == 0) { /* Allocate space for the response buffer */ buffer = ngx_pcalloc(request->pool, sizeof(ngx_buf_t)); if (buffer == NULL) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Failed to allocate response buffer"); gridfile_destroy(&gfile); gridfs_destroy(&gfs); return NGX_HTTP_INTERNAL_SERVER_ERROR; } buffer->pos = NULL; buffer->last = NULL; buffer->memory = 1; buffer->last_buf = 1; out.buf = buffer; out.next = NULL; gridfile_destroy(&gfile); gridfs_destroy(&gfs); 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 */ gridfs_cln = ngx_pool_cleanup_add(request->pool, sizeof(ngx_http_gridfs_cleanup_t)); if (gridfs_cln == NULL) { gridfile_destroy(&gfile); gridfs_destroy(&gfs); return NGX_HTTP_INTERNAL_SERVER_ERROR; } gridfs_cln->handler = ngx_http_gridfs_cleanup; gridfs_clndata = gridfs_cln->data; gridfs_clndata->cursors = cursors; gridfs_clndata->numchunks = numchunks; /* Read and serve chunk by chunk */ for (i = 0; i < numchunks; i++) { /* Allocate space for the response buffer */ buffer = ngx_pcalloc(request->pool, sizeof(ngx_buf_t)); if (buffer == NULL) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Failed to allocate response buffer"); gridfile_destroy(&gfile); gridfs_destroy(&gfs); return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* Fetch the chunk from mongo */ do { e = FALSE; 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 || 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); } gridfile_destroy(&gfile); gridfs_destroy(&gfs); return NGX_HTTP_SERVICE_UNAVAILABLE; } } } while (e); chunk = cursors[i]->current; bson_find(&it, &chunk, "data"); chunk_len = bson_iterator_bin_len( &it ); 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; /* Serve the Chunk */ rc = ngx_http_output_filter(request, &out); /* TODO: More Codes to Catch? */ if (rc == NGX_ERROR) { gridfile_destroy(&gfile); gridfs_destroy(&gfs); return NGX_ERROR; } } gridfile_destroy(&gfile); gridfs_destroy(&gfs); return rc; }
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; ngx_buf_t* buffer; ngx_chain_t out; ngx_str_t location_name; ngx_str_t full_uri; char* value; gridfs gfs; gridfile gfile; gridfs_offset length; char* data; ngx_uint_t chunksize; ngx_uint_t numchunks; ngx_uint_t chunklength; char* contenttype; ngx_uint_t i; ngx_int_t rc = NGX_OK; bson query; bson_buffer buf; bson_oid_t oid; 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); location_name = core_conf->name; full_uri = request->uri; /* defensive */ if (full_uri.len < location_name.len) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Invalid location name or uri."); return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* Extract the value from the uri */ value = (char*)malloc(sizeof(char) * (full_uri.len - location_name.len + 1)); if (value == NULL) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Failed to allocate memory for value buffer."); return NGX_HTTP_INTERNAL_SERVER_ERROR; } memcpy(value, full_uri.data + location_name.len, full_uri.len - location_name.len); value[full_uri.len - location_name.len] = '\0'; /* URL Decoding */ if (!url_decode(value)) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Malformed request."); free(value); return NGX_HTTP_BAD_REQUEST; } /* If no mongo connection, create a default connection */ /* TODO: Find a better place for this logic */ if (!gridfs_conf->gridfs_conn->connected) { switch (mongo_connect(gridfs_conf->gridfs_conn, NULL)) { case mongo_conn_success: break; case mongo_conn_bad_arg: ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Mongo Exception: Bad Arguments"); return NGX_HTTP_INTERNAL_SERVER_ERROR; case mongo_conn_no_socket: ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Mongo Exception: No Socket"); return NGX_HTTP_INTERNAL_SERVER_ERROR; case mongo_conn_fail: ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Mongo Exception: Connection Failure"); return NGX_HTTP_INTERNAL_SERVER_ERROR; case mongo_conn_not_master: ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Mongo Exception: Not Master"); return NGX_HTTP_INTERNAL_SERVER_ERROR; default: ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Mongo Exception: Unknown Error"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } } /* Find the GridFile */ gridfs_init(gridfs_conf->gridfs_conn, (const char*)gridfs_conf->gridfs_db.data, (const char*)gridfs_conf->gridfs_root_collection.data, &gfs); bson_buffer_init(&buf); switch (gridfs_conf->gridfs_type) { case bson_oid: bson_oid_from_string(&oid, value); bson_append_oid(&buf, (char*)gridfs_conf->gridfs_field.data, &oid); break; case bson_int: bson_append_int(&buf, (char*)gridfs_conf->gridfs_field.data, atoi(value)); break; case bson_string: bson_append_string(&buf, (char*)gridfs_conf->gridfs_field.data, value); break; } bson_from_buffer(&query, &buf); if(!gridfs_find_query(&gfs, &query, &gfile)){ bson_destroy(&query); free(value); return NGX_HTTP_NOT_FOUND; } bson_destroy(&query); free(value); /* Get information about the file */ length = gridfile_get_contentlength(&gfile); chunksize = gridfile_get_chunksize(&gfile); numchunks = gridfile_get_numchunks(&gfile); contenttype = (char*)gridfile_get_contenttype(&gfile); /* Set the headers */ request->headers_out.status = NGX_HTTP_OK; request->headers_out.content_length_n = length; if (contenttype != NULL) { request->headers_out.content_type.len = strlen(contenttype); request->headers_out.content_type.data = (u_char*)contenttype; } else ngx_http_set_content_type(request); ngx_http_send_header(request); /* Read and serve chunk by chunk */ for (i = 0; i < numchunks; i++) { /* Allocate space for the response buffer */ buffer = ngx_pcalloc(request->pool, sizeof(ngx_buf_t)); if (buffer == NULL) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Failed to allocate response buffer"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* Allocate space for the buffer of data */ data = ngx_pcalloc(request->pool, sizeof(char)*chunksize); if (data == NULL) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Failed to allocate buffer for data"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* Set up the buffer chain */ chunklength = gridfile_read(&gfile, chunksize, data); buffer->pos = (u_char*)data; buffer->last = (u_char*)data + chunklength; 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); /* TODO: More Codes to Catch? */ if (rc == NGX_ERROR) { return NGX_ERROR; } } return rc; }
int test_bson_generic( void ) { bson_iterator it, it2, it3; bson_oid_t oid; bson_timestamp_t ts; bson_timestamp_t ts_result; bson b[1]; bson copy[1]; bson scope[1]; ts.i = 1; ts.t = 2; bson_init( b ); bson_append_double( b, "d", 3.14 ); bson_append_string( b, "s", "hello" ); bson_append_string_n( b, "s_n", "goodbye cruel world", 7 ); { bson_append_start_object( b, "o" ); bson_append_start_array( b, "a" ); bson_append_binary( b, "0", 8, "w\0rld", 5 ); bson_append_finish_object( b ); bson_append_finish_object( b ); } bson_append_undefined( b, "u" ); bson_oid_from_string( &oid, "010203040506070809101112" ); ASSERT( !memcmp( oid.bytes, "\x001\x002\x003\x004\x005\x006\x007\x008\x009\x010\x011\x012", 12 ) ); bson_append_oid( b, "oid", &oid ); bson_append_bool( b, "b", 1 ); bson_append_date( b, "date", 0x0102030405060708 ); bson_append_null( b, "n" ); bson_append_regex( b, "r", "^asdf", "imx" ); /* no dbref test (deprecated) */ bson_append_code( b, "c", "function(){}" ); bson_append_code_n( b, "c_n", "function(){}garbage", 12 ); bson_append_symbol( b, "symbol", "symbol" ); bson_append_symbol_n( b, "symbol_n", "symbol and garbage", 6 ); { bson_init( scope ); bson_append_int( scope, "i", 123 ); bson_finish( scope ); bson_append_code_w_scope( b, "cws", "function(){return i}", scope ); bson_destroy( scope ); } bson_append_timestamp( b, "timestamp", &ts ); bson_append_long( b, "l", 0x1122334455667788 ); /* Ensure that we can't copy a non-finished object. */ ASSERT( bson_copy( copy, b ) == BSON_ERROR ); bson_finish( b ); ASSERT( b->err == BSON_VALID ); /* Test append after finish. */ ASSERT( bson_append_string( b, "foo", "bar" ) == BSON_ERROR ); ASSERT( b->err & BSON_ALREADY_FINISHED ); ASSERT( bson_copy( copy, b ) == BSON_OK ); ASSERT( 1 == copy->finished ); ASSERT( 0 == copy->err ); bson_destroy( copy ); bson_print( b ); bson_iterator_init( &it, b ); ASSERT( bson_iterator_more( &it ) ); ASSERT( bson_iterator_next( &it ) == BSON_DOUBLE ); ASSERT( bson_iterator_type( &it ) == BSON_DOUBLE ); ASSERT( !strcmp( bson_iterator_key( &it ), "d" ) ); ASSERT( bson_iterator_double( &it ) == 3.14 ); ASSERT( bson_iterator_more( &it ) ); ASSERT( bson_iterator_next( &it ) == BSON_STRING ); ASSERT( bson_iterator_type( &it ) == BSON_STRING ); ASSERT( !strcmp( bson_iterator_key( &it ), "s" ) ); ASSERT( !strcmp( bson_iterator_string( &it ), "hello" ) ); ASSERT( strcmp( bson_iterator_string( &it ), "" ) ); ASSERT( bson_iterator_more( &it ) ); ASSERT( bson_iterator_next( &it ) == BSON_STRING ); ASSERT( bson_iterator_type( &it ) == BSON_STRING ); ASSERT( !strcmp( bson_iterator_key( &it ), "s_n" ) ); ASSERT( !strcmp( bson_iterator_string( &it ), "goodbye" ) ); ASSERT( bson_iterator_more( &it ) ); ASSERT( bson_iterator_next( &it ) == BSON_OBJECT ); ASSERT( bson_iterator_type( &it ) == BSON_OBJECT ); ASSERT( !strcmp( bson_iterator_key( &it ), "o" ) ); bson_iterator_subiterator( &it, &it2 ); ASSERT( bson_iterator_more( &it2 ) ); ASSERT( bson_iterator_next( &it2 ) == BSON_ARRAY ); ASSERT( bson_iterator_type( &it2 ) == BSON_ARRAY ); ASSERT( !strcmp( bson_iterator_key( &it2 ), "a" ) ); bson_iterator_subiterator( &it2, &it3 ); ASSERT( bson_iterator_more( &it3 ) ); ASSERT( bson_iterator_next( &it3 ) == BSON_BINDATA ); ASSERT( bson_iterator_type( &it3 ) == BSON_BINDATA ); ASSERT( !strcmp( bson_iterator_key( &it3 ), "0" ) ); ASSERT( bson_iterator_bin_type( &it3 ) == 8 ); ASSERT( bson_iterator_bin_len( &it3 ) == 5 ); ASSERT( !memcmp( bson_iterator_bin_data( &it3 ), "w\0rld", 5 ) ); ASSERT( bson_iterator_more( &it3 ) ); ASSERT( bson_iterator_next( &it3 ) == BSON_EOO ); ASSERT( bson_iterator_type( &it3 ) == BSON_EOO ); ASSERT( !bson_iterator_more( &it3 ) ); ASSERT( bson_iterator_more( &it2 ) ); ASSERT( bson_iterator_next( &it2 ) == BSON_EOO ); ASSERT( bson_iterator_type( &it2 ) == BSON_EOO ); ASSERT( !bson_iterator_more( &it2 ) ); ASSERT( bson_iterator_more( &it ) ); ASSERT( bson_iterator_next( &it ) == BSON_UNDEFINED ); ASSERT( bson_iterator_type( &it ) == BSON_UNDEFINED ); ASSERT( !strcmp( bson_iterator_key( &it ), "u" ) ); ASSERT( bson_iterator_more( &it ) ); ASSERT( bson_iterator_next( &it ) == BSON_OID ); ASSERT( bson_iterator_type( &it ) == BSON_OID ); ASSERT( !strcmp( bson_iterator_key( &it ), "oid" ) ); ASSERT( !memcmp( bson_iterator_oid( &it )->bytes, "\x001\x002\x003\x004\x005\x006\x007\x008\x009\x010\x011\x012", 12 ) ); ASSERT( bson_iterator_oid( &it )->ints[0] == oid.ints[0] ); ASSERT( bson_iterator_oid( &it )->ints[1] == oid.ints[1] ); ASSERT( bson_iterator_oid( &it )->ints[2] == oid.ints[2] ); ASSERT( bson_iterator_more( &it ) ); ASSERT( bson_iterator_next( &it ) == BSON_BOOL ); ASSERT( bson_iterator_type( &it ) == BSON_BOOL ); ASSERT( !strcmp( bson_iterator_key( &it ), "b" ) ); ASSERT( bson_iterator_bool( &it ) == 1 ); ASSERT( bson_iterator_more( &it ) ); ASSERT( bson_iterator_next( &it ) == BSON_DATE ); ASSERT( bson_iterator_type( &it ) == BSON_DATE ); ASSERT( !strcmp( bson_iterator_key( &it ), "date" ) ); ASSERT( bson_iterator_date( &it ) == 0x0102030405060708 ); ASSERT( bson_iterator_more( &it ) ); ASSERT( bson_iterator_next( &it ) == BSON_NULL ); ASSERT( bson_iterator_type( &it ) == BSON_NULL ); ASSERT( !strcmp( bson_iterator_key( &it ), "n" ) ); ASSERT( bson_iterator_more( &it ) ); ASSERT( bson_iterator_next( &it ) == BSON_REGEX ); ASSERT( bson_iterator_type( &it ) == BSON_REGEX ); ASSERT( !strcmp( bson_iterator_key( &it ), "r" ) ); ASSERT( !strcmp( bson_iterator_regex( &it ), "^asdf" ) ); ASSERT( !strcmp( bson_iterator_regex_opts( &it ), "imx" ) ); ASSERT( bson_iterator_more( &it ) ); ASSERT( bson_iterator_next( &it ) == BSON_CODE ); ASSERT( bson_iterator_type( &it ) == BSON_CODE ); ASSERT( !strcmp( bson_iterator_code(&it), "function(){}") ); ASSERT( !strcmp( bson_iterator_key( &it ), "c" ) ); ASSERT( !strcmp( bson_iterator_string( &it ), "" ) ); ASSERT( bson_iterator_more( &it ) ); ASSERT( bson_iterator_next( &it ) == BSON_CODE ); ASSERT( bson_iterator_type( &it ) == BSON_CODE ); ASSERT( !strcmp( bson_iterator_key( &it ), "c_n" ) ); ASSERT( !strcmp( bson_iterator_string( &it ), "" ) ); ASSERT( !strcmp( bson_iterator_code( &it ), "function(){}" ) ); ASSERT( bson_iterator_more( &it ) ); ASSERT( bson_iterator_next( &it ) == BSON_SYMBOL ); ASSERT( bson_iterator_type( &it ) == BSON_SYMBOL ); ASSERT( !strcmp( bson_iterator_key( &it ), "symbol" ) ); ASSERT( !strcmp( bson_iterator_string( &it ), "symbol" ) ); ASSERT( bson_iterator_more( &it ) ); ASSERT( bson_iterator_next( &it ) == BSON_SYMBOL ); ASSERT( bson_iterator_type( &it ) == BSON_SYMBOL ); ASSERT( !strcmp( bson_iterator_key( &it ), "symbol_n" ) ); ASSERT( !strcmp( bson_iterator_string( &it ), "symbol" ) ); ASSERT( bson_iterator_more( &it ) ); ASSERT( bson_iterator_next( &it ) == BSON_CODEWSCOPE ); ASSERT( bson_iterator_type( &it ) == BSON_CODEWSCOPE ); ASSERT( !strcmp( bson_iterator_key( &it ), "cws" ) ); ASSERT( !strcmp( bson_iterator_code( &it ), "function(){return i}" ) ); { bson scope; bson_iterator_code_scope( &it, &scope ); bson_iterator_init( &it2, &scope ); ASSERT( bson_iterator_more( &it2 ) ); ASSERT( bson_iterator_next( &it2 ) == BSON_INT ); ASSERT( bson_iterator_type( &it2 ) == BSON_INT ); ASSERT( !strcmp( bson_iterator_key( &it2 ), "i" ) ); ASSERT( bson_iterator_int( &it2 ) == 123 ); ASSERT( bson_iterator_more( &it2 ) ); ASSERT( bson_iterator_next( &it2 ) == BSON_EOO ); ASSERT( bson_iterator_type( &it2 ) == BSON_EOO ); ASSERT( !bson_iterator_more( &it2 ) ); } ASSERT( bson_iterator_more( &it ) ); ASSERT( bson_iterator_next( &it ) == BSON_TIMESTAMP ); ASSERT( bson_iterator_type( &it ) == BSON_TIMESTAMP ); ASSERT( !strcmp( bson_iterator_key( &it ), "timestamp" ) ); ts_result = bson_iterator_timestamp( &it ); ASSERT( ts_result.i == 1 ); ASSERT( ts_result.t == 2 ); ASSERT( bson_iterator_more( &it ) ); ASSERT( bson_iterator_next( &it ) == BSON_LONG ); ASSERT( bson_iterator_type( &it ) == BSON_LONG ); ASSERT( !strcmp( bson_iterator_key( &it ), "l" ) ); ASSERT( bson_iterator_long( &it ) == 0x1122334455667788 ); ASSERT( bson_iterator_more( &it ) ); ASSERT( bson_iterator_next( &it ) == BSON_EOO ); ASSERT( bson_iterator_type( &it ) == BSON_EOO ); ASSERT( !bson_iterator_more( &it ) ); bson_destroy( b ); { bson bsrc[1]; bson_init( bsrc ); bson_append_double( bsrc, "d", 3.14 ); bson_finish( bsrc ); ASSERT( bsrc->err == BSON_VALID ); bson_init( b ); bson_append_double( b, "", 3.14 ); /* test empty name (in general) */ bson_iterator_init( &it, bsrc ); ASSERT( bson_iterator_more( &it ) ); ASSERT( bson_iterator_next( &it ) == BSON_DOUBLE ); ASSERT( bson_iterator_type( &it ) == BSON_DOUBLE ); bson_append_element( b, "d", &it ); bson_append_element( b, 0, &it ); /* test null */ bson_append_element( b, "", &it ); /* test empty name */ bson_finish( b ); ASSERT( b->err == BSON_VALID ); /* bson_print( b ); */ bson_iterator_init( &it, b ); ASSERT( bson_iterator_more( &it ) ); ASSERT( bson_iterator_next( &it ) == BSON_DOUBLE ); ASSERT( !strcmp( bson_iterator_key( &it ), "" ) ); ASSERT( bson_iterator_double( &it ) == 3.14 ); ASSERT( bson_iterator_more( &it ) ); ASSERT( bson_iterator_next( &it ) == BSON_DOUBLE ); ASSERT( !strcmp( bson_iterator_key( &it ), "d" ) ); ASSERT( bson_iterator_double( &it ) == 3.14 ); ASSERT( bson_iterator_more( &it ) ); ASSERT( bson_iterator_next( &it ) == BSON_DOUBLE ); ASSERT( !strcmp( bson_iterator_key( &it ), "d" ) ); ASSERT( bson_iterator_double( &it ) == 3.14 ); ASSERT( bson_iterator_more( &it ) ); ASSERT( bson_iterator_next( &it ) == BSON_DOUBLE ); ASSERT( !strcmp( bson_iterator_key( &it ), "" ) ); ASSERT( bson_iterator_double( &it ) == 3.14 ); ASSERT( bson_iterator_more( &it ) ); ASSERT( bson_iterator_next( &it ) == BSON_EOO ); ASSERT( !bson_iterator_more( &it ) ); bson_destroy( bsrc ); bson_destroy( b ); } return 0; }
INLINE void v_to_bson P3(bson *, out, char *, k, svalue_t *, v) { switch(v->type) { case T_STRING: { if(!strcmp(k,"_id") && strlen(v->u.string) == 24){ bson_oid_t oid[1]; bson_oid_from_string(oid,v->u.string); bson_append_oid(out,k,oid); }else{ bson_append_string(out,k,v->u.string); } return; } case T_CLASS: { bson_append_string(out,k,"CLASS"); return; } case T_NUMBER: { bson_append_long(out,k,v->u.number); return; } case T_REAL: { bson_append_double(out,k,v->u.real); return; } case T_ARRAY: { int i; int size; char buf[20]; size = v->u.arr->size; svalue_t *sv = v->u.arr->item; bson_append_start_array(out, k); for(i=0; i<size; i++) { sprintf(buf,"%d",i); v_to_bson(out,buf,sv++); } bson_append_finish_array( out ); return; } case T_MAPPING: { char num[25]; int j = v->u.map->table_size; mapping_node_t **a = v->u.map->table, *elt; bson_append_start_object(out, k); do { for (elt = a[j]; elt; elt = elt = elt->next) { if(elt->values->type == T_STRING){ v_to_bson(out,elt->values->u.string,elt->values+1); }else if(elt->values->type == T_NUMBER){ sprintf(num,"%ld",v->u.number); v_to_bson(out,num,elt->values+1); }else{ continue; } } } while (j--); bson_append_finish_object( out ); return; } } }
int main(int argc, char *argv[]) { bson_buffer bb; bson b; bson_iterator it, it2, it3; bson_oid_t oid; bson_buffer_init(&bb); bson_append_double(&bb, "d", 3.14); bson_append_string(&bb, "s", "hello"); { bson_buffer *obj = bson_append_start_object(&bb, "o"); bson_buffer *arr = bson_append_start_array(obj, "a"); bson_append_binary(arr, "0", 8, "w\0rld", 5); bson_append_finish_object(arr); bson_append_finish_object(obj); } bson_append_undefined(&bb, "u"); bson_oid_from_string(&oid, "010203040506070809101112"); ASSERT(!memcmp(oid.bytes, "\x001\x002\x003\x004\x005\x006\x007\x008\x009\x010\x011\x012", 12)); bson_append_oid(&bb, "oid", &oid); bson_append_bool(&bb, "b", 1); bson_append_date(&bb, "date", 0x0102030405060708ULL); bson_append_null(&bb, "n"); bson_append_regex(&bb, "r", "^asdf", "imx"); /* no dbref test (deprecated) */ bson_append_code(&bb, "c", "function(){}"); bson_append_symbol(&bb, "symbol", "SYMBOL"); { bson_buffer scope_buf; bson scope; bson_buffer_init(&scope_buf); bson_append_int(&scope_buf, "i", 123); bson_from_buffer(&scope, &scope_buf); bson_append_code_w_scope(&bb, "cws", "function(){return i}", &scope); bson_destroy(&scope); } /* no timestamp test (internal) */ bson_append_long(&bb, "l", 0x1122334455667788ULL); bson_from_buffer(&b, &bb); bson_iterator_init(&it, b.data); ASSERT(bson_iterator_more(&it)); ASSERT(bson_iterator_next(&it) == bson_double); ASSERT(bson_iterator_type(&it) == bson_double); ASSERT(!strcmp(bson_iterator_key(&it), "d")); ASSERT(bson_iterator_double(&it) == 3.14); ASSERT(bson_iterator_more(&it)); ASSERT(bson_iterator_next(&it) == bson_string); ASSERT(bson_iterator_type(&it) == bson_string); ASSERT(!strcmp(bson_iterator_key(&it), "s")); ASSERT(!strcmp(bson_iterator_string(&it), "hello")); ASSERT(bson_iterator_more(&it)); ASSERT(bson_iterator_next(&it) == bson_object); ASSERT(bson_iterator_type(&it) == bson_object); ASSERT(!strcmp(bson_iterator_key(&it), "o")); bson_iterator_subiterator(&it, &it2); ASSERT(bson_iterator_more(&it2)); ASSERT(bson_iterator_next(&it2) == bson_array); ASSERT(bson_iterator_type(&it2) == bson_array); ASSERT(!strcmp(bson_iterator_key(&it2), "a")); bson_iterator_subiterator(&it2, &it3); ASSERT(bson_iterator_more(&it3)); ASSERT(bson_iterator_next(&it3) == bson_bindata); ASSERT(bson_iterator_type(&it3) == bson_bindata); ASSERT(!strcmp(bson_iterator_key(&it3), "0")); ASSERT(bson_iterator_bin_type(&it3) == 8); ASSERT(bson_iterator_bin_len(&it3) == 5); ASSERT(!memcmp(bson_iterator_bin_data(&it3), "w\0rld", 5)); ASSERT(bson_iterator_more(&it3)); ASSERT(bson_iterator_next(&it3) == bson_eoo); ASSERT(bson_iterator_type(&it3) == bson_eoo); ASSERT(!bson_iterator_more(&it3)); ASSERT(bson_iterator_more(&it2)); ASSERT(bson_iterator_next(&it2) == bson_eoo); ASSERT(bson_iterator_type(&it2) == bson_eoo); ASSERT(!bson_iterator_more(&it2)); ASSERT(bson_iterator_more(&it)); ASSERT(bson_iterator_next(&it) == bson_undefined); ASSERT(bson_iterator_type(&it) == bson_undefined); ASSERT(!strcmp(bson_iterator_key(&it), "u")); ASSERT(bson_iterator_more(&it)); ASSERT(bson_iterator_next(&it) == bson_oid); ASSERT(bson_iterator_type(&it) == bson_oid); ASSERT(!strcmp(bson_iterator_key(&it), "oid")); ASSERT(!memcmp(bson_iterator_oid(&it)->bytes, "\x001\x002\x003\x004\x005\x006\x007\x008\x009\x010\x011\x012", 12)); ASSERT(bson_iterator_oid(&it)->ints[0] == oid.ints[0]); ASSERT(bson_iterator_oid(&it)->ints[1] == oid.ints[1]); ASSERT(bson_iterator_oid(&it)->ints[2] == oid.ints[2]); ASSERT(bson_iterator_more(&it)); ASSERT(bson_iterator_next(&it) == bson_bool); ASSERT(bson_iterator_type(&it) == bson_bool); ASSERT(!strcmp(bson_iterator_key(&it), "b")); ASSERT(bson_iterator_bool(&it) == 1); ASSERT(bson_iterator_more(&it)); ASSERT(bson_iterator_next(&it) == bson_date); ASSERT(bson_iterator_type(&it) == bson_date); ASSERT(!strcmp(bson_iterator_key(&it), "date")); ASSERT(bson_iterator_date(&it) == 0x0102030405060708ULL); ASSERT(bson_iterator_more(&it)); ASSERT(bson_iterator_next(&it) == bson_null); ASSERT(bson_iterator_type(&it) == bson_null); ASSERT(!strcmp(bson_iterator_key(&it), "n")); ASSERT(bson_iterator_more(&it)); ASSERT(bson_iterator_next(&it) == bson_regex); ASSERT(bson_iterator_type(&it) == bson_regex); ASSERT(!strcmp(bson_iterator_key(&it), "r")); ASSERT(!strcmp(bson_iterator_regex(&it), "^asdf")); ASSERT(!strcmp(bson_iterator_regex_opts(&it), "imx")); ASSERT(bson_iterator_more(&it)); ASSERT(bson_iterator_next(&it) == bson_code); ASSERT(bson_iterator_type(&it) == bson_code); ASSERT(!strcmp(bson_iterator_key(&it), "c")); ASSERT(!strcmp(bson_iterator_string(&it), "function(){}")); ASSERT(!strcmp(bson_iterator_code(&it), "function(){}")); ASSERT(bson_iterator_more(&it)); ASSERT(bson_iterator_next(&it) == bson_symbol); ASSERT(bson_iterator_type(&it) == bson_symbol); ASSERT(!strcmp(bson_iterator_key(&it), "symbol")); ASSERT(!strcmp(bson_iterator_string(&it), "SYMBOL")); ASSERT(bson_iterator_more(&it)); ASSERT(bson_iterator_next(&it) == bson_codewscope); ASSERT(bson_iterator_type(&it) == bson_codewscope); ASSERT(!strcmp(bson_iterator_key(&it), "cws")); ASSERT(!strcmp(bson_iterator_code(&it), "function(){return i}")); { bson scope; bson_iterator_code_scope(&it, &scope); bson_iterator_init(&it2, scope.data); ASSERT(bson_iterator_more(&it2)); ASSERT(bson_iterator_next(&it2) == bson_int); ASSERT(bson_iterator_type(&it2) == bson_int); ASSERT(!strcmp(bson_iterator_key(&it2), "i")); ASSERT(bson_iterator_int(&it2) == 123); ASSERT(bson_iterator_more(&it2)); ASSERT(bson_iterator_next(&it2) == bson_eoo); ASSERT(bson_iterator_type(&it2) == bson_eoo); ASSERT(!bson_iterator_more(&it2)); } ASSERT(bson_iterator_more(&it)); ASSERT(bson_iterator_next(&it) == bson_long); ASSERT(bson_iterator_type(&it) == bson_long); ASSERT(!strcmp(bson_iterator_key(&it), "l")); ASSERT(bson_iterator_long(&it) == 0x1122334455667788ULL); ASSERT(bson_iterator_more(&it)); ASSERT(bson_iterator_next(&it) == bson_eoo); ASSERT(bson_iterator_type(&it) == bson_eoo); ASSERT(!bson_iterator_more(&it)); return 0; }
static void lua_val_to_bson(lua_State *L, const char *key, int vpos, bson *bs, int tref) { int vtype = lua_type(L, vpos); char nbuf[TCNUMBUFSIZ]; if (key == NULL && vtype != LUA_TTABLE) { luaL_error(L, "lua_val_to_bson: Table must be on top of lua stack"); return; } switch (vtype) { case LUA_TTABLE: { if (vpos < 0) { vpos = lua_gettop(L) + vpos + 1; } lua_checkstack(L, 3); int bsontype_found = luaL_getmetafield(L, vpos, "__bsontype"); if (!bsontype_found) { lua_rawgeti(L, LUA_REGISTRYINDEX, tref); //+ reg table lua_pushvalue(L, vpos); //+ val lua_rawget(L, -2); //-val +reg table val if (lua_toboolean(L, -1)) { //already traversed lua_pop(L, 2); break; } lua_pop(L, 1); //-reg table val lua_pushvalue(L, vpos); lua_pushboolean(L, 1); lua_rawset(L, -3); lua_pop(L, 1); //-reg table int len = 0; bool query = false; bool array = true; if (luaL_getmetafield(L, vpos, "__query")) { lua_pop(L, 1); query = true; array = false; } if (array) { for (lua_pushnil(L); lua_next(L, vpos); lua_pop(L, 1)) { ++len; if ((lua_type(L, -2) != LUA_TNUMBER) || (lua_tointeger(L, -2) != len)) { lua_pop(L, 2); array = false; break; } } } if (array) { if (key) bson_append_start_array(bs, key); int i; for (i = 1; i <= len; ++i, lua_pop(L, 1)) { lua_rawgeti(L, vpos, i); bson_numstrn(nbuf, TCNUMBUFSIZ, (int64_t) i); lua_val_to_bson(L, nbuf, -1, bs, tref); } if (key) bson_append_finish_array(bs); } else if (query) { //special query builder case //oarr format: //{ {fname1, v1, v2...}, {fname2, v21, v22,..}, ... } //where: vN: {op, val} OR {val} with '__bval' metafield //Eg: {fname : {$inc : {...}, $dec : {...}}} -> {fname, {$inc, {}}, {$dec, {}}} lua_getfield(L, vpos, "_oarr"); //+oarr if (!lua_istable(L, -1)) { //it is not array lua_pop(L, 1); break; } if (key) bson_append_start_object(bs, key); //iterate over _oarr int ipos = lua_gettop(L); size_t ilen = lua_objlen(L, ipos); lua_checkstack(L, 2); size_t i; for (i = 1; i <= ilen; ++i, lua_pop(L, 1)) { lua_rawgeti(L, ipos, i); //gettop == 3 if (!lua_istable(L, -1)) continue; char *fname = NULL; int jpos = lua_gettop(L); size_t jlen = lua_objlen(L, jpos); lua_checkstack(L, 3); bool wrapped = false; size_t j; for (j = 1; j <= jlen; ++j, lua_pop(L, 1)) { lua_rawgeti(L, jpos, j); if (j == 1) { fname = strdup(lua_tostring(L, -1)); continue; } if (!fname || !lua_istable(L, -1)) { //invalid state lua_pop(L, 1); //pop val break; } int vblkpos = lua_gettop(L); if (j == 2 && luaL_getmetafield(L, -1, "__bval")) { //{val} single value +metafield lua_pop(L, 1); //-metafield lua_rawgeti(L, vblkpos, 1); //+val lua_val_to_bson(L, fname, lua_gettop(L), bs, tref); lua_pop(L, 2); //-val -lua_rawgeti break; //Terminate due single val } else { //{op, val} value if (!wrapped) { bson_append_start_object(bs, fname); wrapped = true; } lua_rawgeti(L, vblkpos, 1); //+op const char *op = lua_tostring(L, -1); if (op) { lua_rawgeti(L, vblkpos, 2); //+val lua_val_to_bson(L, op, lua_gettop(L), bs, tref); lua_pop(L, 1); //-val } lua_pop(L, 1); //-op } } if (wrapped) { bson_append_finish_object(bs); } if (fname) { free(fname); fname = NULL; } } if (key) bson_append_finish_object(bs); lua_pop(L, 1); //-oarr } else { if (key) bson_append_start_object(bs, key); TCLIST *keys = tclistnew(); //we need to sort keys due to unordered nature of lua tables for (lua_pushnil(L); lua_next(L, vpos);) { lua_pop(L, 1); //-val size_t ksize = 0; int ktype = lua_type(L, -1); if (ktype == LUA_TSTRING) { //accept only string keys const char* key = lua_tolstring(L, -1, &ksize); tclistpush(keys, key, ksize); } } tclistsort(keys); int i; for (i = 0; i < TCLISTNUM(keys); ++i) { int vkeysz = TCLISTVALSIZ(keys, i); const char *vkey = TCLISTVALPTR(keys, i); lua_pushlstring(L, vkey, vkeysz); lua_rawget(L, vpos); //+val if (key == NULL && lua_type(L, -1) == LUA_TSTRING && vkeysz == JDBIDKEYNAMEL && !strcmp(JDBIDKEYNAME, vkey)) { //root level OID as string //pack OID as type table lua_push_bsontype_table(L, BSON_OID); //+type table lua_pushvalue(L, -2); //dup oid(val) on stack lua_rawseti(L, -2, 1); //pop oid val if (ejdbisvalidoidstr(lua_tostring(L, -2))) { lua_val_to_bson(L, vkey, lua_gettop(L), bs, tref); } else { luaL_error(L, "OID _id='%s' is not valid", lua_tostring(L, -2)); } lua_pop(L, 1); //-type table } else { lua_val_to_bson(L, vkey, lua_gettop(L), bs, tref); } lua_pop(L, 1); //-val } tclistdel(keys); if (key) bson_append_finish_object(bs); } } else { //metafield __bsontype on top int bson_type = lua_tointeger(L, -1); if (!key && bson_type != BSON_OBJECT && bson_type != BSON_ARRAY) { lua_pop(L, 1); luaL_error(L, "Invalid object structure"); } lua_pop(L, 1); //-metafield __bsontype lua_rawgeti(L, -1, 1); //get first value switch (bson_type) { case BSON_OID: { const char* boid = lua_tostring(L, -1); if (boid && strlen(boid) == 24) { bson_oid_t oid; bson_oid_from_string(&oid, boid); bson_append_oid(bs, key, &oid); } break; } case BSON_DATE: bson_append_date(bs, key, (bson_date_t) lua_tonumber(L, -1)); break; case BSON_REGEX: { const char* regex = lua_tostring(L, -1); lua_rawgeti(L, -2, 2); // re opts const char* options = lua_tostring(L, -1); if (regex && options) { bson_append_regex(bs, key, regex, options); } lua_pop(L, 1); break; } case BSON_BINDATA: { size_t len; const char* cbuf = lua_tolstring(L, -1, &len); bson_append_binary(bs, key, BSON_BIN_BINARY, cbuf, len); break; } case BSON_NULL: bson_append_null(bs, key); break; case BSON_UNDEFINED: bson_append_undefined(bs, key); break; case BSON_OBJECT: if (key) bson_append_start_object(bs, key); lua_val_to_bson(L, NULL, vpos, bs, tref); if (key) bson_append_finish_object(bs); break; case BSON_ARRAY: if (key) bson_append_start_array(bs, key); lua_val_to_bson(L, NULL, vpos, bs, tref); if (key) bson_append_finish_array(bs); break; case BSON_DOUBLE: bson_append_double(bs, key, (double) lua_tonumber(L, -1)); break; case BSON_INT: bson_append_int(bs, key, (int32_t) lua_tonumber(L, -1)); break; case BSON_LONG: bson_append_long(bs, key, (int64_t) lua_tonumber(L, -1)); break; case BSON_BOOL: bson_append_bool(bs, key, lua_toboolean(L, -1)); break; default: break; } lua_pop(L, 1); //-1 first value } break; } case LUA_TNIL: bson_append_null(bs, key); break; case LUA_TNUMBER: { lua_Number numval = lua_tonumber(L, vpos); if (numval == floor(numval)) { int64_t iv = (int64_t) numval; if (-(1LL << 31) <= iv && iv <= (1LL << 31)) { bson_append_int(bs, key, iv); } else { bson_append_long(bs, key, iv); } } else { bson_append_double(bs, key, numval); } break; } case LUA_TBOOLEAN: bson_append_bool(bs, key, lua_toboolean(L, vpos)); break; case LUA_TSTRING: bson_append_string(bs, key, lua_tostring(L, vpos)); break; } }
EXPORT void mongo_bson_oid_from_string(char* s, void* oid) { bson_oid_from_string((bson_oid_t*) oid, s); }
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; ngx_buf_t* buffer; ngx_chain_t out; ngx_str_t location_name; ngx_str_t full_uri; char* value; gridfs gfs; gridfile gfile; gridfs_offset length; ngx_uint_t chunksize; ngx_uint_t numchunks; char* contenttype; ngx_uint_t i; ngx_int_t rc = NGX_OK; bson query; bson_buffer buf; bson_oid_t oid; mongo_cursor ** cursors; gridfs_offset chunk_len; const char * chunk_data; bson_iterator it; bson chunk; ngx_pool_cleanup_t* gridfs_cln; ngx_http_gridfs_cleanup_t* gridfs_clndata; 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); location_name = core_conf->name; full_uri = request->uri; gridfs_cln = ngx_pool_cleanup_add(request->pool, sizeof(ngx_http_gridfs_cleanup_t)); if (gridfs_cln == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } gridfs_cln->handler = ngx_http_gridfs_cleanup; gridfs_clndata = gridfs_cln->data; /* defensive */ if (full_uri.len < location_name.len) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Invalid location name or uri."); return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* Extract the value from the uri */ value = (char*)malloc(sizeof(char) * (full_uri.len - location_name.len + 1)); if (value == NULL) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Failed to allocate memory for value buffer."); return NGX_HTTP_INTERNAL_SERVER_ERROR; } memcpy(value, full_uri.data + location_name.len, full_uri.len - location_name.len); value[full_uri.len - location_name.len] = '\0'; /* URL Decoding */ if (!url_decode(value)) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Malformed request."); free(value); return NGX_HTTP_BAD_REQUEST; } /* Find the GridFile */ gridfs_init(gridfs_conf->mongod_conn, (const char*)gridfs_conf->gridfs_db.data, (const char*)gridfs_conf->gridfs_root_collection.data, &gfs); bson_buffer_init(&buf); switch (gridfs_conf->gridfs_type) { case bson_oid: bson_oid_from_string(&oid, value); bson_append_oid(&buf, (char*)gridfs_conf->gridfs_field.data, &oid); break; case bson_int: bson_append_int(&buf, (char*)gridfs_conf->gridfs_field.data, ngx_atoi((u_char*)value, strlen(value))); break; case bson_string: bson_append_string(&buf, (char*)gridfs_conf->gridfs_field.data, value); break; } bson_from_buffer(&query, &buf); if(!gridfs_find_query(&gfs, &query, &gfile)){ bson_destroy(&query); free(value); return NGX_HTTP_NOT_FOUND; } bson_destroy(&query); free(value); /* Get information about the file */ length = gridfile_get_contentlength(&gfile); chunksize = gridfile_get_chunksize(&gfile); numchunks = gridfile_get_numchunks(&gfile); contenttype = (char*)gridfile_get_contenttype(&gfile); /* Set the headers */ request->headers_out.status = NGX_HTTP_OK; request->headers_out.content_length_n = length; if (contenttype != NULL) { request->headers_out.content_type.len = strlen(contenttype); request->headers_out.content_type.data = (u_char*)contenttype; } else ngx_http_set_content_type(request); /* Determine if content is gzipped, set headers accordingly */ if ( gridfile_get_boolean(&gfile,"gzipped") ) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, gridfile_get_field(&gfile,"gzipped") ); request->headers_out.content_encoding = ngx_list_push(&request->headers_out.headers); if (request->headers_out.content_encoding == NULL) { return NGX_ERROR; } request->headers_out.content_encoding->hash = 1; request->headers_out.content_encoding->key.len = sizeof("Content-Encoding") - 1; request->headers_out.content_encoding->key.data = (u_char *) "Content-Encoding"; request->headers_out.content_encoding->value.len = sizeof("gzip") - 1; request->headers_out.content_encoding->value.data = (u_char *) "gzip"; } ngx_http_send_header(request); if (numchunks == 0) { /* Allocate space for the response buffer */ buffer = ngx_pcalloc(request->pool, sizeof(ngx_buf_t)); if (buffer == NULL) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Failed to allocate response buffer"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } buffer->pos = NULL; buffer->last = NULL; buffer->memory = 1; buffer->last_buf = 1; out.buf = buffer; out.next = NULL; return ngx_http_output_filter(request, &out); } cursors = (mongo_cursor **)ngx_pcalloc(request->pool, sizeof(mongo_cursor *) * numchunks); /* Read and serve chunk by chunk */ for (i = 0; i < numchunks; i++) { /* Allocate space for the response buffer */ buffer = ngx_pcalloc(request->pool, sizeof(ngx_buf_t)); if (buffer == NULL) { ngx_log_error(NGX_LOG_ERR, request->connection->log, 0, "Failed to allocate response buffer"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* Fetch the chunk from mongo */ cursors[i] = gridfile_get_chunks(&gfile, i, 1); mongo_cursor_next(cursors[i]); chunk = cursors[i]->current; bson_find(&it, &chunk, "data"); chunk_len = bson_iterator_bin_len( &it ); 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; /* Serve the Chunk */ rc = ngx_http_output_filter(request, &out); /* TODO: More Codes to Catch? */ if (rc == NGX_ERROR) { return NGX_ERROR; } } gridfs_clndata->cursors = cursors; gridfs_clndata->numchunks = numchunks; return rc; }
static bcon_error_t bson_bcon_key_value(bson *b, const char *key, const char *typespec, const bcon bci) { bcon_error_t ret = BCON_OK; bson_oid_t oid; char ptype = typespec ? typespec[1] : '_'; char utype = typespec ? typespec[2] : '_'; switch (ptype) { case '_': /* kv(b, key, utype, bci) */ switch (utype) { case '_': /* fall through */ case 's': bson_append_string( b, key, bci.s ); break; /* common case */ case 'f': bson_append_double( b, key, bci.f ); break; case 'D': bson_append_start_object( b, key ); ret = bson_append_bcon( b, bci.D ); bson_append_finish_object( b ); break; case 'A': bson_append_start_array( b, key ); ret = bson_append_bcon_array( b, bci.A ); bson_append_finish_array( b ); break; case 'o': if (*bci.o == '\0') bson_oid_gen( &oid ); else bson_oid_from_string( &oid, bci.o ); bson_append_oid( b, key, &oid ); break; case 'b': bson_append_bool( b, key, bci.b ); break; case 't': bson_append_time_t( b, key, bci.t ); break; case 'v': bson_append_null( b, key ); break; /* void */ case 'x': bson_append_symbol( b, key, bci.x ); break; case 'i': bson_append_int( b, key, bci.i ); break; case 'l': bson_append_long( b, key, bci.l ); break; default: printf("\nptype:'%c' utype:'%c'\n", ptype, utype); assert(NOT_REACHED); break; } break; case 'R': /* krv(b, key, utype, bci) */ switch (utype) { case 'f': bson_append_double( b, key, *bci.Rf ); break; case 's': bson_append_string( b, key, bci.Rs ); break; case 'D': bson_append_start_object( b, key ); ret = bson_append_bcon( b, bci.RD ); bson_append_finish_object( b ); break; case 'A': bson_append_start_array( b, key ); ret = bson_append_bcon_array( b, bci.RA ); bson_append_finish_array( b ); break; case 'o': if (*bci.o == '\0') bson_oid_gen( &oid ); else bson_oid_from_string( &oid, bci.o ); bson_append_oid( b, key, &oid ); break; case 'b': bson_append_bool( b, key, *bci.Rb ); break; case 't': bson_append_time_t( b, key, *bci.Rt ); break; case 'x': bson_append_symbol( b, key, bci.Rx ); break; case 'i': bson_append_int( b, key, *bci.Ri ); break; case 'l': bson_append_long( b, key, *bci.Rl ); break; default: printf("\nptype:'%c' utype:'%c'\n", ptype, utype); assert(NOT_REACHED); break; } break; case 'P': /* kpv(b, key, utype, bci) */ if (*bci.Pv != 0) { switch (utype) { case 'f': bson_append_double( b, key, **bci.Pf ); break; case 's': bson_append_string( b, key, *bci.Ps ); break; case 'D': bson_append_start_object( b, key ); ret = bson_append_bcon( b, *bci.PD ); bson_append_finish_object( b ); break; case 'A': bson_append_start_array( b, key ); ret = bson_append_bcon_array( b, *bci.PA ); bson_append_finish_array( b ); break; case 'o': if (**bci.Po == '\0') bson_oid_gen( &oid ); else bson_oid_from_string( &oid, *bci.Po ); bson_append_oid( b, key, &oid ); break; case 'b': bson_append_bool( b, key, **bci.Pb ); break; case 't': bson_append_time_t( b, key, **bci.Pt ); break; case 'x': if (*bci.Px != 0) bson_append_symbol( b, key, *bci.Px ); break; case 'i': bson_append_int( b, key, **bci.Pi ); break; case 'l': bson_append_long( b, key, **bci.Pl ); break; default: printf("\nptype:'%c' utype:'%c'\n", ptype, utype); assert(NOT_REACHED); break; } } break; default: printf("\nptype:'%c' utype:'%c'\n", ptype, utype); assert(NOT_REACHED); break; } return ret; }