Update exception message. Atleast boost 1.44.0 uses this one
[libt2n] / test / timeout.cpp
1 /*
2 Copyright (C) 2004 by Intra2net AG
3
4 The software in this package is distributed under the GNU General
5 Public License version 2 (with a special exception described below).
6
7 A copy of GNU General Public License (GPL) is included in this distribution,
8 in the file COPYING.GPL.
9
10 As a special exception, if other files instantiate templates or use macros
11 or inline functions from this file, or you compile this file and link it
12 with other works to produce a work based on this file, this file
13 does not by itself cause the resulting work to be covered
14 by the GNU General Public License.
15
16 However the source code for this file must still be made available
17 in accordance with section (3) of the GNU General Public License.
18
19 This exception does not invalidate any other reasons why a work based
20 on 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
56 using namespace std;
57 using namespace libt2n;
58
59 string 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
68 class 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
97 class 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
127 BOOST_CLASS_EXPORT(testfunc2_cmd)
128 BOOST_CLASS_EXPORT(testfunc2_res)
129
130 class test_timeoutFixture : public KillChildOnShutdownFixture
131 {
132 protected:
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
161 BOOST_FIXTURE_TEST_SUITE(test_timeout, test_timeoutFixture)
162
163 BOOST_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
206 BOOST_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
255 BOOST_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
317 BOOST_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
379 BOOST_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
456 BOOST_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
511 BOOST_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
572 BOOST_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
675 BOOST_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
778 BOOST_AUTO_TEST_SUITE_END()