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);
   }
Example #2
0
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;
}
Example #3
0
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;
}
Example #5
0
/* 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;
}
Example #7
0
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;
}
Example #8
0
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);
}
Example #9
0
/* 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);
}
Example #10
0
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;
}
Example #11
0
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);
}