aboutsummaryrefslogtreecommitdiff
path: root/src/util
diff options
context:
space:
mode:
authorGabor X Toth <*@tg-x.net>2014-05-29 16:35:45 +0000
committerGabor X Toth <*@tg-x.net>2014-05-29 16:35:45 +0000
commit0a3564262a17b8e0dddb553c31bb783c99ee1ec1 (patch)
tree0297bfdcd4b51779a5eb7a63b1d9950098cc1efd /src/util
parentf7c240021dc6f06e3e143713261305b127f20735 (diff)
downloadgnunet-0a3564262a17b8e0dddb553c31bb783c99ee1ec1.tar.gz
gnunet-0a3564262a17b8e0dddb553c31bb783c99ee1ec1.zip
client manager: higher level client API with a transmission queue and message handler registration
Diffstat (limited to 'src/util')
-rw-r--r--src/util/Makefile.am1
-rw-r--r--src/util/client_manager.c496
2 files changed, 497 insertions, 0 deletions
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
index a33d6f63d..c8c760757 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -73,6 +73,7 @@ libgnunetutil_la_SOURCES = \
73 bandwidth.c \ 73 bandwidth.c \
74 bio.c \ 74 bio.c \
75 client.c \ 75 client.c \
76 client_manager.c \
76 common_allocation.c \ 77 common_allocation.c \
77 common_endian.c \ 78 common_endian.c \
78 common_logging.c \ 79 common_logging.c \
diff --git a/src/util/client_manager.c b/src/util/client_manager.c
new file mode 100644
index 000000000..c415ce845
--- /dev/null
+++ b/src/util/client_manager.c
@@ -0,0 +1,496 @@
1/*
2 This file is part of GNUnet.
3 (C) 2013 Christian Grothoff (and other contributing authors)
4
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.
9
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.
14
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.
19*/
20
21/**
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
26 */
27
28#include <inttypes.h>
29
30#include "platform.h"
31#include "gnunet_util_lib.h"
32
33#define LOG(kind,...) GNUNET_log_from (kind, "util",__VA_ARGS__)
34
35
36/**
37 * List of arrays of message handlers.
38 */
39struct HandlersListItem
40{
41 struct HandlersListItem *prev;
42 struct HandlersListItem *next;
43
44 /**
45 * NULL-terminated array of handlers.
46 */
47 const struct GNUNET_CLIENT_MANAGER_MessageHandler *handlers;
48};
49
50
51struct MessageQueueItem
52{
53 struct MessageQueueItem *prev;
54 struct MessageQueueItem *next;
55 struct GNUNET_MessageHeader *msg;
56};
57
58
59struct GNUNET_CLIENT_MANAGER_Connection
60{
61 /**
62 * Configuration to use.
63 */
64 const struct GNUNET_CONFIGURATION_Handle *cfg;
65
66 /**
67 * Client connection to service.
68 */
69 struct GNUNET_CLIENT_Connection *client;
70
71 /**
72 * Currently pending transmission request, or NULL for none.
73 */
74 struct GNUNET_CLIENT_TransmitHandle *client_tmit;
75
76 /**
77 * Service name to connect to.
78 */
79 const char *service_name;
80
81 /**
82 * Head of messages to transmit to the service.
83 */
84 struct MessageQueueItem *tmit_head;
85
86 /**
87 * Tail of messages to transmit to the service.
88 */
89 struct MessageQueueItem *tmit_tail;
90
91 /**
92 * Message handlers.
93 */
94 const struct GNUNET_CLIENT_MANAGER_MessageHandler *handlers;
95
96 /**
97 * User context value.
98 * @see GNUNET_CLIENT_MANAGER_set_user_context()
99 * @see GNUNET_CLIENT_MANAGER_get_user_context()
100 */
101 void *user_ctx;
102
103 /**
104 * Last size given when user context was initialized.
105 * Used for sanity check.
106 */
107 size_t user_ctx_size;
108
109 /**
110 * Task doing exponential back-off trying to reconnect.
111 */
112 GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
113
114 /**
115 * Time for next connect retry.
116 */
117 struct GNUNET_TIME_Relative reconnect_delay;
118
119 /**
120 * Are we currently polling for incoming messages?
121 */
122 uint8_t in_receive;
123
124 /**
125 * #GNUNET_YES if GNUNET_CLIENT_MANAGER_disconnect() was called
126 * and we're transmitting the last messages from the queue.
127 */
128 uint8_t disconnecting;
129};
130
131
132/**
133 * Handle received messages from the service.
134 */
135static void
136recv_message (void *cls, const struct GNUNET_MessageHeader *msg)
137{
138 struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
139 uint16_t type = 0, size = 0;
140
141 if (NULL != msg)
142 {
143 type = ntohs (msg->type);
144 size = ntohs (msg->size);
145 /* FIXME: decrease reconnect_delay gradually after a successful reconnection */
146 }
147
148 size_t i = 0;
149 while (NULL != mgr->handlers[i].callback)
150 {
151 const struct GNUNET_CLIENT_MANAGER_MessageHandler *mh = &mgr->handlers[i];
152 if ((mh->type == type) || (mh->type == GNUNET_MESSAGE_TYPE_ALL))
153 {
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)))
157 {
158 LOG (GNUNET_ERROR_TYPE_ERROR,
159 "Expected %u bytes for message of type %u, got %u.\n",
160 mh->expected_size, type, size);
161 GNUNET_break_op (0);
162 GNUNET_CLIENT_disconnect (mgr->client);
163 mgr->client = NULL;
164 recv_message (mgr, NULL);
165 break;
166 }
167 mh->callback (mh->callback_cls, mgr, msg);
168 }
169 i++;
170 }
171 if (NULL != mgr->client)
172 {
173 GNUNET_CLIENT_receive (mgr->client, &recv_message, mgr,
174 GNUNET_TIME_UNIT_FOREVER_REL);
175 }
176}
177
178
179/**
180 * Schedule transmission of the next message from our queue.
181 *
182 * @param mgr Client manager connection.
183 */
184static void
185transmit_next (struct GNUNET_CLIENT_MANAGER_Connection *mgr);
186
187
188/**
189 * Transmit next message to service.
190 *
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.
194 *
195 * @return Number of bytes copied to @a buf.
196 */
197static size_t
198send_next_message (void *cls, size_t buf_size, void *buf)
199{
200 LOG (GNUNET_ERROR_TYPE_DEBUG, "send_next_message()\n");
201 struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
202
203 if (NULL == buf)
204 {
205 /* disconnected */
206 recv_message (mgr, NULL);
207 return 0;
208 }
209
210 struct MessageQueueItem *mqi = mgr->tmit_head;
211 if (NULL == mqi)
212 return 0;
213
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);
218
219 GNUNET_CONTAINER_DLL_remove (mgr->tmit_head, mgr->tmit_tail, mqi);
220 GNUNET_free (mqi->msg);
221 GNUNET_free (mqi);
222
223 if (NULL != mgr->tmit_head)
224 transmit_next (mgr);
225
226 if (GNUNET_NO == mgr->in_receive)
227 {
228 mgr->in_receive = GNUNET_YES;
229 GNUNET_CLIENT_receive (mgr->client, &recv_message, mgr,
230 GNUNET_TIME_UNIT_FOREVER_REL);
231 }
232 return size;
233}
234
235
236/**
237 * Schedule transmission of the next message from our queue.
238 *
239 * @param mgr Client manager connection.
240 */
241static void
242transmit_next (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
243{
244 LOG (GNUNET_ERROR_TYPE_DEBUG, "transmit_next()\n");
245 if (NULL != mgr->client_tmit || NULL == mgr->client)
246 return;
247
248 if (NULL == mgr->tmit_head)
249 {
250 if (GNUNET_YES == mgr->disconnecting)
251 GNUNET_CLIENT_MANAGER_disconnect (mgr, GNUNET_NO);
252 return;
253 }
254
255 mgr->client_tmit
256 = GNUNET_CLIENT_notify_transmit_ready (mgr->client,
257 ntohs (mgr->tmit_head->msg->size),
258 GNUNET_TIME_UNIT_FOREVER_REL,
259 GNUNET_NO,
260 &send_next_message,
261 mgr);
262}
263
264
265/**
266 * Try again to connect to the service.
267 *
268 * @param cls Channel handle.
269 * @param tc Scheduler context.
270 */
271static void
272reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
273{
274 struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
275 mgr->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
276
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);
282
283 transmit_next (mgr);
284}
285
286
287/**
288 * Connect to service.
289 *
290 * @param cfg Configuration to use.
291 * @param service_name Service name to connect to.
292 * @param handlers Message handlers.
293 *
294 * @return Client manager connection handle.
295 */
296struct GNUNET_CLIENT_MANAGER_Connection *
297GNUNET_CLIENT_MANAGER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
298 const char *service_name,
299 const struct
300 GNUNET_CLIENT_MANAGER_MessageHandler *handlers)
301{
302 struct GNUNET_CLIENT_MANAGER_Connection *
303 mgr = GNUNET_malloc (sizeof (*mgr));
304 mgr->cfg = cfg;
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);
309 return mgr;
310}
311
312
313/**
314 * Disconnect from the service.
315 *
316 * @param mgr Client manager connection.
317 * @param transmit_queue Transmit pending messages in queue before disconnecting.
318 */
319void
320GNUNET_CLIENT_MANAGER_disconnect (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
321 int transmit_queue)
322{
323 if (NULL != mgr->tmit_head)
324 {
325 if (GNUNET_YES == transmit_queue)
326 {
327 mgr->disconnecting = GNUNET_YES;
328 transmit_next (mgr);
329 }
330 else
331 {
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);
336 }
337 }
338 if (mgr->reconnect_task != GNUNET_SCHEDULER_NO_TASK)
339 {
340 GNUNET_SCHEDULER_cancel (mgr->reconnect_task);
341 mgr->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
342 }
343 if (NULL != mgr->client_tmit)
344 {
345 GNUNET_CLIENT_notify_transmit_ready_cancel (mgr->client_tmit);
346 mgr->client_tmit = NULL;
347 }
348 if (NULL != mgr->client)
349 {
350 GNUNET_CLIENT_disconnect (mgr->client);
351 mgr->client = NULL;
352 }
353 GNUNET_free (mgr);
354}
355
356
357/**
358 * Reschedule connect to the service using exponential back-off.
359 *
360 * @param mgr Client manager connection.
361 */
362void
363GNUNET_CLIENT_MANAGER_reconnect (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
364{
365 if (GNUNET_SCHEDULER_NO_TASK != mgr->reconnect_task)
366 return;
367
368 if (NULL != mgr->client_tmit)
369 {
370 GNUNET_CLIENT_notify_transmit_ready_cancel (mgr->client_tmit);
371 mgr->client_tmit = NULL;
372 }
373 if (NULL != mgr->client)
374 {
375 GNUNET_CLIENT_disconnect (mgr->client);
376 mgr->client = NULL;
377 }
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);
385}
386
387
388/**
389 * Add a message to the end of the transmission queue.
390 *
391 * @param mgr Client manager connection.
392 * @param msg Message to transmit. It is free()'d after transmission.
393 */
394void
395GNUNET_CLIENT_MANAGER_transmit (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
396 struct GNUNET_MessageHeader *msg)
397{
398 struct MessageQueueItem *mqi = GNUNET_malloc (sizeof (*mqi));
399 mqi->msg = msg;
400 GNUNET_CONTAINER_DLL_insert_tail (mgr->tmit_head, mgr->tmit_tail, mqi);
401 transmit_next (mgr);
402}
403
404
405/**
406 * Add a message to the beginning of the transmission queue.
407 *
408 * @param mgr Client manager connection.
409 * @param msg Message to transmit. It is free()'d after transmission.
410 */
411void
412GNUNET_CLIENT_MANAGER_transmit_now (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
413 struct GNUNET_MessageHeader *msg)
414{
415 struct MessageQueueItem *mqi = GNUNET_malloc (sizeof (*mqi));
416 mqi->msg = msg;
417 GNUNET_CONTAINER_DLL_insert (mgr->tmit_head, mgr->tmit_tail, mqi);
418 transmit_next (mgr);
419}
420
421
422/**
423 * Drop all queued messages.
424 *
425 * @param mgr Client manager connection.
426 */
427void
428GNUNET_CLIENT_MANAGER_drop_queue (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
429{
430 struct MessageQueueItem *cur, *next = mgr->tmit_head;
431 while (NULL != next)
432 {
433 cur = next;
434 next = cur->next;
435 GNUNET_free (cur->msg);
436 GNUNET_free (cur);
437 }
438}
439
440
441/**
442 * Obtain client connection handle.
443 *
444 * @param mgr Client manager connection handle.
445 *
446 * @return Client connection handle.
447 */
448struct GNUNET_CLIENT_Connection *
449GNUNET_CLIENT_MANAGER_get_client (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
450{
451 return mgr->client;
452}
453
454
455/**
456 * Return user context associated with the given client.
457 * Note: you should probably use the macro (call without the underscore).
458 *
459 * @param mgr Client manager connection.
460 * @param size Number of bytes in user context struct (for verification only).
461 * @return User context.
462 */
463void *
464GNUNET_CLIENT_MANAGER_get_user_context_ (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
465 size_t size)
466{
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;
472}
473
474
475/**
476 * Set user context to be associated with the given client.
477 * Note: you should probably use the macro (call without the underscore).
478 *
479 * @param mgr Client manager connection.
480 * @param ctx User context.
481 * @param size Number of bytes in user context struct (for verification only).
482 */
483void
484GNUNET_CLIENT_MANAGER_set_user_context_ (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
485 void *ctx,
486 size_t size)
487{
488 if (NULL == ctx)
489 {
490 mgr->user_ctx_size = 0;
491 mgr->user_ctx = ctx;
492 return;
493 }
494 mgr->user_ctx_size = size;
495 mgr->user_ctx = ctx;
496}