-more vector -> scalar renaming
[oweals/gnunet.git] / src / scalarproduct / scalarproduct_api.c
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 2, 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 scalarproduct/scalarproduct_api.c
23  * @brief API for the scalarproduct
24  * @author Christian Fuchs
25  * @author Gaurav Kukreja
26  * 
27  */
28 #include "platform.h"
29 #include "gnunet_util_lib.h"
30 #include "gnunet_statistics_service.h"
31 #include "gnunet_scalarproduct_service.h"
32 #include "gnunet_protocols.h"
33
34 #define LOG(kind,...) GNUNET_log_from (kind, "scalarproduct-api",__VA_ARGS__)
35
36 /**************************************************************
37  ***  Datatype Declarations                          **********
38  **************************************************************/
39
40 /**
41  * Entry in the request queue per client
42  */
43 struct GNUNET_SCALARPRODUCT_QueueEntry
44 {
45   /**
46    * This is a linked list.
47    */
48   struct GNUNET_SCALARPRODUCT_QueueEntry *next;
49
50   /**
51    * This is a linked list.
52    */
53   struct GNUNET_SCALARPRODUCT_QueueEntry *prev;
54
55   /**
56    * Handle to the master context.
57    */
58   struct GNUNET_SCALARPRODUCT_Handle *h;
59
60   /**
61    * Size of the message
62    */
63   uint16_t message_size;
64
65   /**
66    * Message to be sent to the scalarproduct service
67    */
68   struct GNUNET_SCALARPRODUCT_client_request* msg;
69
70   union
71   {
72     /**
73      * Function to call after transmission of the request.
74      */
75     GNUNET_SCALARPRODUCT_ContinuationWithStatus cont_status;
76
77     /**
78      * Function to call after transmission of the request.
79      */
80     GNUNET_SCALARPRODUCT_DatumProcessor cont_datum;
81   };
82
83   /**
84    * Closure for 'cont'.
85    */
86   void *cont_cls;
87
88   /**
89    * Has this message been transmitted to the service?
90    * Only ever GNUNET_YES for the head of the queue.
91    * Note that the overall struct should end at a
92    * multiple of 64 bits.
93    */
94   int16_t was_transmitted;
95
96   /**
97    * Timeout for the current operation.
98    */
99   struct GNUNET_TIME_Absolute timeout;
100
101   /**
102    * Task for timeout signaling.
103    */
104   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
105
106   /**
107    * Response Processor for response from the service. This function calls the
108    * continuation function provided by the client.
109    */
110   GNUNET_SCALARPRODUCT_ResponseMessageHandler response_proc;
111 };
112
113 /**************************************************************
114  ***  Function Declarations                          **********
115  **************************************************************/
116
117 /**
118  * Creates a new entry at the tail of the DLL
119  * 
120  * @param h handle to the master context
121  * 
122  * @return pointer to the entry
123  */
124 static struct GNUNET_SCALARPRODUCT_QueueEntry *
125 make_queue_entry (struct GNUNET_SCALARPRODUCT_Handle *h);
126
127 /**
128  * Removes the head entry from the queue
129  * 
130  * @param h Handle to the master context
131  */
132 static struct GNUNET_SCALARPRODUCT_QueueEntry *
133 free_queue_head_entry (struct GNUNET_SCALARPRODUCT_Handle * h);
134
135 /**
136  * Triggered when timeout occurs for a request in queue
137  * 
138  * @param cls The pointer to the QueueEntry
139  * @param tc Task Context
140  */
141 static void
142 timeout_queue_entry (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
143
144 /**
145  * Called when a response is received from the service. After basic check
146  * handler in qe->response_proc is called. This functions handles the response
147  * to the client which used the API.
148  * 
149  * @param cls Pointer to the Master Context
150  * @param msg Pointer to the data received in response
151  */
152 static void
153 receive_cb (void *cls, const struct GNUNET_MessageHeader *msg);
154
155 /**
156  * Transmits the request to the VectorProduct Sevice
157  * 
158  * @param cls Closure
159  * @param size Size of the buffer
160  * @param buf Pointer to the buffer
161  * 
162  * @return Size of the message sent
163  */
164 static size_t transmit_request (void *cls, size_t size,
165                                 void *buf);
166
167 /**
168  * Issues transmit request for the new entries in the queue
169  * 
170  * @param h handle to the master context
171  */
172 static void
173 process_queue (struct GNUNET_SCALARPRODUCT_Handle *h);
174
175 /**************************************************************
176  ***  Static Function Declarations                   **********
177  **************************************************************/
178
179
180 /**
181  * Creates a new entry at the tail of the DLL
182  * 
183  * @param h handle to the master context
184  * 
185  * @return pointer to the entry
186  */
187 static struct GNUNET_SCALARPRODUCT_QueueEntry *
188 make_queue_entry (struct GNUNET_SCALARPRODUCT_Handle *h)
189 {
190   struct GNUNET_SCALARPRODUCT_QueueEntry *qe;
191
192   qe = GNUNET_new (struct GNUNET_SCALARPRODUCT_QueueEntry);
193
194   // if queue empty
195   if (NULL == h->queue_head && NULL == h->queue_tail)
196     {
197       qe->next = NULL;
198       qe->prev = NULL;
199       h->queue_head = qe;
200       h->queue_tail = qe;
201     }
202   else
203     {
204       qe->prev = h->queue_tail;
205       h->queue_tail->next = qe;
206       h->queue_tail = qe;
207     }
208
209   return qe;
210 }
211
212
213 /**
214  * Removes the head entry from the queue
215  * 
216  * @param h Handle to the master context
217  */
218 static struct GNUNET_SCALARPRODUCT_QueueEntry *
219 free_queue_head_entry (struct GNUNET_SCALARPRODUCT_Handle * h)
220 {
221   struct GNUNET_SCALARPRODUCT_QueueEntry * qe = NULL;
222
223   GNUNET_assert (NULL != h);
224   if (NULL == h->queue_head && NULL == h->queue_tail)
225     {
226       // The queue is empty. Just return.
227       LOG (GNUNET_ERROR_TYPE_DEBUG, "Queue was empty when free_queue_head_entry was called.\n");
228     }
229   else if (h->queue_head == h->queue_tail) //only one entry
230     {
231       qe = h->queue_head;
232       qe->next = NULL;
233       qe->prev = NULL;
234       h->queue_head = NULL;
235       h->queue_tail = NULL;
236     }
237   else
238     {
239       qe = h->queue_head;
240       h->queue_head = h->queue_head->next;
241       h->queue_head->prev = NULL;
242       qe->next = NULL;
243       qe->prev = NULL;
244     }
245   return qe;
246 }
247
248
249 /**
250  * Triggered when timeout occurs for a request in queue
251  * 
252  * @param cls The pointer to the QueueEntry
253  * @param tc Task Context
254  */
255 static void
256 timeout_queue_entry (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
257 {
258   struct GNUNET_SCALARPRODUCT_QueueEntry * qe = cls;
259
260   // Update Statistics
261   GNUNET_STATISTICS_update (qe->h->stats,
262                             gettext_noop ("# queue entry timeouts"), 1,
263                             GNUNET_NO);
264
265   // Clear the timeout_task
266   qe->timeout_task = GNUNET_SCHEDULER_NO_TASK;
267
268   // transmit_request is supposed to cancel timeout task.
269   // If message was not transmitted, there is definitely an error.
270   GNUNET_assert (GNUNET_NO == qe->was_transmitted);
271
272   LOG (GNUNET_ERROR_TYPE_INFO, "Timeout of request in datastore queue\n");
273
274   // remove the queue_entry for the queue
275   GNUNET_CONTAINER_DLL_remove (qe->h->queue_head, qe->h->queue_tail, qe);
276   qe->response_proc (qe, NULL, GNUNET_SCALARPRODUCT_Status_Timeout);
277 }
278
279
280 /**
281  * Handles the RESULT received in reply of prepare_response from the 
282  * service
283  * 
284  * @param cls Handle to the Master Context
285  * @param msg Pointer to the response received
286  */
287 static void
288 process_status_message (void *cls,
289                         const struct GNUNET_MessageHeader *msg,
290                         enum GNUNET_SCALARPRODUCT_ResponseStatus status)
291 {
292   struct GNUNET_SCALARPRODUCT_QueueEntry *qe = cls;
293
294   GNUNET_assert (qe != NULL);
295
296   if (qe->cont_status != NULL)
297     qe->cont_status (qe->cont_cls, &qe->msg->key, status);
298 }
299
300
301 /**
302  * Handles the RESULT received in reply of prepare_response from the 
303  * service
304  * 
305  * @param cls Handle to the Master Context
306  * @param msg Pointer to the response received
307  */
308 static void
309 process_result_message (void *cls,
310                         const struct GNUNET_MessageHeader *msg,
311                         enum GNUNET_SCALARPRODUCT_ResponseStatus status)
312 {
313   struct GNUNET_SCALARPRODUCT_QueueEntry *qe = cls;
314
315   GNUNET_assert (qe != NULL);
316
317   if (msg == NULL && qe->cont_datum != NULL)
318     {
319       LOG (GNUNET_ERROR_TYPE_DEBUG, "Timeout reached or session terminated.\n");
320     }
321   if (qe->cont_datum != NULL)
322     {
323       qe->cont_datum (qe->cont_cls, &qe->msg->key, &qe->msg->peer, status, (struct GNUNET_SCALARPRODUCT_client_response *) msg);
324     }
325 }
326
327
328 /**
329  * Called when a response is received from the service. After basic check
330  * handler in qe->response_proc is called. This functions handles the response
331  * to the client which used the API.
332  * 
333  * @param cls Pointer to the Master Context
334  * @param msg Pointer to the data received in response
335  */
336 static void
337 receive_cb (void *cls, const struct GNUNET_MessageHeader *msg)
338 {
339   struct GNUNET_SCALARPRODUCT_Handle *h = cls;
340   struct GNUNET_SCALARPRODUCT_QueueEntry *qe;
341   int16_t was_transmitted;
342   struct GNUNET_SCALARPRODUCT_client_response *message =
343           (struct GNUNET_SCALARPRODUCT_client_response *) msg;
344
345   h->in_receive = GNUNET_NO;
346   LOG (GNUNET_ERROR_TYPE_DEBUG, "Received reply from VectorProduct\n");
347
348   if (NULL == (qe = free_queue_head_entry (h)))
349     {
350       /**
351        * The queue head will be NULL if the client disconnected,
352        * * In case of Alice, client disconnected after sending request, before receiving response
353        * * In case of Bob, client disconnected after preparing response, before getting request from Alice.
354        */
355       process_queue (h);
356       return;
357     }
358
359   if (h->client == NULL)
360     {
361       // GKUKREJA : handle this correctly
362       /**
363        * The queue head will be NULL if the client disconnected,
364        * * In case of Alice, client disconnected after sending request, before receiving response
365        * * In case of Bob, client disconnected after preparing response, before getting request from Alice.
366        */
367       process_queue (h);
368       return;
369     }
370
371   was_transmitted = qe->was_transmitted;
372   // Control will only come here, when the request was transmitted to service,
373   // and service responded.
374   GNUNET_assert (was_transmitted == GNUNET_YES);
375
376   if (msg == NULL)
377     {
378       LOG (GNUNET_ERROR_TYPE_WARNING, "Service responded with NULL!\n");
379       qe->response_proc (qe, NULL, GNUNET_SCALARPRODUCT_Status_Failure);
380     }
381   else if ((ntohs (msg->type) != GNUNET_MESSAGE_TYPE_SCALARPRODUCT_SERVICE_TO_CLIENT))
382     {
383       LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid Message Received\n");
384       qe->response_proc (qe, msg, GNUNET_SCALARPRODUCT_Status_InvalidResponse);
385     }
386   else if (ntohl (message->product_length) == 0)
387     {
388       // response for the responder client, successful
389       GNUNET_STATISTICS_update (h->stats,
390                                 gettext_noop ("# SUC responder result messages received"), 1,
391                                 GNUNET_NO);
392
393       LOG (GNUNET_ERROR_TYPE_DEBUG, "Received message from service without product attached.\n");
394       qe->response_proc (qe, msg, GNUNET_SCALARPRODUCT_Status_Success);
395     }
396   else if (ntohl (message->product_length) > 0)
397     {
398       // response for the requester client, successful
399       GNUNET_STATISTICS_update (h->stats,
400                                 gettext_noop ("# SUC requester result messages received"), 1,
401                                 GNUNET_NO);
402
403       LOG (GNUNET_ERROR_TYPE_DEBUG, "Received message from requester service for requester client.\n");
404       qe->response_proc (qe, msg, GNUNET_SCALARPRODUCT_Status_Success);
405     }
406
407   GNUNET_free (qe);
408   process_queue (h);
409 }
410
411
412 /**
413  * Transmits the request to the VectorProduct Sevice
414  * 
415  * @param cls Closure
416  * @param size Size of the buffer
417  * @param buf Pointer to the buffer
418  * 
419  * @return Size of the message sent
420  */
421 static size_t
422 transmit_request (void *cls, size_t size,
423                   void *buf)
424 {
425   struct GNUNET_SCALARPRODUCT_Handle *h = cls;
426   struct GNUNET_SCALARPRODUCT_QueueEntry *qe;
427   size_t msize;
428   
429   if (NULL == (qe = h->queue_head))
430     {
431       LOG (GNUNET_ERROR_TYPE_DEBUG, "Queue head is NULL!\n\n");
432       return 0;
433     }
434
435   GNUNET_SCHEDULER_cancel (qe->timeout_task);
436   qe->timeout_task = GNUNET_SCHEDULER_NO_TASK;
437
438   h->th = NULL;
439   if (NULL == (qe = h->queue_head))
440     return 0; /* no entry in queue */
441   if (buf == NULL)
442     {
443       LOG (GNUNET_ERROR_TYPE_DEBUG, "Failed to transmit request to SCALARPRODUCT.\n");
444       GNUNET_STATISTICS_update (h->stats,
445                                 gettext_noop ("# transmission request failures"),
446                                 1, GNUNET_NO);
447       GNUNET_SCALARPRODUCT_disconnect (h);
448       return 0;
449     }
450   if (size < (msize = qe->message_size))
451     {
452       process_queue (h);
453       return 0;
454     }
455   LOG (GNUNET_ERROR_TYPE_DEBUG, "Transmitting %u byte request to SCALARPRODUCT\n",
456        msize);
457
458   memcpy (buf, qe->msg, size);
459   GNUNET_free (qe->msg);
460   qe->was_transmitted = GNUNET_YES;
461
462   GNUNET_assert (GNUNET_NO == h->in_receive);
463   h->in_receive = GNUNET_YES;
464
465   GNUNET_CLIENT_receive (h->client, &receive_cb, h,
466                          GNUNET_TIME_UNIT_FOREVER_REL);
467
468 #if INSANE_STATISTICS
469   GNUNET_STATISTICS_update (h->stats,
470                             gettext_noop ("# bytes sent to scalarproduct"), 1,
471                             GNUNET_NO);
472 #endif
473   return size;
474 }
475
476
477 /**
478  * Issues transmit request for the new entries in the queue
479  * 
480  * @param h handle to the master context
481  */
482 static void
483 process_queue (struct GNUNET_SCALARPRODUCT_Handle *h)
484 {
485   struct GNUNET_SCALARPRODUCT_QueueEntry *qe;
486   
487   if (NULL == (qe = h->queue_head))
488     {
489       LOG (GNUNET_ERROR_TYPE_DEBUG, "Queue empty\n");
490       return; /* no entry in queue */
491     }
492   if (qe->was_transmitted == GNUNET_YES)
493     {
494       LOG (GNUNET_ERROR_TYPE_DEBUG, "Head request already transmitted\n");
495       return; /* waiting for replies */
496     }
497   if (h->th != NULL)
498     {
499       LOG (GNUNET_ERROR_TYPE_DEBUG, "Pending transmission request\n");
500       return; /* request pending */
501     }
502   if (h->client == NULL)
503     {
504       LOG (GNUNET_ERROR_TYPE_DEBUG, "Not connected\n");
505       return; /* waiting for reconnect */
506     }
507   if (GNUNET_YES == h->in_receive)
508     {
509       /* wait for response to previous query */
510       return;
511     }
512
513   h->th =
514           GNUNET_CLIENT_notify_transmit_ready (h->client, qe->message_size,
515                                                GNUNET_TIME_UNIT_FOREVER_REL,
516                                                GNUNET_YES,
517                                                &transmit_request, h);
518
519   if (h->th == NULL)
520     {
521       LOG (GNUNET_ERROR_TYPE_ERROR,
522            _ ("Failed to send a message to the scalarproduct service\n"));
523       return;
524     }
525
526   GNUNET_assert (GNUNET_NO == h->in_receive);
527   GNUNET_break (NULL != h->th);
528 }
529
530
531
532 /**************************************************************
533  ***  API                                            **********
534  **************************************************************/
535
536
537 /**
538  * Called by the responder client to prepare response
539  * 
540  * @param h handle to the master context
541  * @param key Session key - unique to the requesting client
542  * @param element_count Number of elements in the vector
543  * @param mask_length number of bytes in the mask
544  * @param elements Array of elements of the vector
545  * @param mask Array of the mask
546  * @param timeout Relative timeout for the operation
547  * @param cont Callback function
548  * @param cont_cls Closure for the callback function
549  */
550 struct GNUNET_SCALARPRODUCT_QueueEntry *
551 GNUNET_SCALARPRODUCT_prepare_response (struct GNUNET_SCALARPRODUCT_Handle *h,
552                                        const struct GNUNET_HashCode * key,
553                                        uint16_t element_count,
554                                        int32_t * elements,
555                                        struct GNUNET_TIME_Relative timeout,
556                                        GNUNET_SCALARPRODUCT_ContinuationWithStatus cont,
557                                        void *cont_cls)
558 {
559   struct GNUNET_SCALARPRODUCT_QueueEntry *qe = make_queue_entry (h);
560   int32_t * vector;
561   uint16_t size;
562   unsigned int i;
563   
564   GNUNET_assert (GNUNET_SERVER_MAX_MESSAGE_SIZE >= sizeof (struct GNUNET_SCALARPRODUCT_client_request)
565                  +element_count * sizeof (int32_t));
566   size = sizeof (struct GNUNET_SCALARPRODUCT_client_request) +element_count * sizeof (int32_t);
567
568   qe->message_size = size;
569   qe->msg = GNUNET_malloc (size);
570   qe->msg->header.size = htons (size);
571   qe->msg->header.type = htons (GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_BOB);
572   qe->msg->element_count = htons (element_count);
573   qe->msg->mask_length = htons (0);
574   memcpy (&qe->msg->key, key, sizeof (struct GNUNET_HashCode));
575   qe->cont_status = cont;
576   qe->cont_cls = cont_cls;
577   qe->was_transmitted = GNUNET_NO;
578   qe->timeout_task = GNUNET_SCHEDULER_add_delayed (timeout, &timeout_queue_entry, qe);
579   qe->response_proc = &process_status_message;
580   qe->timeout = GNUNET_TIME_relative_to_absolute (timeout);
581
582   vector = (int32_t *) & qe->msg[1];
583   // copy each element over to the message
584   for (i = 0; i < element_count; i++)
585     vector[i] = htonl (elements[i]);
586
587   process_queue (h);
588   return qe;
589 }
590
591
592 /**
593  * Request the Scalar Product Evaluation
594  * 
595  * @param h handle to the master context
596  * @param key Session key - unique to the requesting client
597  * @param peer PeerID of the other peer
598  * @param element_count Number of elements in the vector
599  * @param mask_length number of bytes in the mask
600  * @param elements Array of elements of the vector
601  * @param mask Array of the mask
602  * @param timeout Relative timeout for the operation
603  * @param cont Callback function
604  * @param cont_cls Closure for the callback function
605  */
606 struct GNUNET_SCALARPRODUCT_QueueEntry *
607 GNUNET_SCALARPRODUCT_request (struct GNUNET_SCALARPRODUCT_Handle *h,
608                               const struct GNUNET_HashCode * key,
609                               const struct GNUNET_PeerIdentity * peer,
610                               uint16_t element_count,
611                               uint16_t mask_length,
612                               int32_t * elements,
613                               const unsigned char * mask,
614                               struct GNUNET_TIME_Relative timeout,
615                               GNUNET_SCALARPRODUCT_DatumProcessor cont,
616                               void *cont_cls)
617 {
618   struct GNUNET_SCALARPRODUCT_QueueEntry *qe = make_queue_entry (h);
619   int32_t * vector;
620   uint16_t size;
621   unsigned int i;
622   
623   GNUNET_assert (GNUNET_SERVER_MAX_MESSAGE_SIZE >= sizeof (struct GNUNET_SCALARPRODUCT_client_request)
624                  +element_count * sizeof (int32_t)
625                  + mask_length);
626   size = sizeof (struct GNUNET_SCALARPRODUCT_client_request) +element_count * sizeof (int32_t) + mask_length;
627
628   qe->message_size = size;
629   qe->msg = GNUNET_malloc (size);
630   qe->msg->header.size = htons (size);
631   qe->msg->header.type = htons (GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_ALICE);
632   memcpy (&qe->msg->peer, peer, sizeof (struct GNUNET_PeerIdentity));
633   qe->msg->element_count = htons (element_count);
634   qe->msg->mask_length = htons (mask_length);
635   memcpy (&qe->msg->key, key, sizeof (struct GNUNET_HashCode));
636   qe->cont_datum = cont;
637   qe->cont_cls = cont_cls;
638   qe->was_transmitted = GNUNET_NO;
639   qe->timeout_task = GNUNET_SCHEDULER_add_delayed (timeout, &timeout_queue_entry, qe);
640   qe->response_proc = &process_result_message;
641   qe->timeout = GNUNET_TIME_relative_to_absolute (timeout);
642
643   vector = (int32_t*) & qe->msg[1];
644   // copy each element over to the message
645   for (i = 0; i < element_count; i++)
646     vector[i] = htonl (elements[i]);
647
648   // fill in the mask
649   memcpy (&vector[element_count], mask, mask_length);
650
651   process_queue (h);
652   return qe;
653 }
654
655
656 /**
657  * Connect to the scalarproduct service.
658  *
659  * @param cfg configuration to use
660  * @return handle to use to access the service
661  */
662 struct GNUNET_SCALARPRODUCT_Handle *
663 GNUNET_SCALARPRODUCT_connect (const struct GNUNET_CONFIGURATION_Handle * cfg)
664 {
665   struct GNUNET_CLIENT_Connection *client;
666   struct GNUNET_SCALARPRODUCT_Handle *h;
667
668   client = GNUNET_CLIENT_connect ("scalarproduct", cfg);
669
670   if (NULL == client)
671     {
672       LOG (GNUNET_ERROR_TYPE_ERROR,
673            _ ("Failed to connect to the scalarproduct service\n"));
674       return NULL;
675     }
676
677   h = GNUNET_malloc (sizeof (struct GNUNET_SCALARPRODUCT_Handle) +
678                      GNUNET_SERVER_MAX_MESSAGE_SIZE - 1);
679   h->client = client;
680   h->cfg = cfg;
681   h->stats = GNUNET_STATISTICS_create ("scalarproduct-api", cfg);
682   return h;
683 }
684
685
686 /**
687  * Disconnect from the scalarproduct service.
688  * 
689  * @param h handle to the scalarproduct
690  */
691 void
692 GNUNET_SCALARPRODUCT_disconnect (struct GNUNET_SCALARPRODUCT_Handle * h)
693 {
694   struct GNUNET_SCALARPRODUCT_QueueEntry * qe;
695
696   LOG (GNUNET_ERROR_TYPE_INFO,
697        "Disconnecting from VectorProduct\n");
698
699   while (NULL != h->queue_head)
700     {
701       GNUNET_assert (NULL != (qe = free_queue_head_entry (h)));
702       qe->response_proc (qe, NULL, GNUNET_SCALARPRODUCT_Status_ServiceDisconnected);
703     }
704
705   if (h->client != NULL)
706     {
707       GNUNET_CLIENT_disconnect (h->client);
708       h->client = NULL;
709     }
710
711   GNUNET_STATISTICS_destroy (h->stats, GNUNET_NO);
712   h->stats = NULL;
713 }
714
715 /* end of ext_api.c */