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