root/test/test-tls-tcp-io.c

Revision 7fc7cc43795e2dc3eca7e924d74101d441bafb67, 13.6 kB (checked in by Hans Petter Jansson <hpj@gong.(none)>, 2 years ago)

Implement GError error reporting in high-level I/O functions.

  • Property mode set to 100644
Line 
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
3 /* test-tls-tcp-io.c - FlowTlsTcpIO test.
4  *
5  * Copyright (C) 2006 Hans Petter Jansson
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  *
22  * Authors: Hans Petter Jansson <hpj@copyleft.no>
23  */
24
25 #define TEST_UNIT_NAME "FlowTlsTcpIO"
26 #define TEST_TIMEOUT_S 60
27
28 /* Test variables; adjustable */
29
30 #define SOCKETS_NUM            15
31 #define SOCKETS_CONCURRENT_MAX 5
32
33 #define BUFFER_SIZE            1000000   /* Amount of data to transfer */
34 #define PACKET_MAX_SIZE        8192      /* Max transfer unit */
35 #define PACKET_MIN_SIZE        1         /* Min transfer unit */
36
37 #define TOTAL_PAUSE_TIME_MS    3000      /* Total time to spend *not* reading or writing */
38 #define PAUSE_MIN_LENGTH_MS    10        /* Min pause unit */
39 #define PAUSE_MAX_LENGTH_MS    200       /* Max pause unit */
40
41 /* Calculations to determine the probability of pausing for
42  * each packet processed. No user serviceable parts inside. */
43
44 #define PROBABILITY_MULTIPLIER 1000000   /* For fixed-point fractions */
45 #define PACKET_AVG_SIZE        (PACKET_MIN_SIZE + ((PACKET_MAX_SIZE - PACKET_MIN_SIZE) / 2))
46 #define PAUSE_AVG_LENGTH_MS    (PAUSE_MIN_LENGTH_MS + ((PAUSE_MAX_LENGTH_MS - PAUSE_MIN_LENGTH_MS) / 2))
47 #define NUM_PAUSES             (TOTAL_PAUSE_TIME_MS / PAUSE_AVG_LENGTH_MS)
48 #define TOTAL_EXPECTED_PACKETS (BUFFER_SIZE / PACKET_AVG_SIZE)
49 #define TOTAL_EXPECTED_EVENTS  (TOTAL_EXPECTED_PACKETS * 2)  /* Account for both reads and writes */
50 #define PAUSE_PROBABILITY      ((NUM_PAUSES * PROBABILITY_MULTIPLIER) / TOTAL_EXPECTED_EVENTS)
51
52 #include "test-common.c"
53
54 typedef struct
55 {
56   gint read_offset;
57   gint write_offset;
58 }
59 TransferInfo;
60
61 static guchar               *buffer                 = NULL;
62
63 static FlowIPService        *loopback_service       = NULL;
64 static FlowIPService        *bad_loopback_service   = NULL;
65 static FlowTlsTcpIOListener *tls_tcp_listener       = NULL;
66 static FlowTlsTcpIO         *tls_tcp_reader         = NULL;
67 static FlowTlsTcpIO         *tls_tcp_writer         = NULL;
68
69 static GHashTable           *transfer_info_table    = NULL;
70
71 static gint                  sockets_done           = 0;
72 static gint                  sockets_running        = 0;
73
74 static FlowTlsTcpIO         *main_tls_tcp_io        = NULL;
75
76 static void
77 transfer_info_free (TransferInfo *transfer_info)
78 {
79   g_slice_free (TransferInfo, transfer_info);
80 }
81
82 static gboolean
83 subthread_stalled_in_read (TransferInfo *transfer_info)
84 {
85   test_print ("Subthread stalled in read: read_offset=%d write_offset=%d\n",
86               transfer_info->read_offset, transfer_info->write_offset);
87   return TRUE;  /* So we can remove it unconditionally */
88 }
89
90 static gboolean
91 subthread_stalled_in_write (TransferInfo *transfer_info)
92 {
93   test_print ("Subthread stalled in write: read_offset=%d write_offset=%d\n",
94               transfer_info->read_offset, transfer_info->write_offset);
95   return TRUE;  /* So we can remove it unconditionally */
96 }
97
98 static void
99 subthread_main (void)
100 {
101   FlowTlsTcpIO *tls_tcp_io;
102   TransferInfo  transfer_info;
103   guchar        temp_buffer [PACKET_MAX_SIZE];
104   guint         id;
105
106   test_print ("Subthread connecting to main thread\n");
107
108   tls_tcp_io = flow_tls_tcp_io_new ();
109   flow_tcp_io_sync_connect (FLOW_TCP_IO (tls_tcp_io), loopback_service, NULL);
110
111   test_print ("Subthread connected to main thread\n");
112
113   transfer_info.read_offset  = 0;
114   transfer_info.write_offset = 0;
115
116   while (transfer_info.read_offset  < BUFFER_SIZE ||
117          transfer_info.write_offset < BUFFER_SIZE)
118   {
119     if (transfer_info.write_offset < BUFFER_SIZE)
120     {
121       gint len;
122
123       len = g_random_int_range (PACKET_MIN_SIZE, PACKET_MAX_SIZE);
124       len = MIN (len, BUFFER_SIZE - transfer_info.write_offset);
125
126       id = flow_timeout_add_to_current_thread (5000, (GSourceFunc) subthread_stalled_in_write, &transfer_info);
127
128       flow_io_sync_write (FLOW_IO (tls_tcp_io), buffer + transfer_info.write_offset, len, NULL);
129       test_print ("Subthread wrote %d bytes\n", len);
130
131       flow_source_remove_from_current_thread (id);
132
133       transfer_info.write_offset += len;
134     }
135
136     if (transfer_info.read_offset < BUFFER_SIZE)
137     {
138       gint len;
139
140       len = g_random_int_range (PACKET_MIN_SIZE, PACKET_MAX_SIZE);
141       len = MIN (len, BUFFER_SIZE - transfer_info.read_offset);
142
143       id = flow_timeout_add_to_current_thread (5000, (GSourceFunc) subthread_stalled_in_read, &transfer_info);
144
145       if (!flow_io_sync_read_exact (FLOW_IO (tls_tcp_io), temp_buffer, len, NULL))
146         test_end (TEST_RESULT_FAILED, "subthread short read");
147
148       flow_source_remove_from_current_thread (id);
149
150       test_print ("Subthread read %d bytes\n", len);
151
152       if (memcmp (buffer + transfer_info.read_offset, temp_buffer, len))
153         test_end (TEST_RESULT_FAILED, "subthread read mismatch");
154
155       transfer_info.read_offset += len;
156     }
157   }
158
159   test_print ("Subthread disconnecting\n");
160   flow_tcp_io_sync_disconnect (FLOW_TCP_IO (tls_tcp_io), NULL);
161   test_print ("Subthread disconnected\n");
162
163   g_object_unref (tls_tcp_io);
164   test_print ("Subthread cleaned up\n");
165 }
166
167 static gboolean
168 spawn_subthread (void)
169 {
170   if (sockets_running >= SOCKETS_CONCURRENT_MAX || sockets_done >= SOCKETS_NUM)
171     return TRUE;
172
173   test_print ("Spawning new subthread\n");
174   g_thread_create ((GThreadFunc) subthread_main, NULL, FALSE, NULL);
175
176   sockets_running++;
177   return TRUE;
178 }
179
180 static void
181 print_tls_tcp_io_status (FlowTlsTcpIO *tls_tcp_io, TransferInfo *transfer_info)
182 {
183   test_print ("[%p] read_offset=%d write_offset=%d\n",
184               tls_tcp_io, transfer_info->read_offset, transfer_info->write_offset);
185 }
186
187 static gboolean
188 print_status (void)
189 {
190   test_print ("Active sockets (main thread):\n");
191
192   g_hash_table_foreach (transfer_info_table, (GHFunc) print_tls_tcp_io_status, NULL);
193
194   return TRUE;
195 }
196
197 static void
198 read_notify (FlowTlsTcpIO *tls_tcp_io)
199 {
200   TransferInfo *transfer_info;
201   guchar        temp_buffer [PACKET_MAX_SIZE];
202   gint          len;
203
204   test_print ("Main thread read notify (%p)\n", tls_tcp_io);
205
206   transfer_info = g_hash_table_lookup (transfer_info_table, tls_tcp_io);
207   g_assert (transfer_info != NULL);
208
209   while ((len = flow_io_read (FLOW_IO (tls_tcp_io), temp_buffer, PACKET_MAX_SIZE)))
210   {
211     test_print ("Main thread read %d bytes\n", len);
212
213     if (memcmp (buffer + transfer_info->read_offset, temp_buffer, len))
214       test_end (TEST_RESULT_FAILED, "main thread read mismatch");
215
216     transfer_info->read_offset += len;
217
218     if (transfer_info->read_offset > BUFFER_SIZE)
219       test_end (TEST_RESULT_FAILED, "main thread read past buffer length");
220   }
221 }
222
223 static void
224 write_notify (FlowTlsTcpIO *tls_tcp_io)
225 {
226   TransferInfo *transfer_info;
227   gint          len;
228
229   test_print ("Main thread write notify (%p)\n", tls_tcp_io);
230
231   transfer_info = g_hash_table_lookup (transfer_info_table, tls_tcp_io);
232   g_assert (transfer_info != NULL);
233
234   len = g_random_int_range (PACKET_MIN_SIZE, PACKET_MAX_SIZE);
235   len = MIN (len, BUFFER_SIZE - transfer_info->write_offset);
236
237   flow_io_write (FLOW_IO (tls_tcp_io), buffer + transfer_info->write_offset, len);
238   test_print ("Main thread wrote %d bytes\n", len);
239
240   transfer_info->write_offset += len;
241
242   if (transfer_info->write_offset == BUFFER_SIZE)
243   {
244     flow_io_flush (FLOW_IO (tls_tcp_io));
245     flow_io_set_write_notify (FLOW_IO (tls_tcp_io), NULL, NULL);
246     test_print ("Main thread write done\n");
247   }
248 }
249
250 static void
251 lost_connection (FlowTlsTcpIO *tls_tcp_io)
252 {
253   TransferInfo *transfer_info;
254
255   test_print ("Main thread got connectivity change\n");
256
257   if (flow_tcp_io_get_connectivity (FLOW_TCP_IO (tls_tcp_io)) != FLOW_CONNECTIVITY_DISCONNECTED)
258     return;
259
260   transfer_info = g_hash_table_lookup (transfer_info_table, tls_tcp_io);
261   g_assert (transfer_info != NULL);
262
263   if (transfer_info->read_offset < BUFFER_SIZE)
264     test_end (TEST_RESULT_FAILED, "main thread did not read all data");
265
266   if (transfer_info->write_offset < BUFFER_SIZE)
267     test_end (TEST_RESULT_FAILED, "main thread did not write all data");
268
269   test_print ("Main thread lost connection\n");
270   g_hash_table_remove (transfer_info_table, tls_tcp_io);
271   g_object_unref (tls_tcp_io);
272
273   sockets_done++;
274   sockets_running--;
275
276   if (sockets_done >= SOCKETS_NUM && sockets_running == 0)
277     test_quit_main_loop ();
278 }
279
280 static void
281 new_connection (void)
282 {
283   FlowTlsTcpIO *tls_tcp_io;
284   TransferInfo *transfer_info;
285
286   tls_tcp_io = flow_tls_tcp_io_listener_pop_connection (tls_tcp_listener);
287   if (!tls_tcp_io)
288     return;
289
290   main_tls_tcp_io = tls_tcp_io;
291
292   test_print ("Main thread received connection\n");
293
294   transfer_info = g_slice_new0 (TransferInfo);
295   g_hash_table_insert (transfer_info_table, tls_tcp_io, transfer_info);
296
297   flow_io_set_read_notify  (FLOW_IO (tls_tcp_io), (FlowNotifyFunc) read_notify, tls_tcp_io);
298   flow_io_set_write_notify (FLOW_IO (tls_tcp_io), (FlowNotifyFunc) write_notify, tls_tcp_io);
299
300   g_signal_connect (tls_tcp_io, "connectivity-changed", (GCallback) lost_connection, NULL);
301 }
302
303 static void
304 long_test (void)
305 {
306   guint id [2];
307
308   test_print ("Long test begin\n");
309
310   id [0] = g_timeout_add (250, (GSourceFunc) spawn_subthread, NULL);
311   id [1] = g_timeout_add (5000, (GSourceFunc) print_status, NULL);
312   g_signal_connect (tls_tcp_listener, "new-connection", (GCallback) new_connection, NULL);
313
314   test_run_main_loop ();
315
316   g_source_remove (id [0]);
317   g_source_remove (id [1]);
318
319   test_print ("Long test end\n");
320 }
321
322 static void
323 short_tests (void)
324 {
325   guchar read_buffer [2048];
326
327   test_print ("Short tests begin\n");
328
329   tls_tcp_writer = flow_tls_tcp_io_new ();
330
331 #if 0
332   if (flow_tcp_io_sync_connect (FLOW_TCP_IO (tls_tcp_writer), bad_loopback_service, NULL))
333     test_end (TEST_RESULT_FAILED, "bad connect did not fail as expected");
334
335   test_print ("Bad connect failed as expected\n");
336 #endif
337
338   if (!flow_tcp_io_sync_connect (FLOW_TCP_IO (tls_tcp_writer), loopback_service, NULL))
339     test_end (TEST_RESULT_FAILED, "could not connect to short-test listener");
340
341   test_print ("Client connect ok\n");
342
343   tls_tcp_reader = flow_tls_tcp_io_listener_sync_pop_connection (tls_tcp_listener);
344   if (!tls_tcp_reader)
345     test_end (TEST_RESULT_FAILED, "missed connection on listener end");
346
347   test_print ("Server connect ok\n");
348
349   flow_io_write (FLOW_IO (tls_tcp_writer), buffer,        512);
350   flow_io_write (FLOW_IO (tls_tcp_writer), buffer +  512, 512);
351   flow_io_write (FLOW_IO (tls_tcp_writer), buffer + 1024, 512);
352   flow_io_write (FLOW_IO (tls_tcp_writer), buffer + 1536, 512);
353
354   test_print ("Wrote data\n");
355
356   /* Partial read */
357
358   flow_io_sync_read_exact (FLOW_IO (tls_tcp_reader), read_buffer, 2000, NULL);
359
360   /* Check read */
361
362   if (memcmp (buffer, read_buffer, 2000))
363     test_end (TEST_RESULT_FAILED, "data mismatch in short transfer (1)");
364
365   /* Read remaining bytes with an oversized request */
366
367   if (flow_io_sync_read (FLOW_IO (tls_tcp_reader), read_buffer, 100, NULL) != 48)
368     test_end (TEST_RESULT_FAILED, "oversized read request returned wrong count");
369
370   /* Make sure remaining bytes match */
371
372   if (memcmp (buffer + 2000, read_buffer, 48))
373     test_end (TEST_RESULT_FAILED, "data mismatch in short transfer (2)");
374
375   /* Make sure nothing got clobbered */
376
377   if (memcmp (buffer + 48, read_buffer + 48, 1000))
378     test_end (TEST_RESULT_FAILED, "data mismatch in short transfer (3)");
379
380   /* Disconnect */
381
382   flow_tcp_io_sync_disconnect (FLOW_TCP_IO (tls_tcp_reader), NULL);
383   flow_tcp_io_sync_disconnect (FLOW_TCP_IO (tls_tcp_writer), NULL);
384
385   g_object_unref (tls_tcp_reader);
386   g_object_unref (tls_tcp_writer);
387
388   test_print ("Short tests end\n");
389 }
390
391 static void
392 test_run (void)
393 {
394   FlowIPAddr *ip_addr;
395   gint        i;
396
397   g_random_set_seed (time (NULL));
398
399   /* Set up a buffer with random data */
400
401   buffer = g_malloc (BUFFER_SIZE);
402
403   for (i = 0; i < BUFFER_SIZE; )
404   {
405     guchar *p = buffer + i;
406
407     if (i < BUFFER_SIZE - 4)
408     {
409       *((guint32 *) p) = g_random_int ();
410       i += 4;
411     }
412     else
413     {
414       *p = (guchar) g_random_int ();
415       i++;
416     }
417   }
418
419   transfer_info_table = g_hash_table_new_full (g_direct_hash, g_direct_equal,
420                                                NULL, (GDestroyNotify) transfer_info_free);
421
422   loopback_service = flow_ip_service_new ();
423   ip_addr = flow_ip_addr_new ();
424   flow_ip_addr_set_string (ip_addr, "127.0.0.1");
425   flow_ip_service_set_port (loopback_service, 2533);
426   flow_ip_service_add_address (loopback_service, ip_addr);
427   g_object_unref (ip_addr);
428
429   bad_loopback_service = flow_ip_service_new ();
430   ip_addr = flow_ip_addr_new ();
431   flow_ip_addr_set_string (ip_addr, "127.0.0.1");
432   flow_ip_service_set_port (bad_loopback_service, 12505);
433   flow_ip_service_add_address (bad_loopback_service, ip_addr);
434   g_object_unref (ip_addr);
435
436   tls_tcp_listener = flow_tls_tcp_io_listener_new ();
437   if (!flow_tcp_listener_set_local_service (FLOW_TCP_LISTENER (tls_tcp_listener), loopback_service, NULL))
438     test_end (TEST_RESULT_FAILED, "could not bind short-test listener");
439
440   short_tests ();
441   long_test ();
442
443   g_hash_table_destroy (transfer_info_table);
444   transfer_info_table = NULL;
445
446   g_object_unref (tls_tcp_listener);
447   tls_tcp_listener = NULL;
448
449   g_object_unref (loopback_service);
450   loopback_service = NULL;
451
452   g_free (buffer);
453   buffer = NULL;
454 }
Note: See TracBrowser for help on using the browser.