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-client-mgr", __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;
97 * Disconnect callback.
99 void (*disconnect_cb)(void *);
102 * Disconnect closure.
104 void *disconnect_cls;
107 * User context value.
108 * @see GNUNET_CLIENT_MANAGER_set_user_context()
109 * @see GNUNET_CLIENT_MANAGER_get_user_context()
114 * Last size given when user context was initialized.
115 * Used for sanity check.
117 size_t user_ctx_size;
120 * Task doing exponential back-off trying to reconnect.
122 GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
125 * Time for next connect retry.
127 struct GNUNET_TIME_Relative reconnect_delay;
130 * Are we currently polling for incoming messages?
135 * #GNUNET_YES if GNUNET_CLIENT_MANAGER_disconnect() was called
136 * and we're transmitting the last messages from the queue.
138 uint8_t is_disconnecting;
143 * Handle received messages from the service.
146 recv_message (void *cls, const struct GNUNET_MessageHeader *msg)
148 struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
149 uint16_t type = 0, size = 0;
153 type = ntohs (msg->type);
154 size = ntohs (msg->size);
155 /* FIXME: decrease reconnect_delay gradually after a successful reconnection */
159 while (NULL != mgr->handlers[i].callback)
161 const struct GNUNET_CLIENT_MANAGER_MessageHandler *mh = &mgr->handlers[i];
162 if ((mh->type == type) || (mh->type == GNUNET_MESSAGE_TYPE_ALL))
164 if (0 != mh->expected_size
165 && ((GNUNET_NO == mh->is_variable_size && size != mh->expected_size)
166 || (GNUNET_YES == mh->is_variable_size && size < mh->expected_size)))
168 LOG (GNUNET_ERROR_TYPE_ERROR,
169 "Expected %u bytes for message of type %u, got %u.\n",
170 mh->expected_size, type, size);
172 GNUNET_CLIENT_disconnect (mgr->client);
174 recv_message (mgr, NULL);
177 mh->callback (mh->callback_cls, mgr, msg);
181 if (NULL != mgr->client)
183 GNUNET_CLIENT_receive (mgr->client, &recv_message, mgr,
184 GNUNET_TIME_UNIT_FOREVER_REL);
190 * Schedule transmission of the next message from our queue.
192 * @param mgr Client manager connection.
195 transmit_next (struct GNUNET_CLIENT_MANAGER_Connection *mgr);
199 schedule_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
201 struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
202 GNUNET_CLIENT_MANAGER_disconnect (mgr, GNUNET_NO,
203 mgr->disconnect_cb, mgr->disconnect_cls);
208 * Transmit next message to service.
211 * struct GNUNET_CLIENT_MANAGER_Connection
213 * Number of bytes available in @a buf.
215 * Where to copy the message.
217 * @return Number of bytes copied to @a buf.
220 send_next_message (void *cls, size_t buf_size, void *buf)
222 LOG (GNUNET_ERROR_TYPE_DEBUG, "send_next_message()\n");
223 struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
228 recv_message (mgr, NULL);
232 struct MessageQueueItem *mqi = mgr->tmit_head;
236 uint16_t size = ntohs (mqi->msg->size);
237 mgr->client_tmit = NULL;
238 GNUNET_assert (size <= buf_size);
239 memcpy (buf, mqi->msg, size);
241 GNUNET_CONTAINER_DLL_remove (mgr->tmit_head, mgr->tmit_tail, mqi);
242 GNUNET_free (mqi->msg);
245 if (NULL != mgr->tmit_head)
249 else if (GNUNET_YES == mgr->is_disconnecting)
251 GNUNET_SCHEDULER_add_now (&schedule_disconnect, mgr);
255 if (GNUNET_NO == mgr->in_receive)
257 mgr->in_receive = GNUNET_YES;
258 GNUNET_CLIENT_receive (mgr->client, &recv_message, mgr,
259 GNUNET_TIME_UNIT_FOREVER_REL);
266 * Schedule transmission of the next message from our queue.
268 * @param mgr Client manager connection.
271 transmit_next (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
273 LOG (GNUNET_ERROR_TYPE_DEBUG, "transmit_next()\n");
274 if (NULL != mgr->client_tmit || NULL == mgr->client)
277 if (NULL == mgr->tmit_head)
279 if (GNUNET_YES == mgr->is_disconnecting)
280 GNUNET_CLIENT_MANAGER_disconnect (mgr, GNUNET_NO,
281 mgr->disconnect_cb, mgr->disconnect_cls);
286 = GNUNET_CLIENT_notify_transmit_ready (mgr->client,
287 ntohs (mgr->tmit_head->msg->size),
288 GNUNET_TIME_UNIT_FOREVER_REL,
296 * Try again to connect to the service.
304 schedule_reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
306 struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
307 mgr->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
309 LOG (GNUNET_ERROR_TYPE_DEBUG,
310 "Connecting to %s service.\n", mgr->service_name);
311 GNUNET_assert (NULL == mgr->client);
312 mgr->client = GNUNET_CLIENT_connect (mgr->service_name, mgr->cfg);
313 GNUNET_assert (NULL != mgr->client);
320 * Connect to service.
323 * Configuration to use.
324 * @param service_name
325 * Service name to connect to.
329 * @return Client manager connection handle.
331 struct GNUNET_CLIENT_MANAGER_Connection *
332 GNUNET_CLIENT_MANAGER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
333 const char *service_name,
335 GNUNET_CLIENT_MANAGER_MessageHandler *handlers)
337 struct GNUNET_CLIENT_MANAGER_Connection *
338 mgr = GNUNET_malloc (sizeof (*mgr));
340 mgr->service_name = service_name;
341 mgr->handlers = handlers;
342 mgr->reconnect_delay = GNUNET_TIME_UNIT_ZERO;
343 mgr->reconnect_task = GNUNET_SCHEDULER_add_now (&schedule_reconnect, mgr);
349 * Disconnect from the service.
352 * Client manager connection.
353 * @param transmit_queue
354 * Transmit pending messages in queue before disconnecting.
355 * @param disconnect_cb
356 * Function called after disconnected from the service.
358 * Closure for @a disconnect_cb.
361 GNUNET_CLIENT_MANAGER_disconnect (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
363 GNUNET_ContinuationCallback disconnect_cb,
366 LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting (%d)\n", transmit_queue);
367 mgr->disconnect_cb = disconnect_cb;
368 mgr->disconnect_cls = cls;
369 if (NULL != mgr->tmit_head)
371 if (GNUNET_YES == transmit_queue)
373 mgr->is_disconnecting = GNUNET_YES;
379 LOG (GNUNET_ERROR_TYPE_DEBUG,
380 "Disconnecting while there are still messages "
381 "in the transmission queue.\n");
382 GNUNET_CLIENT_MANAGER_drop_queue (mgr);
385 if (mgr->reconnect_task != GNUNET_SCHEDULER_NO_TASK)
387 GNUNET_SCHEDULER_cancel (mgr->reconnect_task);
388 mgr->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
390 if (NULL != mgr->client_tmit)
392 GNUNET_CLIENT_notify_transmit_ready_cancel (mgr->client_tmit);
393 mgr->client_tmit = NULL;
395 if (NULL != mgr->client)
397 GNUNET_CLIENT_disconnect (mgr->client);
400 if (NULL != mgr->disconnect_cb)
401 mgr->disconnect_cb (mgr->disconnect_cls);
403 LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnected.\n");
408 * Reschedule connect to the service using exponential back-off.
411 * Client manager connection.
414 GNUNET_CLIENT_MANAGER_reconnect (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
416 if (GNUNET_SCHEDULER_NO_TASK != mgr->reconnect_task)
419 if (NULL != mgr->client_tmit)
421 GNUNET_CLIENT_notify_transmit_ready_cancel (mgr->client_tmit);
422 mgr->client_tmit = NULL;
424 if (NULL != mgr->client)
426 GNUNET_CLIENT_disconnect (mgr->client);
429 mgr->in_receive = GNUNET_NO;
430 LOG (GNUNET_ERROR_TYPE_DEBUG,
431 "Scheduling task to reconnect to service in %s.\n",
432 GNUNET_STRINGS_relative_time_to_string (mgr->reconnect_delay, GNUNET_YES));
433 mgr->reconnect_task =
434 GNUNET_SCHEDULER_add_delayed (mgr->reconnect_delay, &schedule_reconnect, mgr);
435 mgr->reconnect_delay = GNUNET_TIME_STD_BACKOFF (mgr->reconnect_delay);
440 * Add a message to the end of the transmission queue.
443 * Client manager connection.
445 * Message to transmit, should be allocated with GNUNET_malloc() or
446 * GNUNET_new(), as it is freed with GNUNET_free() after transmission.
449 GNUNET_CLIENT_MANAGER_transmit (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
450 struct GNUNET_MessageHeader *msg)
452 struct MessageQueueItem *mqi = GNUNET_malloc (sizeof (*mqi));
454 GNUNET_CONTAINER_DLL_insert_tail (mgr->tmit_head, mgr->tmit_tail, mqi);
460 * Add a message to the beginning of the transmission queue.
463 * Client manager connection.
465 * Message to transmit, should be allocated with GNUNET_malloc() or
466 * GNUNET_new(), as it is freed with GNUNET_free() after transmission.
469 GNUNET_CLIENT_MANAGER_transmit_now (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
470 struct GNUNET_MessageHeader *msg)
472 struct MessageQueueItem *mqi = GNUNET_malloc (sizeof (*mqi));
474 GNUNET_CONTAINER_DLL_insert (mgr->tmit_head, mgr->tmit_tail, mqi);
480 * Drop all queued messages.
483 * Client manager connection.
486 GNUNET_CLIENT_MANAGER_drop_queue (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
488 struct MessageQueueItem *cur, *next = mgr->tmit_head;
493 GNUNET_free (cur->msg);
500 * Obtain client connection handle.
503 * Client manager connection.
505 * @return Client connection handle.
507 struct GNUNET_CLIENT_Connection *
508 GNUNET_CLIENT_MANAGER_get_client (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
515 * Return user context associated with the given client.
516 * Note: you should probably use the macro (call without the underscore).
519 * Client manager connection.
521 * Number of bytes in user context struct (for verification only).
523 * @return User context.
526 GNUNET_CLIENT_MANAGER_get_user_context_ (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
529 if ((0 == mgr->user_ctx_size) &&
530 (NULL == mgr->user_ctx))
531 return NULL; /* never set */
532 GNUNET_assert (size == mgr->user_ctx_size);
533 return mgr->user_ctx;
538 * Set user context to be associated with the given client.
539 * Note: you should probably use the macro (call without the underscore).
542 * Client manager connection.
546 * Number of bytes in user context struct (for verification only).
549 GNUNET_CLIENT_MANAGER_set_user_context_ (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
555 mgr->user_ctx_size = 0;
559 mgr->user_ctx_size = size;