2 This file is part of GNUnet.
3 (C) 2013 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file util/client_manager.c
23 * @brief Client manager; higher level client API with transmission queue
24 * and message handler registration.
25 * @author Gabor X Toth
31 #include "gnunet_util_lib.h"
33 #define LOG(kind,...) GNUNET_log_from (kind, "util",__VA_ARGS__)
37 * List of arrays of message handlers.
39 struct HandlersListItem
41 struct HandlersListItem *prev;
42 struct HandlersListItem *next;
45 * NULL-terminated array of handlers.
47 const struct GNUNET_CLIENT_MANAGER_MessageHandler *handlers;
51 struct MessageQueueItem
53 struct MessageQueueItem *prev;
54 struct MessageQueueItem *next;
55 struct GNUNET_MessageHeader *msg;
59 struct GNUNET_CLIENT_MANAGER_Connection
62 * Configuration to use.
64 const struct GNUNET_CONFIGURATION_Handle *cfg;
67 * Client connection to service.
69 struct GNUNET_CLIENT_Connection *client;
72 * Currently pending transmission request, or NULL for none.
74 struct GNUNET_CLIENT_TransmitHandle *client_tmit;
77 * Service name to connect to.
79 const char *service_name;
82 * Head of messages to transmit to the service.
84 struct MessageQueueItem *tmit_head;
87 * Tail of messages to transmit to the service.
89 struct MessageQueueItem *tmit_tail;
94 const struct GNUNET_CLIENT_MANAGER_MessageHandler *handlers;
98 * @see GNUNET_CLIENT_MANAGER_set_user_context()
99 * @see GNUNET_CLIENT_MANAGER_get_user_context()
104 * Last size given when user context was initialized.
105 * Used for sanity check.
107 size_t user_ctx_size;
110 * Task doing exponential back-off trying to reconnect.
112 GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
115 * Time for next connect retry.
117 struct GNUNET_TIME_Relative reconnect_delay;
120 * Are we currently polling for incoming messages?
125 * #GNUNET_YES if GNUNET_CLIENT_MANAGER_disconnect() was called
126 * and we're transmitting the last messages from the queue.
128 uint8_t disconnecting;
133 * Handle received messages from the service.
136 recv_message (void *cls, const struct GNUNET_MessageHeader *msg)
138 struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
139 uint16_t type = 0, size = 0;
143 type = ntohs (msg->type);
144 size = ntohs (msg->size);
145 /* FIXME: decrease reconnect_delay gradually after a successful reconnection */
149 while (NULL != mgr->handlers[i].callback)
151 const struct GNUNET_CLIENT_MANAGER_MessageHandler *mh = &mgr->handlers[i];
152 if ((mh->type == type) || (mh->type == GNUNET_MESSAGE_TYPE_ALL))
154 if (0 != mh->expected_size
155 && ((GNUNET_NO == mh->is_variable_size && size != mh->expected_size)
156 || (GNUNET_YES == mh->is_variable_size && size < mh->expected_size)))
158 LOG (GNUNET_ERROR_TYPE_ERROR,
159 "Expected %u bytes for message of type %u, got %u.\n",
160 mh->expected_size, type, size);
162 GNUNET_CLIENT_disconnect (mgr->client);
164 recv_message (mgr, NULL);
167 mh->callback (mh->callback_cls, mgr, msg);
171 if (NULL != mgr->client)
173 GNUNET_CLIENT_receive (mgr->client, &recv_message, mgr,
174 GNUNET_TIME_UNIT_FOREVER_REL);
180 * Schedule transmission of the next message from our queue.
182 * @param mgr Client manager connection.
185 transmit_next (struct GNUNET_CLIENT_MANAGER_Connection *mgr);
189 * Transmit next message to service.
191 * @param cls The struct GNUNET_PSYC_Channel.
192 * @param size Number of bytes available in @a buf.
193 * @param buf Where to copy the message.
195 * @return Number of bytes copied to @a buf.
198 send_next_message (void *cls, size_t buf_size, void *buf)
200 LOG (GNUNET_ERROR_TYPE_DEBUG, "send_next_message()\n");
201 struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
206 recv_message (mgr, NULL);
210 struct MessageQueueItem *mqi = mgr->tmit_head;
214 uint16_t size = ntohs (mqi->msg->size);
215 mgr->client_tmit = NULL;
216 GNUNET_assert (size <= buf_size);
217 memcpy (buf, mqi->msg, size);
219 GNUNET_CONTAINER_DLL_remove (mgr->tmit_head, mgr->tmit_tail, mqi);
220 GNUNET_free (mqi->msg);
223 if (NULL != mgr->tmit_head)
226 if (GNUNET_NO == mgr->in_receive)
228 mgr->in_receive = GNUNET_YES;
229 GNUNET_CLIENT_receive (mgr->client, &recv_message, mgr,
230 GNUNET_TIME_UNIT_FOREVER_REL);
237 * Schedule transmission of the next message from our queue.
239 * @param mgr Client manager connection.
242 transmit_next (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
244 LOG (GNUNET_ERROR_TYPE_DEBUG, "transmit_next()\n");
245 if (NULL != mgr->client_tmit || NULL == mgr->client)
248 if (NULL == mgr->tmit_head)
250 if (GNUNET_YES == mgr->disconnecting)
251 GNUNET_CLIENT_MANAGER_disconnect (mgr, GNUNET_NO);
256 = GNUNET_CLIENT_notify_transmit_ready (mgr->client,
257 ntohs (mgr->tmit_head->msg->size),
258 GNUNET_TIME_UNIT_FOREVER_REL,
266 * Try again to connect to the service.
268 * @param cls Channel handle.
269 * @param tc Scheduler context.
272 reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
274 struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
275 mgr->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
277 LOG (GNUNET_ERROR_TYPE_DEBUG,
278 "Connecting to %s service.\n", mgr->service_name);
279 GNUNET_assert (NULL == mgr->client);
280 mgr->client = GNUNET_CLIENT_connect (mgr->service_name, mgr->cfg);
281 GNUNET_assert (NULL != mgr->client);
288 * Connect to service.
290 * @param cfg Configuration to use.
291 * @param service_name Service name to connect to.
292 * @param handlers Message handlers.
294 * @return Client manager connection handle.
296 struct GNUNET_CLIENT_MANAGER_Connection *
297 GNUNET_CLIENT_MANAGER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
298 const char *service_name,
300 GNUNET_CLIENT_MANAGER_MessageHandler *handlers)
302 struct GNUNET_CLIENT_MANAGER_Connection *
303 mgr = GNUNET_malloc (sizeof (*mgr));
305 mgr->service_name = service_name;
306 mgr->handlers = handlers;
307 mgr->reconnect_delay = GNUNET_TIME_UNIT_ZERO;
308 mgr->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, mgr);
314 * Disconnect from the service.
316 * @param mgr Client manager connection.
317 * @param transmit_queue Transmit pending messages in queue before disconnecting.
320 GNUNET_CLIENT_MANAGER_disconnect (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
323 if (NULL != mgr->tmit_head)
325 if (GNUNET_YES == transmit_queue)
327 mgr->disconnecting = GNUNET_YES;
332 LOG (GNUNET_ERROR_TYPE_DEBUG,
333 "Disconnecting while there are still messages "
334 "in the transmission queue.\n");
335 GNUNET_CLIENT_MANAGER_drop_queue (mgr);
338 if (mgr->reconnect_task != GNUNET_SCHEDULER_NO_TASK)
340 GNUNET_SCHEDULER_cancel (mgr->reconnect_task);
341 mgr->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
343 if (NULL != mgr->client_tmit)
345 GNUNET_CLIENT_notify_transmit_ready_cancel (mgr->client_tmit);
346 mgr->client_tmit = NULL;
348 if (NULL != mgr->client)
350 GNUNET_CLIENT_disconnect (mgr->client);
358 * Reschedule connect to the service using exponential back-off.
360 * @param mgr Client manager connection.
363 GNUNET_CLIENT_MANAGER_reconnect (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
365 if (GNUNET_SCHEDULER_NO_TASK != mgr->reconnect_task)
368 if (NULL != mgr->client_tmit)
370 GNUNET_CLIENT_notify_transmit_ready_cancel (mgr->client_tmit);
371 mgr->client_tmit = NULL;
373 if (NULL != mgr->client)
375 GNUNET_CLIENT_disconnect (mgr->client);
378 mgr->in_receive = GNUNET_NO;
379 LOG (GNUNET_ERROR_TYPE_DEBUG,
380 "Scheduling task to reconnect to service in %s.\n",
381 GNUNET_STRINGS_relative_time_to_string (mgr->reconnect_delay, GNUNET_YES));
382 mgr->reconnect_task =
383 GNUNET_SCHEDULER_add_delayed (mgr->reconnect_delay, &reconnect, mgr);
384 mgr->reconnect_delay = GNUNET_TIME_STD_BACKOFF (mgr->reconnect_delay);
389 * Add a message to the end of the transmission queue.
391 * @param mgr Client manager connection.
392 * @param msg Message to transmit. It is free()'d after transmission.
395 GNUNET_CLIENT_MANAGER_transmit (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
396 struct GNUNET_MessageHeader *msg)
398 struct MessageQueueItem *mqi = GNUNET_malloc (sizeof (*mqi));
400 GNUNET_CONTAINER_DLL_insert_tail (mgr->tmit_head, mgr->tmit_tail, mqi);
406 * Add a message to the beginning of the transmission queue.
408 * @param mgr Client manager connection.
409 * @param msg Message to transmit. It is free()'d after transmission.
412 GNUNET_CLIENT_MANAGER_transmit_now (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
413 struct GNUNET_MessageHeader *msg)
415 struct MessageQueueItem *mqi = GNUNET_malloc (sizeof (*mqi));
417 GNUNET_CONTAINER_DLL_insert (mgr->tmit_head, mgr->tmit_tail, mqi);
423 * Drop all queued messages.
425 * @param mgr Client manager connection.
428 GNUNET_CLIENT_MANAGER_drop_queue (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
430 struct MessageQueueItem *cur, *next = mgr->tmit_head;
435 GNUNET_free (cur->msg);
442 * Obtain client connection handle.
444 * @param mgr Client manager connection handle.
446 * @return Client connection handle.
448 struct GNUNET_CLIENT_Connection *
449 GNUNET_CLIENT_MANAGER_get_client (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
456 * Return user context associated with the given client.
457 * Note: you should probably use the macro (call without the underscore).
459 * @param mgr Client manager connection.
460 * @param size Number of bytes in user context struct (for verification only).
461 * @return User context.
464 GNUNET_CLIENT_MANAGER_get_user_context_ (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
467 if ((0 == mgr->user_ctx_size) &&
468 (NULL == mgr->user_ctx))
469 return NULL; /* never set */
470 GNUNET_assert (size == mgr->user_ctx_size);
471 return mgr->user_ctx;
476 * Set user context to be associated with the given client.
477 * Note: you should probably use the macro (call without the underscore).
479 * @param mgr Client manager connection.
480 * @param ctx User context.
481 * @param size Number of bytes in user context struct (for verification only).
484 GNUNET_CLIENT_MANAGER_set_user_context_ (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
490 mgr->user_ctx_size = 0;
494 mgr->user_ctx_size = size;