mongoc_server_description_t * mongoc_topology_description_select (mongoc_topology_description_t *topology, mongoc_ss_optype_t optype, const mongoc_read_prefs_t *read_pref, int64_t local_threshold_ms) { mongoc_array_t suitable_servers; mongoc_server_description_t *sd = NULL; ENTRY; if (!topology->compatible) { /* TODO, should we return an error object here, or just treat as a case where there are no suitable servers? */ RETURN(NULL); } if (topology->type == MONGOC_TOPOLOGY_SINGLE) { sd = (mongoc_server_description_t *)mongoc_set_get_item (topology->servers, 0); if (sd->has_is_master) { RETURN(sd); } else { RETURN(NULL); } } _mongoc_array_init(&suitable_servers, sizeof(mongoc_server_description_t *)); mongoc_topology_description_suitable_servers(&suitable_servers, optype, topology, read_pref, local_threshold_ms); if (suitable_servers.len != 0) { sd = _mongoc_array_index(&suitable_servers, mongoc_server_description_t*, rand() % suitable_servers.len); }
mock_server_t * mock_server_new () { mock_server_t *server = (mock_server_t *)bson_malloc0 (sizeof (mock_server_t)); server->request_timeout_msec = get_future_timeout_ms (); _mongoc_array_init (&server->autoresponders, sizeof (autoresponder_handle_t)); _mongoc_array_init (&server->worker_threads, sizeof (mongoc_thread_t)); mongoc_cond_init (&server->cond); mongoc_mutex_init (&server->mutex); server->q = q_new (); server->start_time = bson_get_monotonic_time (); return server; }
mock_server_t * mock_server_new () { mock_server_t *server = (mock_server_t *)bson_malloc0 (sizeof (mock_server_t)); server->request_timeout_msec = 10 * 1000; _mongoc_array_init (&server->autoresponders, sizeof (autoresponder_handle_t)); _mongoc_array_init (&server->worker_threads, sizeof (mongoc_thread_t)); mongoc_cond_init (&server->cond); mongoc_mutex_init (&server->mutex); server->q = q_new (); server->start_time = bson_get_monotonic_time (); if (test_framework_getenv_bool ("MONGOC_TEST_SERVER_VERBOSE")) { server->verbose = true; } return server; }
mongoc_bulk_operation_t * mongoc_bulk_operation_new (bool ordered) { mongoc_bulk_operation_t *bulk; bulk = bson_malloc0 (sizeof *bulk); bulk->ordered = ordered; _mongoc_array_init (&bulk->commands, sizeof (mongoc_write_command_t)); return bulk; }
/* TODO: merge with mock_server_reply? */ void mock_server_reply_simple (mock_server_t *server, mongoc_stream_t *client, const mongoc_rpc_t *request, mongoc_reply_flags_t flags, const bson_t *doc, int64_t cursor_id) { mongoc_iovec_t *iov; mongoc_array_t ar; mongoc_rpc_t r = {{ 0 }}; size_t expected = 0; ssize_t n_written; int iovcnt; int i; BSON_ASSERT (server); BSON_ASSERT (request); BSON_ASSERT (client); BSON_ASSERT (doc); _mongoc_array_init (&ar, sizeof (mongoc_iovec_t)); mongoc_mutex_lock (&server->mutex); r.reply.request_id = ++server->last_response_id; mongoc_mutex_unlock (&server->mutex); r.reply.msg_len = 0; r.reply.response_to = request->header.request_id; r.reply.opcode = MONGOC_OPCODE_REPLY; r.reply.flags = flags; r.reply.cursor_id = cursor_id; r.reply.start_from = 0; r.reply.n_returned = 1; r.reply.documents = bson_get_data (doc); r.reply.documents_len = doc->len; _mongoc_rpc_gather (&r, &ar); _mongoc_rpc_swab_to_le (&r); iov = (mongoc_iovec_t *)ar.data; iovcnt = (int) ar.len; for (i = 0; i < iovcnt; i++) { expected += iov[i].iov_len; } n_written = mongoc_stream_writev (client, iov, (size_t) iovcnt, -1); assert (n_written == expected); _mongoc_array_destroy (&ar); }
mongoc_bulk_operation_t * mongoc_bulk_operation_new (bool ordered) { mongoc_bulk_operation_t *bulk; bulk = (mongoc_bulk_operation_t *)bson_malloc0 (sizeof *bulk); bulk->flags.bypass_document_validation = MONGOC_BYPASS_DOCUMENT_VALIDATION_DEFAULT; bulk->flags.ordered = ordered; bulk->server_id = 0; _mongoc_array_init (&bulk->commands, sizeof (mongoc_write_command_t)); return bulk; }
mongoc_async_cmd_t * mongoc_async_cmd_new (mongoc_async_t *async, mongoc_stream_t *stream, bool is_setup_done, struct addrinfo *dns_result, mongoc_async_cmd_initiate_t initiator, int64_t initiate_delay_ms, mongoc_async_cmd_setup_t setup, void *setup_ctx, const char *dbname, const bson_t *cmd, mongoc_async_cmd_cb_t cb, void *cb_data, int64_t timeout_msec) { mongoc_async_cmd_t *acmd; BSON_ASSERT (cmd); BSON_ASSERT (dbname); acmd = (mongoc_async_cmd_t *) bson_malloc0 (sizeof (*acmd)); acmd->async = async; acmd->dns_result = dns_result; acmd->timeout_msec = timeout_msec; acmd->stream = stream; acmd->initiator = initiator; acmd->initiate_delay_ms = initiate_delay_ms; acmd->setup = setup; acmd->setup_ctx = setup_ctx; acmd->cb = cb; acmd->data = cb_data; acmd->connect_started = bson_get_monotonic_time (); bson_copy_to (cmd, &acmd->cmd); _mongoc_array_init (&acmd->array, sizeof (mongoc_iovec_t)); _mongoc_buffer_init (&acmd->buffer, NULL, 0, NULL, NULL); _mongoc_async_cmd_init_send (acmd, dbname); _mongoc_async_cmd_state_start (acmd, is_setup_done); async->ncmds++; DL_APPEND (async->cmds, acmd); return acmd; }
void mock_server_reply_multi (request_t *request, mongoc_reply_flags_t flags, const bson_t *docs, int n_docs, int64_t cursor_id) { const mongoc_rpc_t *request_rpc; mock_server_t *server; mongoc_stream_t *client; char *doc_json; bson_string_t *docs_json; mongoc_iovec_t *iov; mongoc_array_t ar; mongoc_rpc_t r = {{ 0 }}; size_t expected = 0; ssize_t n_written; int iovcnt; int i; uint8_t *buf; uint8_t *ptr; size_t len; BSON_ASSERT (request); BSON_ASSERT (docs); request_rpc = &request->request_rpc; server = request->server; client = request->client; docs_json = bson_string_new (""); for (i = 0; i < n_docs; i++) { doc_json = bson_as_json (&docs[i], NULL); bson_string_append (docs_json, doc_json); bson_free (doc_json); if (i < n_docs - 1) { bson_string_append (docs_json, ", "); } } if (mock_server_get_verbose (request->server)) { printf ("%5.2f %hu <- %hu \t%s\n", mock_server_get_uptime_sec (request->server), request->client_port, mock_server_get_port (request->server), docs_json->str); fflush (stdout); } len = 0; for (i = 0; i < n_docs; i++) { len += docs[i].len; } ptr = buf = bson_malloc (len); for (i = 0; i < n_docs; i++) { memcpy (ptr, bson_get_data (&docs[i]), docs[i].len); ptr += docs[i].len; } _mongoc_array_init (&ar, sizeof (mongoc_iovec_t)); if (!(request->opcode == MONGOC_OPCODE_QUERY && request_rpc->query.flags & MONGOC_QUERY_EXHAUST)) { server->last_response_id++; } mongoc_mutex_lock (&server->mutex); r.reply.request_id = server->last_response_id; mongoc_mutex_unlock (&server->mutex); r.reply.msg_len = 0; r.reply.response_to = request_rpc->header.request_id; r.reply.opcode = MONGOC_OPCODE_REPLY; r.reply.flags = flags; r.reply.cursor_id = cursor_id; r.reply.start_from = 0; r.reply.n_returned = 1; r.reply.documents = buf; r.reply.documents_len = (uint32_t)len; _mongoc_rpc_gather (&r, &ar); _mongoc_rpc_swab_to_le (&r); iov = (mongoc_iovec_t *)ar.data; iovcnt = (int) ar.len; for (i = 0; i < iovcnt; i++) { expected += iov[i].iov_len; } n_written = mongoc_stream_writev (client, iov, (size_t) iovcnt, -1); assert (n_written == expected); bson_string_free (docs_json, true); _mongoc_array_destroy (&ar); bson_free (buf); }
/* TODO: factor */ static void * worker_thread (void *data) { worker_closure_t *closure = (worker_closure_t *) data; mock_server_t *server = closure->server; mongoc_stream_t *client_stream = closure->client_stream; mongoc_buffer_t buffer; mongoc_rpc_t *rpc = NULL; bool handled; bson_error_t error; int32_t msg_len; bool stopped; sync_queue_t *q; request_t *request; mongoc_array_t autoresponders; ssize_t i; autoresponder_handle_t handle; #ifdef MONGOC_ENABLE_SSL bool ssl; #endif ENTRY; BSON_ASSERT(closure); #ifdef MONGOC_ENABLE_SSL mongoc_mutex_lock (&server->mutex); ssl = server->ssl; mongoc_mutex_unlock (&server->mutex); if (ssl) { if (!mongoc_stream_tls_handshake_block (client_stream, "localhost", TIMEOUT, &error)) { MONGOC_ERROR("Blocking TLS handshake failed"); mongoc_stream_close (client_stream); mongoc_stream_destroy (client_stream); RETURN (NULL); } } #endif _mongoc_buffer_init (&buffer, NULL, 0, NULL, NULL); _mongoc_array_init (&autoresponders, sizeof (autoresponder_handle_t)); again: bson_free (rpc); rpc = NULL; handled = false; mongoc_mutex_lock (&server->mutex); stopped = server->stopped; mongoc_mutex_unlock (&server->mutex); if (stopped) { GOTO(failure); } if (_mongoc_buffer_fill (&buffer, client_stream, 4, TIMEOUT, &error) == -1) { GOTO (again); } assert (buffer.len >= 4); memcpy (&msg_len, buffer.data + buffer.off, 4); msg_len = BSON_UINT32_FROM_LE (msg_len); if (msg_len < 16) { MONGOC_WARNING ("No data"); GOTO (failure); } if (_mongoc_buffer_fill (&buffer, client_stream, (size_t) msg_len, -1, &error) == -1) { MONGOC_WARNING ("%s():%d: %s", BSON_FUNC, __LINE__, error.message); GOTO (failure); } assert (buffer.len >= (unsigned) msg_len); /* copies message from buffer */ request = request_new (&buffer, msg_len, server, client_stream, closure->port); mongoc_mutex_lock (&server->mutex); _mongoc_array_copy (&autoresponders, &server->autoresponders); mongoc_mutex_unlock (&server->mutex); if (mock_server_get_verbose (server)) { printf ("%5.2f %hu -> %hu %s\n", mock_server_get_uptime_sec (server), closure->port, server->port, request->as_str); fflush (stdout); } /* run responders most-recently-added-first */ for (i = server->autoresponders.len - 1; i >= 0; i--) { handle = _mongoc_array_index (&server->autoresponders, autoresponder_handle_t, i); if (handle.responder (request, handle.data)) { handled = true; /* responder should destroy the request */ request = NULL; break; } } if (!handled) { q = mock_server_get_queue (server); q_put (q, (void *) request); request = NULL; } memmove (buffer.data, buffer.data + buffer.off + msg_len, buffer.len - msg_len); buffer.off = 0; buffer.len -= msg_len; GOTO (again); failure: _mongoc_array_destroy (&autoresponders); _mongoc_buffer_destroy (&buffer); mongoc_stream_close (client_stream); mongoc_stream_destroy (client_stream); bson_free (rpc); bson_free (closure); _mongoc_buffer_destroy (&buffer); RETURN (NULL); }
static void * main_thread (void *data) { mock_server_t *server = (mock_server_t *)data; mongoc_socket_t *client_sock; bool stopped; uint16_t port; mongoc_stream_t *client_stream; worker_closure_t *closure; mongoc_thread_t thread; mongoc_array_t worker_threads; size_t i; mongoc_mutex_lock (&server->mutex); server->running = true; mongoc_cond_signal (&server->cond); mongoc_mutex_unlock (&server->mutex); for (; ;) { client_sock = mongoc_socket_accept_ex ( server->sock, bson_get_monotonic_time () + TIMEOUT, &port); mongoc_mutex_lock (&server->mutex); stopped = server->stopped; mongoc_mutex_unlock (&server->mutex); if (stopped) { break; } if (client_sock) { if (mock_server_get_verbose (server)) { printf ("%5.2f %hu -> server port %hu (connected)\n", mock_server_get_uptime_sec (server), port, server->port); fflush (stdout); } client_stream = mongoc_stream_socket_new (client_sock); #ifdef MONGOC_ENABLE_SSL mongoc_mutex_lock (&server->mutex); if (server->ssl) { server->ssl_opts.weak_cert_validation = 1; client_stream = mongoc_stream_tls_new (client_stream, &server->ssl_opts, 0); if (!client_stream) { mongoc_mutex_unlock (&server->mutex); perror ("Failed to attach tls stream"); break; } } mongoc_mutex_unlock (&server->mutex); #endif closure = (worker_closure_t *)bson_malloc (sizeof *closure); closure->server = server; closure->client_stream = client_stream; closure->port = port; mongoc_thread_create (&thread, worker_thread, closure); mongoc_mutex_lock (&server->mutex); _mongoc_array_append_val (&server->worker_threads, thread); mongoc_mutex_unlock (&server->mutex); } } /* copy list of worker threads and join them all */ _mongoc_array_init (&worker_threads, sizeof (mongoc_thread_t)); mongoc_mutex_lock (&server->mutex); _mongoc_array_copy (&worker_threads, &server->worker_threads); mongoc_mutex_unlock (&server->mutex); for (i = 0; i < worker_threads.len; i++) { mongoc_thread_join ( _mongoc_array_index (&worker_threads, mongoc_thread_t, i)); } _mongoc_array_destroy (&worker_threads); mongoc_mutex_lock (&server->mutex); server->running = false; mongoc_mutex_unlock (&server->mutex); return NULL; }
static void * worker_thread (void *data) { worker_closure_t *closure = (worker_closure_t *) data; mock_server_t *server = closure->server; mongoc_stream_t *client_stream = closure->client_stream; mongoc_buffer_t buffer; mongoc_rpc_t *rpc = NULL; bool handled; bson_error_t error; int32_t msg_len; sync_queue_t *requests; sync_queue_t *replies; request_t *request; mongoc_array_t autoresponders; ssize_t i; autoresponder_handle_t handle; reply_t *reply; #ifdef MONGOC_ENABLE_SSL bool ssl; #endif ENTRY; /* queue of client replies sent over this worker's connection */ replies = q_new (); #ifdef MONGOC_ENABLE_SSL mongoc_mutex_lock (&server->mutex); ssl = server->ssl; mongoc_mutex_unlock (&server->mutex); if (ssl) { if (!mongoc_stream_tls_handshake_block (client_stream, "localhost", TIMEOUT, &error)) { mongoc_stream_close (client_stream); mongoc_stream_destroy (client_stream); RETURN (NULL); } } #endif _mongoc_buffer_init (&buffer, NULL, 0, NULL, NULL); _mongoc_array_init (&autoresponders, sizeof (autoresponder_handle_t)); again: /* loop, checking for requests to receive or replies to send */ bson_free (rpc); rpc = NULL; if (_mongoc_buffer_fill (&buffer, client_stream, 4, 10, &error) > 0) { assert (buffer.len >= 4); memcpy (&msg_len, buffer.data + buffer.off, 4); msg_len = BSON_UINT32_FROM_LE (msg_len); if (msg_len < 16) { MONGOC_WARNING ("No data"); GOTO (failure); } if (_mongoc_buffer_fill (&buffer, client_stream, (size_t) msg_len, -1, &error) == -1) { MONGOC_WARNING ("%s():%d: %s", BSON_FUNC, __LINE__, error.message); GOTO (failure); } assert (buffer.len >= (unsigned) msg_len); /* copies message from buffer */ request = request_new (&buffer, msg_len, server, client_stream, closure->port, replies); memmove (buffer.data, buffer.data + buffer.off + msg_len, buffer.len - msg_len); buffer.off = 0; buffer.len -= msg_len; mongoc_mutex_lock (&server->mutex); _mongoc_array_copy (&autoresponders, &server->autoresponders); mongoc_mutex_unlock (&server->mutex); test_suite_mock_server_log ("%5.2f %hu -> %hu %s", mock_server_get_uptime_sec (server), closure->port, server->port, request->as_str); /* run responders most-recently-added-first */ handled = false; for (i = server->autoresponders.len - 1; i >= 0; i--) { handle = _mongoc_array_index (&server->autoresponders, autoresponder_handle_t, i); if (handle.responder (request, handle.data)) { /* responder destroyed request and enqueued a reply in "replies" */ handled = true; request = NULL; break; } } if (!handled) { /* pass to the main thread via the queue */ requests = mock_server_get_queue (server); q_put (requests, (void *) request); } } if (_mock_server_stopping (server)) { GOTO (failure); } reply = q_get (replies, 10); if (reply) { _mock_server_reply_with_stream (server, reply, client_stream); _reply_destroy (reply); } if (_mock_server_stopping (server)) { GOTO (failure); } GOTO (again); failure: _mongoc_array_destroy (&autoresponders); _mongoc_buffer_destroy (&buffer); mongoc_stream_close (client_stream); mongoc_stream_destroy (client_stream); bson_free (rpc); bson_free (closure); _mongoc_buffer_destroy (&buffer); while ((reply = q_get_nowait (replies))) { _reply_destroy (reply); } q_destroy (replies); RETURN (NULL); }