libt2n: (gerd) fix bug receiving fragmented data
[libt2n] / test / timeout.cpp
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
123 class test_timeout : public TestFixture
124 {
125     CPPUNIT_TEST_SUITE(test_timeout);
126
127     CPPUNIT_TEST(HelloTimeoutNothing);
128     CPPUNIT_TEST(HelloTimeoutSlowData);
129     CPPUNIT_TEST(CommandTimeout);
130     CPPUNIT_TEST(CommandSlowResponse);
131
132     CPPUNIT_TEST_SUITE_END();
133
134     public:
135
136     typedef uint32_t packet_size_indicator;
137
138     void setUp()
139     { }
140
141     void tearDown()
142     { }
143
144     void send_hello(string hello_string, socket_server* ss, unsigned int conn_id)
145     {
146         server_connection *sc=ss->get_connection(conn_id);
147         sc->write(hello_string);
148     }
149
150     void send_slow_raw_socket(string data, socket_server* ss, unsigned int conn_id)
151     {
152         socket_server_connection *ssc=dynamic_cast<socket_server_connection*>(ss->get_connection(conn_id));
153
154         // this is an evil hack to get access to real_write, don't ever do this in an app!!!
155         real_write_connection *rwc=(real_write_connection*)ssc;
156
157         // we write one char each 0.2 sec
158         for (int pos=0; pos < data.size(); pos++)
159         {
160             string onebyte;
161             onebyte+=data[pos];
162             rwc->real_write(onebyte);
163             usleep(200000);
164         }
165     }
166
167     void HelloTimeoutNothing()
168     {
169         pid_t pid;
170
171         switch(pid=fork())
172         {
173             case -1:
174             {
175                 CPPUNIT_FAIL("fork error");
176                 break;
177             }
178             case 0:
179             // child
180             {
181                 socket_server ss("./socket");
182
183                 // max 10 sec
184                 for (int i=0; i < 10; i++)
185                     ss.fill_buffer(1000000);
186                 // don't call atexit and stuff
187                 _exit(0);
188             }
189
190             default:
191             // parent
192             {
193                 string data;
194
195                 // wait till server is up
196                 sleep(1);
197                 socket_client_connection sc("./socket");
198
199                 string errormsg;
200
201                 try
202                 {
203                     command_client cc(sc,1000000,1000000);
204                 }
205                 catch(t2n_transfer_error &e)
206                 { errormsg=e.what(); }
207                 catch(...)
208                 { throw; }
209
210                 CPPUNIT_ASSERT_EQUAL(string("timeout exceeded"),errormsg);
211             }
212         }
213     }
214
215     void HelloTimeoutSlowData()
216     {
217         pid_t pid;
218
219         switch(pid=fork())
220         {
221             case -1:
222             {
223                 CPPUNIT_FAIL("fork error");
224                 break;
225             }
226             case 0:
227             // child
228             {
229                 socket_server ss("./socket");
230
231                 // create a valid packet
232                 ostringstream hello;
233                 hello << "T2Nv" << PROTOCOL_VERSION << ';';
234                 int byteordercheck=1;
235                 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
236                 hello << ';';
237
238                 packet_size_indicator psize=htonl(hello.str().size());
239                 std::string send_data(hello.str());
240                 send_data.insert(0,(char*)&psize,sizeof(packet_size_indicator));
241
242                 ss.add_callback(new_connection,bind(&test_timeout::send_slow_raw_socket, boost::ref(*this), send_data,&ss, _1));
243
244                 // max 10 sec
245                 for (int i=0; i < 10; i++)
246                     ss.fill_buffer(1000000);
247                 // don't call atexit and stuff
248                 _exit(0);
249             }
250
251             default:
252             // parent
253             {
254                 string data;
255
256                 // wait till server is up
257                 sleep(1);
258                 socket_client_connection sc("./socket");
259
260                 string errormsg;
261
262                 try
263                 {
264                     command_client cc(sc,1000000,1000000);
265                 }
266                 catch(t2n_transfer_error &e)
267                 { errormsg=e.what(); }
268                 catch(...)
269                 { throw; }
270
271                 CPPUNIT_ASSERT_EQUAL(string("timeout exceeded"),errormsg);
272             }
273         }
274     }
275
276     void CommandTimeout()
277     {
278         pid_t pid;
279
280         switch(pid=fork())
281         {
282             case -1:
283             {
284                 CPPUNIT_FAIL("fork error");
285                 break;
286             }
287             case 0:
288             // child
289             {
290                 socket_server ss("./socket");
291
292                 ostringstream hello;
293                 hello << "T2Nv" << PROTOCOL_VERSION << ';';
294                 int byteordercheck=1;
295                 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
296                 hello << ';';
297
298                 ss.add_callback(new_connection,bind(&test_timeout::send_hello, boost::ref(*this), hello.str(),&ss, _1));
299
300                 // max 10 sec
301                 for (int i=0; i < 10; i++)
302                     ss.fill_buffer(1000000);
303                 // don't call atexit and stuff
304                 _exit(0);
305             }
306
307             default:
308             // parent
309             {
310                 string data;
311
312                 // wait till server is up
313                 sleep(1);
314                 socket_client_connection sc("./socket");
315
316                 command_client cc(sc,1000000,1000000);
317                 result_container rc;
318
319                 string errormsg;
320
321                 try
322                 {
323                     cc.send_command(new testfunc2_cmd("hello"),rc);
324                 }
325                 catch(t2n_transfer_error &e)
326                 { errormsg=e.what(); }
327                 catch(...)
328                 { throw; }
329
330                 CPPUNIT_ASSERT_EQUAL(string("timeout exceeded"),errormsg);
331             }
332         }
333     }
334
335     void CommandSlowResponse()
336     {
337         pid_t pid;
338
339         switch(pid=fork())
340         {
341             case -1:
342             {
343                 CPPUNIT_FAIL("fork error");
344                 break;
345             }
346             case 0:
347             // child
348             {
349                 socket_server ss("./socket");
350
351                 ostringstream hello;
352                 hello << "T2Nv" << PROTOCOL_VERSION << ';';
353                 int byteordercheck=1;
354                 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
355                 hello << ';';
356
357                 ss.add_callback(new_connection,bind(&test_timeout::send_hello, boost::ref(*this), hello.str(),&ss, _1));
358
359                 // max 10 sec
360                 for (int i=0; i < 10; i++)
361                 {
362                     ss.fill_buffer(1000000);
363
364                     string data;
365                     unsigned int cid;
366
367                     if(ss.get_packet(data,cid))
368                     {
369                         // create a valid packet & send
370                         string response="abcdefghijklmnopqrstuvwxyz";
371                         packet_size_indicator psize=htonl(response.size());
372                         std::string send_data(response);
373                         send_data.insert(0,(char*)&psize,sizeof(packet_size_indicator));
374                         send_slow_raw_socket(send_data,&ss,cid);
375                     }
376                 }
377                 // don't call atexit and stuff
378                 _exit(0);
379             }
380
381             default:
382             // parent
383             {
384                 string data;
385
386                 // wait till server is up
387                 sleep(1);
388                 socket_client_connection sc("./socket");
389
390                 command_client cc(sc,1000000,1000000);
391                 result_container rc;
392
393                 string errormsg;
394
395                 try
396                 {
397                     cc.send_command(new testfunc2_cmd("hello"),rc);
398                 }
399                 catch(t2n_transfer_error &e)
400                 { errormsg=e.what(); }
401                 catch(...)
402                 { throw; }
403
404                 CPPUNIT_ASSERT_EQUAL(string("timeout exceeded"),errormsg);
405             }
406         }
407     }
408
409 };
410
411 CPPUNIT_TEST_SUITE_REGISTRATION(test_timeout);