020fd86db0fb6bd841ffaf5b8d6895777ad99a9e
[oweals/gnunet.git] / src / util / server_tc.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009 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 util/server_tc.c
23  * @brief convenience functions for transmission of
24  *        complex responses as a server
25  * @author Christian Grothoff
26  */
27
28 #include "platform.h"
29 #include "gnunet_common.h"
30 #include "gnunet_connection_lib.h"
31 #include "gnunet_scheduler_lib.h"
32 #include "gnunet_server_lib.h"
33 #include "gnunet_time_lib.h"
34
35
36 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
37
38
39 /**
40  * How much buffer space do we want to have at least
41  * before transmitting another increment?
42  */
43 #define MIN_BLOCK_SIZE 128
44
45
46
47 struct GNUNET_SERVER_TransmitContext
48 {
49   /**
50    * Which client are we transmitting to?
51    */
52   struct GNUNET_SERVER_Client *client;
53
54   /**
55    * Transmission buffer. (current offset for writing).
56    */
57   char *buf;
58
59   /**
60    * Number of bytes in buf.
61    */
62   size_t total;
63
64   /**
65    * Offset for writing in buf.
66    */
67   size_t off;
68
69   /**
70    * Timeout for this request.
71    */
72   struct GNUNET_TIME_Absolute timeout;
73 };
74
75
76 /**
77  * Helper function for incremental transmission of the response.
78  */
79 static size_t
80 transmit_response (void *cls, size_t size, void *buf)
81 {
82   struct GNUNET_SERVER_TransmitContext *tc = cls;
83   size_t msize;
84
85   if (buf == NULL)
86     {
87       GNUNET_SERVER_transmit_context_destroy (tc, GNUNET_SYSERR);
88       return 0;
89     }
90   if (tc->total - tc->off > size)
91     msize = size;
92   else
93     msize = tc->total - tc->off;
94   memcpy (buf, &tc->buf[tc->off], msize);
95   tc->off += msize;
96   if (tc->total == tc->off)
97     {
98
99       GNUNET_SERVER_receive_done (tc->client, GNUNET_OK);
100       GNUNET_SERVER_client_drop (tc->client);
101       GNUNET_free_non_null (tc->buf);
102       GNUNET_free (tc);
103     }
104   else
105     {
106       if (NULL ==
107           GNUNET_SERVER_notify_transmit_ready (tc->client,
108                                                GNUNET_MIN (MIN_BLOCK_SIZE,
109                                                            tc->total -
110                                                            tc->off),
111                                                GNUNET_TIME_absolute_get_remaining
112                                                (tc->timeout),
113                                                &transmit_response, tc))
114         {
115           GNUNET_break (0);
116           GNUNET_SERVER_transmit_context_destroy (tc, GNUNET_SYSERR);
117         }
118     }
119   return msize;
120 }
121
122
123 /**
124  * Create a new transmission context for the
125  * given client.
126  *
127  * @param client client to create the context for.
128  * @return NULL on error
129  */
130 struct GNUNET_SERVER_TransmitContext *
131 GNUNET_SERVER_transmit_context_create (struct GNUNET_SERVER_Client *client)
132 {
133   struct GNUNET_SERVER_TransmitContext *tc;
134
135   GNUNET_assert (client != NULL);
136   tc = GNUNET_malloc (sizeof (struct GNUNET_SERVER_TransmitContext));
137   GNUNET_SERVER_client_keep (client);
138   tc->client = client;
139   return tc;
140 }
141
142
143 /**
144  * Append a message to the transmission context.
145  * All messages in the context will be sent by
146  * the transmit_context_run method.
147  *
148  * @param tc context to use
149  * @param data what to append to the result message
150  * @param length length of data
151  * @param type type of the message
152  */
153 void
154 GNUNET_SERVER_transmit_context_append_data (struct
155                                             GNUNET_SERVER_TransmitContext *tc,
156                                             const void *data, size_t length,
157                                             uint16_t type)
158 {
159   struct GNUNET_MessageHeader *msg;
160   size_t size;
161
162   GNUNET_assert (length < GNUNET_SERVER_MAX_MESSAGE_SIZE);
163   size = length + sizeof (struct GNUNET_MessageHeader);
164   GNUNET_assert (size > length);
165   tc->buf = GNUNET_realloc (tc->buf, tc->total + size);
166   msg = (struct GNUNET_MessageHeader *) &tc->buf[tc->total];
167   tc->total += size;
168   msg->size = htons (size);
169   msg->type = htons (type);
170   memcpy (&msg[1], data, length);
171 }
172
173
174 /**
175  * Append a message to the transmission context.
176  * All messages in the context will be sent by
177  * the transmit_context_run method.
178  *
179  * @param tc context to use
180  * @param msg message to append
181  */
182 void
183 GNUNET_SERVER_transmit_context_append_message (struct
184                                                GNUNET_SERVER_TransmitContext
185                                                *tc,
186                                                const struct
187                                                GNUNET_MessageHeader *msg)
188 {
189   struct GNUNET_MessageHeader *m;
190   uint16_t size;
191
192   size = ntohs (msg->size);
193   tc->buf = GNUNET_realloc (tc->buf, tc->total + size);
194   m = (struct GNUNET_MessageHeader *) &tc->buf[tc->total];
195   tc->total += size;
196   memcpy (m, msg, size);
197 }
198
199
200 /**
201  * Execute a transmission context.  If there is
202  * an error in the transmission, the receive_done
203  * method will be called with an error code (GNUNET_SYSERR),
204  * otherwise with GNUNET_OK.
205  *
206  * @param tc transmission context to use
207  * @param timeout when to time out and abort the transmission
208  */
209 void
210 GNUNET_SERVER_transmit_context_run (struct GNUNET_SERVER_TransmitContext *tc,
211                                     struct GNUNET_TIME_Relative timeout)
212 {
213   tc->timeout = GNUNET_TIME_relative_to_absolute (timeout);
214   if (NULL ==
215       GNUNET_SERVER_notify_transmit_ready (tc->client,
216                                            GNUNET_MIN (MIN_BLOCK_SIZE,
217                                                        tc->total), timeout,
218                                            &transmit_response, tc))
219     {
220       GNUNET_break (0);
221       GNUNET_SERVER_transmit_context_destroy (tc, GNUNET_SYSERR);
222     }
223 }
224
225
226 /**
227  * Destroy a transmission context. This function must not be called
228  * after 'GNUNET_SERVER_transmit_context_run'.
229  *
230  * @param tc transmission context to destroy
231  * @param success code to give to 'GNUNET_SERVER_receive_done' for
232  *        the client:  GNUNET_OK to keep the connection open and
233  *                          continue to receive
234  *                GNUNET_NO to close the connection (normal behavior)
235  *                GNUNET_SYSERR to close the connection (signal
236  *                          serious error)
237  */
238 void
239 GNUNET_SERVER_transmit_context_destroy (struct GNUNET_SERVER_TransmitContext
240                                         *tc, int success)
241 {
242   GNUNET_SERVER_receive_done (tc->client, success);
243   GNUNET_SERVER_client_drop (tc->client);
244   GNUNET_free_non_null (tc->buf);
245   GNUNET_free (tc);
246 }
247
248
249 /* end of server_tc.c */