Increase version
[libt2n] / test / timeout.cpp
... / ...
CommitLineData
1/*
2Copyright (C) 2004 by Intra2net AG
3
4The software in this package is distributed under the GNU General
5Public License version 2 (with a special exception described below).
6
7A copy of GNU General Public License (GPL) is included in this distribution,
8in the file COPYING.GPL.
9
10As a special exception, if other files instantiate templates or use macros
11or inline functions from this file, or you compile this file and link it
12with other works to produce a work based on this file, this file
13does not by itself cause the resulting work to be covered
14by the GNU General Public License.
15
16However the source code for this file must still be made available
17in accordance with section (3) of the GNU General Public License.
18
19This exception does not invalidate any other reasons why a work based
20on this file might be covered by the GNU General Public License.
21*/
22#include <sys/types.h>
23#include <unistd.h>
24#include <errno.h>
25#include <signal.h>
26#include <stdio.h>
27
28#include <iostream>
29#include <string>
30#include <sstream>
31#include <stdexcept>
32
33#include <boost/bind.hpp>
34
35#define BOOST_TEST_DYN_LINK
36#include <boost/test/unit_test.hpp>
37
38#include <boost/archive/binary_oarchive.hpp>
39#include <boost/archive/binary_iarchive.hpp>
40#include <boost/archive/xml_oarchive.hpp>
41#include <boost/archive/xml_iarchive.hpp>
42#include <boost/serialization/serialization.hpp>
43
44#include <container.hxx>
45#include <socket_client.hxx>
46#include <socket_server.hxx>
47#include <command_client.hxx>
48#include <command_server.hxx>
49
50#include "test_fixtures.hxx"
51
52#ifdef HAVE_CONFIG_H
53#include <config.h>
54#endif
55
56using namespace std;
57using namespace libt2n;
58
59string testfunc2(const string& str)
60{
61 if (str=="throw")
62 throw libt2n::t2n_runtime_error("throw me around");
63 string ret(str);
64 ret+=", testfunc() was here";
65 return ret;
66}
67
68class testfunc2_res : public libt2n::result
69{
70 private:
71 string res;
72
73 friend class boost::serialization::access;
74 template<class Archive>
75 void serialize(Archive & ar, const unsigned int version)
76 {
77 ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::result);
78 ar & BOOST_SERIALIZATION_NVP(res);
79 }
80
81 public:
82 testfunc2_res()
83 { }
84
85 testfunc2_res(const string& str)
86 {
87 res=str;
88 }
89
90 string get_data()
91 {
92 return res;
93 }
94};
95
96
97class testfunc2_cmd : public libt2n::command
98{
99 private:
100 string param;
101
102 friend class boost::serialization::access;
103 template<class Archive>
104 void serialize(Archive & ar, const unsigned int version)
105 {
106 ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::command);
107 ar & BOOST_SERIALIZATION_NVP(param);
108 }
109
110 public:
111 testfunc2_cmd()
112 { }
113
114 testfunc2_cmd(const string& str)
115 {
116 param=str;
117 }
118
119 libt2n::result* operator()()
120 {
121 return new testfunc2_res(testfunc2(param));
122 }
123};
124
125#include <boost/serialization/export.hpp>
126
127BOOST_CLASS_EXPORT(testfunc2_cmd)
128BOOST_CLASS_EXPORT(testfunc2_res)
129
130class test_timeoutFixture : public KillChildOnShutdownFixture
131{
132protected:
133 typedef uint32_t packet_size_indicator;
134
135 // this is an evil hack to get access to real_write, don't ever do this in an app!!!
136 class real_write_client_connection: public socket_client_connection
137 {
138 public:
139 void real_write(const std::string& data)
140 { socket_write(data); }
141 };
142
143 void send_slow_raw_socket(string data, socket_server* ss, unsigned int conn_id)
144 {
145 socket_server_connection *ssc=dynamic_cast<socket_server_connection*>(ss->get_connection(conn_id));
146
147 // this is an evil hack to get access to real_write, don't ever do this in an app!!!
148 real_write_connection *rwc=(real_write_connection*)ssc;
149
150 // we write one char each 0.2 sec
151 for (int pos=0; pos < data.size(); pos++)
152 {
153 string onebyte;
154 onebyte+=data[pos];
155 rwc->real_write(onebyte);
156 usleep(200000);
157 }
158 }
159};
160
161BOOST_FIXTURE_TEST_SUITE(test_timeout, test_timeoutFixture)
162
163BOOST_AUTO_TEST_CASE(ConnectTimeout)
164{
165 switch(child_pid=fork())
166 {
167 case -1:
168 {
169 BOOST_FAIL("fork error");
170 break;
171 }
172 case 0:
173 // child
174 {
175 try
176 {
177 socket_server ss("./socket");
178 } catch(...)
179 {
180 std::cerr << "exception in child. ignoring\n";
181 }
182
183 // don't call atexit and stuff
184 _exit(0);
185 }
186
187 default:
188 // parent
189 {
190 string data;
191
192 // wait till server is up
193 sleep(1);
194
195 string errormsg;
196
197 socket_client_connection sc("./socket");
198
199 BOOST_CHECK_MESSAGE(sc.connection::is_closed() == true, "connection not closed");
200
201 BOOST_CHECK_MESSAGE(sc.get_last_error_msg() == string("no more retries left after connect error"), "wrong errormessage");
202 }
203 }
204}
205
206BOOST_AUTO_TEST_CASE(HelloTimeoutNothing)
207{
208 switch(child_pid=fork())
209 {
210 case -1:
211 {
212 BOOST_FAIL("fork error");
213 break;
214 }
215 case 0:
216 // child
217 {
218 try
219 {
220 socket_server ss("./socket");
221
222 // max 10 sec
223 for (int i=0; i < 10; i++)
224 ss.fill_buffer(1000000);
225 } catch(...)
226 {
227 std::cerr << "exception in child. ignoring\n";
228 }
229
230 // don't call atexit and stuff
231 _exit(0);
232 }
233
234 default:
235 // parent
236 {
237 string data;
238
239 // wait till server is up
240 sleep(1);
241 socket_client_connection sc("./socket");
242 command_client cc(&sc,1000000,1000000);
243
244 t2n_exception* ep=cc.get_constuctor_exception();
245
246 string errormsg;
247 if (ep)
248 errormsg=ep->what();
249
250 BOOST_CHECK_EQUAL(string("timeout exceeded"),errormsg);
251 }
252 }
253}
254
255BOOST_AUTO_TEST_CASE(HelloTimeoutSlowData)
256{
257 switch(child_pid=fork())
258 {
259 case -1:
260 {
261 BOOST_FAIL("fork error");
262 break;
263 }
264 case 0:
265 // child
266 {
267 try
268 {
269 socket_server ss("./socket");
270
271 // create a valid packet
272 ostringstream hello;
273 hello << "T2Nv" << PROTOCOL_VERSION << ';';
274 int byteordercheck=1;
275 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
276 hello << ';';
277
278 packet_size_indicator psize=htonl(hello.str().size());
279 std::string send_data(hello.str());
280 send_data.insert(0,(char*)&psize,sizeof(packet_size_indicator));
281
282 ss.add_callback(new_connection,bind(&test_timeout::HelloTimeoutSlowData::send_slow_raw_socket, boost::ref(*this), send_data,&ss, _1));
283
284 // max 10 sec
285 for (int i=0; i < 10; i++)
286 ss.fill_buffer(1000000);
287 } catch(...)
288 {
289 std::cerr << "exception in child. ignoring\n";
290 }
291
292 // don't call atexit and stuff
293 _exit(0);
294 }
295
296 default:
297 // parent
298 {
299 string data;
300
301 // wait till server is up
302 sleep(1);
303 socket_client_connection sc("./socket");
304 command_client cc(&sc,1000000,1000000);
305
306 t2n_exception* ep=cc.get_constuctor_exception();
307
308 string errormsg;
309 if (ep)
310 errormsg=ep->what();
311
312 BOOST_CHECK_EQUAL(string("timeout exceeded"),errormsg);
313 }
314 }
315}
316
317BOOST_AUTO_TEST_CASE(CommandTimeout)
318{
319 switch(child_pid=fork())
320 {
321 case -1:
322 {
323 BOOST_FAIL("fork error");
324 break;
325 }
326 case 0:
327 // child
328 {
329 try
330 {
331 socket_server ss("./socket");
332
333 ostringstream hello;
334 hello << "T2Nv" << PROTOCOL_VERSION << ';';
335 int byteordercheck=1;
336 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
337 hello << ';';
338
339 ss.add_callback(new_connection,bind(&test_timeout::CommandTimeout::send_hello, boost::ref(*this), hello.str(),&ss, _1));
340
341 // max 10 sec
342 for (int i=0; i < 10; i++)
343 ss.fill_buffer(1000000);
344 } catch(...)
345 {
346 std::cerr << "exception in child. ignoring\n";
347 }
348
349 // don't call atexit and stuff
350 _exit(0);
351 }
352
353 default:
354 // parent
355 {
356 string data;
357
358 // wait till server is up
359 sleep(1);
360 socket_client_connection sc("./socket");
361
362 command_client cc(&sc,1000000,1000000);
363 result_container rc;
364
365 string errormsg;
366
367 try
368 {
369 cc.send_command(new testfunc2_cmd("hello"),rc);
370 }
371 catch(t2n_transfer_error &e)
372 { errormsg=e.what(); }
373
374 BOOST_CHECK_EQUAL(string("timeout exceeded"),errormsg);
375 }
376 }
377}
378
379BOOST_AUTO_TEST_CASE(CommandSlowResponse)
380{
381 switch(child_pid=fork())
382 {
383 case -1:
384 {
385 BOOST_FAIL("fork error");
386 break;
387 }
388 case 0:
389 // child
390 {
391 try
392 {
393 socket_server ss("./socket");
394
395 ostringstream hello;
396 hello << "T2Nv" << PROTOCOL_VERSION << ';';
397 int byteordercheck=1;
398 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
399 hello << ';';
400
401 ss.add_callback(new_connection,bind(&test_timeout::CommandSlowResponse::send_hello, boost::ref(*this), hello.str(),&ss, _1));
402
403 // max 10 sec
404 for (int i=0; i < 10; i++)
405 {
406 ss.fill_buffer(1000000);
407
408 string data;
409 unsigned int cid;
410
411 if(ss.get_packet(data,cid))
412 {
413 // create a valid packet & send
414 string response="abcdefghijklmnopqrstuvwxyz";
415 packet_size_indicator psize=htonl(response.size());
416 std::string send_data(response);
417 send_data.insert(0,(char*)&psize,sizeof(packet_size_indicator));
418 send_slow_raw_socket(send_data,&ss,cid);
419 }
420 }
421 } catch(...)
422 {
423 std::cerr << "exception in child. ignoring\n";
424 }
425
426 // don't call atexit and stuff
427 _exit(0);
428 }
429
430 default:
431 // parent
432 {
433 string data;
434
435 // wait till server is up
436 sleep(1);
437 socket_client_connection sc("./socket");
438
439 command_client cc(&sc,1000000,1000000);
440 result_container rc;
441
442 string errormsg;
443
444 try
445 {
446 cc.send_command(new testfunc2_cmd("hello"),rc);
447 }
448 catch(t2n_transfer_error &e)
449 { errormsg=e.what(); }
450
451 BOOST_CHECK_EQUAL(string("timeout exceeded"),errormsg);
452 }
453 }
454}
455
456BOOST_AUTO_TEST_CASE(DisconnectOnWrite)
457{
458 switch(child_pid=fork())
459 {
460 case -1:
461 {
462 BOOST_FAIL("fork error");
463 break;
464 }
465 case 0:
466 // child
467 {
468 try
469 {
470 socket_server ss("./socket");
471
472 // bail out as soon as we get something
473 ss.fill_buffer(-1);
474 } catch(...)
475 {
476 std::cerr << "exception in child. ignoring\n";
477 }
478
479 // don't call atexit and stuff
480 _exit(0);
481 }
482
483 default:
484 // parent
485 {
486 string data;
487
488 // don't kill us on broken pipe
489 signal(SIGPIPE, SIG_IGN);
490
491 // wait till server is up
492 sleep(1);
493 socket_client_connection sc("./socket");
494
495 string errormsg;
496
497 string huge(5000000,'x');
498
499 try
500 {
501 sc.write(huge);
502 }
503 catch(t2n_transfer_error &e)
504 { errormsg=e.what(); }
505
506 BOOST_CHECK_EQUAL(string("write() returned Broken pipe"),errormsg);
507 }
508 }
509}
510
511BOOST_AUTO_TEST_CASE(WriteTwice)
512{
513 switch(child_pid=fork())
514 {
515 case -1:
516 {
517 BOOST_FAIL("fork error");
518 break;
519 }
520 case 0:
521 // child
522 {
523 try
524 {
525 socket_server ss("./socket");
526
527 // bail out as soon as we get something
528 ss.fill_buffer(-1);
529 ss.fill_buffer(-1);
530 } catch(...)
531 {
532 std::cerr << "exception in child. ignoring\n";
533 }
534
535 // don't call atexit and stuff
536 _exit(0);
537 }
538
539 default:
540 // parent
541 {
542 string data;
543
544 // don't kill us on broken pipe
545 signal(SIGPIPE, SIG_IGN);
546
547 // wait till server is up
548 sleep(1);
549 socket_client_connection sc("./socket");
550
551 string errormsg;
552
553 sc.write("somedata");
554
555 sleep(1);
556
557 // server should disconnect now
558 try
559 {
560 sc.write("other data(2)");
561 sleep(1);
562 sc.write("other data(3)");
563 }
564 catch(t2n_transfer_error &e)
565 { errormsg=e.what(); }
566
567 BOOST_CHECK_EQUAL(string("write() returned Broken pipe"),errormsg);
568 }
569 }
570}
571
572BOOST_AUTO_TEST_CASE(DisconnectOnRead)
573{
574 pid_t pid2;
575
576 switch(child_pid=fork())
577 {
578 case -1:
579 {
580 BOOST_FAIL("fork error");
581 break;
582 }
583 case 0:
584 // child
585 {
586 try
587 {
588 // wait till server is up
589 sleep(1);
590
591 socket_client_connection sc("./socket");
592
593 // this is an evil hack to get access to real_write, don't ever do this in an app!!!
594 real_write_client_connection *rwc=(real_write_client_connection*)&sc;
595 rwc->real_write(string(10000,'x'));
596 } catch(...)
597 {
598 std::cerr << "exception in child. ignoring\n";
599 }
600
601 // don't call atexit and stuff
602 _exit(0);
603 }
604
605 default:
606 // parent
607 {
608 // don't kill us on broken pipe
609 signal(SIGPIPE, SIG_IGN);
610
611 socket_server ss("./socket");
612
613 time_t t0 = time(NULL);
614
615 // max 5 sec
616 while (time(NULL) < t0 + 5 )
617 {
618 ss.fill_buffer(1000000);
619
620 string data;
621 ss.get_packet(data);
622 }
623
624 // are we still alive and able to process data?
625
626 switch(pid2=fork())
627 {
628 case -1:
629 {
630 BOOST_FAIL("fork error");
631 break;
632 }
633 case 0:
634 // child
635 {
636 try
637 {
638 socket_client_connection *sc=new socket_client_connection("./socket");
639 sc->write(string(10000,'x'));
640 // socket is closed regularly
641 delete sc;
642 } catch(...)
643 {
644 std::cerr << "exception in child. ignoring\n";
645 }
646
647 // don't run regular cleanup, otherwise cppunit stuff gets called
648 _exit(0);
649 }
650
651 default:
652 // parent
653 {
654 string received;
655
656 t0 = time(NULL);
657
658 // max 10 sec
659 while (time(NULL) < t0 + 10 )
660 {
661 ss.fill_buffer(1000000);
662
663 if (ss.get_packet(received))
664 break;
665 }
666
667 BOOST_CHECK_EQUAL(string(10000,'x'),received);
668 }
669 }
670 }
671 }
672 kill(pid2,SIGKILL);
673}
674
675BOOST_AUTO_TEST_CASE(BreakAccept)
676{
677 pid_t pid2;
678
679 switch(child_pid=fork())
680 {
681 case -1:
682 {
683 BOOST_FAIL("fork error");
684 break;
685 }
686 case 0:
687 // child
688 {
689 try
690 {
691 // wait till server is really up and waiting
692 sleep(2);
693
694 // connect with very tight timeout and only 1 retry
695 socket_client_connection sc("./socket",50,1);
696 } catch(...)
697 {
698 std::cerr << "exception in child. ignoring\n";
699 }
700
701 // don't call atexit and stuff
702 _exit(0);
703 }
704
705 default:
706 // parent
707 {
708 // don't kill us on broken pipe
709 signal(SIGPIPE, SIG_IGN);
710
711 socket_server ss("./socket");
712
713 // server is "working" while client wants to connect
714 sleep(5);
715
716 time_t t0 = time(NULL);
717
718 // max 5 sec
719 while (time(NULL) < t0 + 5 )
720 {
721 ss.fill_buffer(1000000);
722
723 string data;
724 ss.get_packet(data);
725 }
726
727 // are we still alive and able to process data?
728
729 switch(pid2=fork())
730 {
731 case -1:
732 {
733 BOOST_FAIL("fork error");
734 break;
735 }
736 case 0:
737 // child
738 {
739 try
740 {
741 socket_client_connection *sc=new socket_client_connection("./socket");
742 sc->write(string(10000,'x'));
743 delete sc;
744 // socket is closed regularly
745 } catch(...)
746 {
747 std::cerr << "exception in child. ignoring\n";
748 }
749
750 // don't run regular cleanup, otherwise cppunit stuff gets called
751 _exit(0);
752 }
753
754 default:
755 // parent
756 {
757 string received;
758
759 t0 = time(NULL);
760
761 // max 10 sec
762 while (time(NULL) < t0 + 10 )
763 {
764 ss.fill_buffer(1000000);
765
766 if (ss.get_packet(received))
767 break;
768 }
769
770 BOOST_CHECK_EQUAL(string(10000,'x'),received);
771 }
772 }
773 }
774 }
775 kill(pid2,SIGKILL);
776}
777
778BOOST_AUTO_TEST_SUITE_END()