request_t *
mock_server_receives_query (mock_server_t *server,
                            const char *ns,
                            mongoc_query_flags_t flags,
                            uint32_t skip,
                            uint32_t n_return,
                            const char *query_json,
                            const char *fields_json)
{
   sync_queue_t *q;
   request_t *request;

   q = mock_server_get_queue (server);
   request = (request_t *) q_get (q, server->request_timeout_msec);

   if (request && !request_matches_query (request,
                                          ns,
                                          flags,
                                          skip,
                                          n_return,
                                          query_json,
                                          fields_json,
                                          false)) {
      request_destroy (request);
      return NULL;
   }

   return request;
}
request_t *
mock_server_receives_request (mock_server_t *server)
{
   sync_queue_t *q;

   q = mock_server_get_queue (server);
   return (request_t *) q_get (q, server->request_timeout_msec);
}
request_t *mock_server_receives_kill_cursors (mock_server_t *server,
                                              int64_t cursor_id)
{
   sync_queue_t *q;
   request_t *request;

   q = mock_server_get_queue (server);

   request = (request_t *) q_get (q, server->request_timeout_msec);

   if (request && !request_matches_kill_cursors (request, cursor_id)) {
      request_destroy (request);
      return NULL;
   }

   return request;
}
request_t *
mock_server_receives_command (mock_server_t *server,
                              const char *database_name,
                              mongoc_query_flags_t flags,
                              const char *command_json,
                              ...)
{
   va_list args;
   char *formatted_command_json = NULL;
   char *ns;
   sync_queue_t *q;
   request_t *request;

   va_start (args, command_json);
   if (command_json) {
      formatted_command_json = bson_strdupv_printf (command_json, args);
   }
   va_end (args);

   ns = bson_strdup_printf ("%s.$cmd", database_name);

   q = mock_server_get_queue (server);
   request = (request_t *) q_get (q, server->request_timeout_msec);

   if (request && !request_matches_query (request,
                                          ns,
                                          flags,
                                          0,
                                          1,
                                          formatted_command_json,
                                          NULL,
                                          true)) {
      request_destroy (request);
      request = NULL;
   }

   bson_free (formatted_command_json);
   bson_free (ns);

   return request;
}
request_t *
mock_server_receives_bulk_insert (mock_server_t *server,
                                  const char *ns,
                                  mongoc_insert_flags_t flags,
                                  int n)
{
   sync_queue_t *q;
   request_t *request;

   q = mock_server_get_queue (server);
   request = (request_t *) q_get (q, server->request_timeout_msec);

   if (request && !request_matches_bulk_insert (request,
                                                ns,
                                                flags,
                                                n)) {
      request_destroy (request);
      return NULL;
   }

   return request;
}
request_t *
mock_server_receives_delete (mock_server_t *server,
                             const char *ns,
                             mongoc_remove_flags_t flags,
                             const char *selector_json)
{
   sync_queue_t *q;
   request_t *request;

   q = mock_server_get_queue (server);
   request = (request_t *) q_get (q, server->request_timeout_msec);

   if (request && !request_matches_delete (request,
                                           ns,
                                           flags,
                                           selector_json)) {
      request_destroy (request);
      return NULL;
   }

   return request;
}
/* 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);
}
Exemple #8
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);
}