1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 #include <Ice/Ice.h>
6 #include <TestHelper.h>
7 #include <Test.h>
8 #include <TestI.h>
9 
10 using namespace std;
11 using namespace Test;
12 
13 namespace
14 {
15 
16 class CallbackBase : public IceUtil::Monitor<IceUtil::Mutex>
17 {
18 public:
19 
20     CallbackBase() :
21         _called(false)
22     {
23     }
24 
25     virtual ~CallbackBase()
26     {
27     }
28 
29     void check()
30     {
31         IceUtil::Monitor<IceUtil::Mutex>::Lock sync(*this);
32         while(!_called)
33         {
34             wait();
35         }
36         _called = false;
37     }
38 
39 protected:
40 
41     void called()
42     {
43         IceUtil::Monitor<IceUtil::Mutex>::Lock sync(*this);
44         assert(!_called);
45         _called = true;
46         notify();
47     }
48 
49 private:
50 
51     bool _called;
52 };
53 
54 class Callback : public IceUtil::Shared, public CallbackBase
55 {
56 public:
57 
58     void response()
59     {
60         called();
61     }
62 
63     void exception(const ::Ice::Exception&)
64     {
65         test(false);
66     }
67 
68     void responseEx()
69     {
70         test(false);
71     }
72 
73     void exceptionEx(const ::Ice::Exception& ex)
74     {
75         test(dynamic_cast<const Ice::InvocationTimeoutException*>(&ex));
76         called();
77     }
78 };
79 typedef IceUtil::Handle<Callback> CallbackPtr;
80 
81 Ice::ConnectionPtr
82 connect(const Ice::ObjectPrxPtr& prx)
83 {
84     //
85     // Establish connection with the given proxy (which might have a timeout
86     // set and might sporadically fail on connection establishment if it's
87     // too slow). The loop ensures that the connection is established by retrying
88     // in case we can a ConnectTimeoutException
89     //
90     int nRetry = 10;
91     while(--nRetry > 0)
92     {
93         try
94         {
95             prx->ice_getConnection(); // Establish connection
96             break;
97         }
98         catch(const Ice::ConnectTimeoutException&)
99         {
100             // Can sporadically occur with slow machines
101         }
102     }
103     return prx->ice_getConnection();
104 }
105 
106 }
107 
108 void
109 allTests(Test::TestHelper* helper)
110 {
111     Ice::CommunicatorPtr communicator = helper->communicator();
112     string sref = "timeout:" + helper->getTestEndpoint();
113     Ice::ObjectPrxPtr obj = communicator->stringToProxy(sref);
114     test(obj);
115 
116     TimeoutPrxPtr timeout = ICE_CHECKED_CAST(TimeoutPrx, obj);
117     test(timeout);
118 
119     ControllerPrxPtr controller =
120         ICE_CHECKED_CAST(ControllerPrx, communicator->stringToProxy("controller:" + helper->getTestEndpoint(1)));
121     test(controller);
122 
123     cout << "testing connect timeout... " << flush;
124     {
125         //
126         // Expect ConnectTimeoutException.
127         //
128         TimeoutPrxPtr to = ICE_UNCHECKED_CAST(TimeoutPrx, obj->ice_timeout(100));
129         controller->holdAdapter(-1);
130         try
131         {
132             to->op();
133             test(false);
134         }
135         catch(const Ice::ConnectTimeoutException&)
136         {
137             // Expected.
138         }
139         controller->resumeAdapter();
140         timeout->op(); // Ensure adapter is active.
141     }
142     {
143         //
144         // Expect success.
145         //
146         TimeoutPrxPtr to = ICE_UNCHECKED_CAST(TimeoutPrx, obj->ice_timeout(-1));
147         controller->holdAdapter(100);
148         try
149         {
150             to->op();
151         }
152         catch(const Ice::ConnectTimeoutException&)
153         {
154             test(false);
155         }
156     }
157     cout << "ok" << endl;
158 
159     // The sequence needs to be large enough to fill the write/recv buffers
160     ByteSeq seq(2000000);
161 
162     cout << "testing connection timeout... " << flush;
163     {
164         //
165         // Expect TimeoutException.
166         //
167         TimeoutPrxPtr to = ICE_UNCHECKED_CAST(TimeoutPrx, obj->ice_timeout(250));
168         connect(to);
169         controller->holdAdapter(-1);
170         try
171         {
172             to->sendData(seq);
173             test(false);
174         }
175         catch(const Ice::TimeoutException&)
176         {
177             // Expected.
178         }
179         controller->resumeAdapter();
180         timeout->op(); // Ensure adapter is active.
181     }
182     {
183         //
184         // Expect success.
185         //
186         TimeoutPrxPtr to = ICE_UNCHECKED_CAST(TimeoutPrx, obj->ice_timeout(2000));
187         controller->holdAdapter(100);
188         try
189         {
190             ByteSeq seq2(1000000);
191             to->sendData(seq2);
192         }
193         catch(const Ice::TimeoutException&)
194         {
195             test(false);
196         }
197     }
198     cout << "ok" << endl;
199 
200     cout << "testing invocation timeout... " << flush;
201     {
202         Ice::ConnectionPtr connection = obj->ice_getConnection();
203         TimeoutPrxPtr to = ICE_UNCHECKED_CAST(TimeoutPrx, obj->ice_invocationTimeout(100));
204         test(connection == to->ice_getConnection());
205         try
206         {
207             to->sleep(500);
208             test(false);
209         }
210         catch(const Ice::InvocationTimeoutException&)
211         {
212         }
213         obj->ice_ping();
214         to = ICE_CHECKED_CAST(TimeoutPrx, obj->ice_invocationTimeout(1000));
215         test(connection == to->ice_getConnection());
216         try
217         {
218             to->sleep(100);
219         }
220         catch(const Ice::InvocationTimeoutException&)
221         {
222             test(false);
223         }
224         test(connection == to->ice_getConnection());
225     }
226     {
227         //
228         // Expect InvocationTimeoutException.
229         //
230         TimeoutPrxPtr to = ICE_UNCHECKED_CAST(TimeoutPrx, obj->ice_invocationTimeout(100));
231 
232 #ifdef ICE_CPP11_MAPPING
233         auto f = to->sleepAsync(500);
234         try
235         {
236             f.get();
237             test(false);
238         }
239         catch(const Ice::InvocationTimeoutException&)
240         {
241         }
242         catch(...)
243         {
244             test(false);
245         }
246 #else
247         CallbackPtr cb = new Callback();
248         to->begin_sleep(500, newCallback_Timeout_sleep(cb, &Callback::responseEx, &Callback::exceptionEx));
249         cb->check();
250 #endif
251         obj->ice_ping();
252     }
253     {
254         //
255         // Expect success.
256         //
257         TimeoutPrxPtr to = ICE_UNCHECKED_CAST(TimeoutPrx, obj->ice_invocationTimeout(1000));
258 #ifdef ICE_CPP11_MAPPING
259         auto f = to->sleepAsync(100);
260         try
261         {
262             f.get();
263         }
264         catch(...)
265         {
266             test(false);
267         }
268 #else
269         CallbackPtr cb = new Callback();
270         to->begin_sleep(100, newCallback_Timeout_sleep(cb, &Callback::response, &Callback::exception));
271         cb->check();
272 #endif
273     }
274     {
275         //
276         // Backward compatible connection timeouts
277         //
278         TimeoutPrxPtr to = ICE_UNCHECKED_CAST(TimeoutPrx, obj->ice_invocationTimeout(-2)->ice_timeout(250));
279         Ice::ConnectionPtr con = connect(to);
280         try
281         {
282             to->sleep(750);
283             test(false);
284         }
285         catch(const Ice::TimeoutException&)
286         {
287             try
288             {
289                 con->getInfo();
290                 test(false);
291             }
292             catch(const Ice::TimeoutException&)
293             {
294                 // Connection got closed as well.
295             }
296         }
297         obj->ice_ping();
298         try
299         {
300             con = connect(to);
301 #ifdef ICE_CPP11_MAPPING
302             to->sleepAsync(750).get();
303 #else
304             to->end_sleep(to->begin_sleep(750));
305 #endif
306             test(false);
307         }
308         catch(const Ice::TimeoutException&)
309         {
310             try
311             {
312                 con->getInfo();
313                 test(false);
314             }
315             catch(const Ice::TimeoutException&)
316             {
317                 // Connection got closed as well.
318             }
319         }
320         obj->ice_ping();
321     }
322     cout << "ok" << endl;
323 
324     cout << "testing close timeout... " << flush;
325     {
326         TimeoutPrxPtr to = ICE_UNCHECKED_CAST(TimeoutPrx, obj->ice_timeout(250));
327         Ice::ConnectionPtr connection = connect(to);
328         controller->holdAdapter(-1);
329         connection->close(Ice::ICE_SCOPED_ENUM(ConnectionClose, GracefullyWithWait));
330         try
331         {
332             connection->getInfo(); // getInfo() doesn't throw in the closing state.
333         }
334         catch(const Ice::LocalException&)
335         {
336             test(false);
337         }
338         while(true)
339         {
340             try
341             {
342                 connection->getInfo();
343                 IceUtil::ThreadControl::sleep(IceUtil::Time::milliSeconds(10));
344             }
345             catch(const Ice::ConnectionManuallyClosedException& ex)
346             {
347                 // Expected.
348                 test(ex.graceful);
349                 break;
350             }
351         }
352         controller->resumeAdapter();
353         timeout->op(); // Ensure adapter is active.
354     }
355     cout << "ok" << endl;
356 
357     cout << "testing timeout overrides... " << flush;
358     {
359         //
360         // Test Ice.Override.Timeout. This property overrides all
361         // endpoint timeouts.
362         //
363         Ice::InitializationData initData;
364         initData.properties = communicator->getProperties()->clone();
365         initData.properties->setProperty("Ice.Override.ConnectTimeout", "250");
366         initData.properties->setProperty("Ice.Override.Timeout", "100");
367         Ice::CommunicatorHolder ich(initData);
368         TimeoutPrxPtr to = ICE_UNCHECKED_CAST(TimeoutPrx, ich->stringToProxy(sref));
369         connect(to);
370         controller->holdAdapter(-1);
371         try
372         {
373             to->sendData(seq);
374             test(false);
375         }
376         catch(const Ice::TimeoutException&)
377         {
378             // Expected.
379         }
380         controller->resumeAdapter();
381         timeout->op(); // Ensure adapter is active.
382 
383         //
384         // Calling ice_timeout() should have no effect.
385         //
386         to = ICE_UNCHECKED_CAST(TimeoutPrx, to->ice_timeout(1000));
387         connect(to);
388         controller->holdAdapter(-1);
389         try
390         {
391             to->sendData(seq);
392             test(false);
393         }
394         catch(const Ice::TimeoutException&)
395         {
396             // Expected.
397         }
398         controller->resumeAdapter();
399         timeout->op(); // Ensure adapter is active.
400     }
401     {
402         //
403         // Test Ice.Override.ConnectTimeout.
404         //
405         Ice::InitializationData initData;
406         initData.properties = communicator->getProperties()->clone();
407         initData.properties->setProperty("Ice.Override.ConnectTimeout", "250");
408         Ice::CommunicatorHolder ich(initData);
409         controller->holdAdapter(-1);
410         TimeoutPrxPtr to = ICE_UNCHECKED_CAST(TimeoutPrx, ich->stringToProxy(sref));
411         try
412         {
413             to->op();
414             test(false);
415         }
416         catch(const Ice::ConnectTimeoutException&)
417         {
418             // Expected.
419         }
420         controller->resumeAdapter();
421         timeout->op(); // Ensure adapter is active.
422 
423         //
424         // Calling ice_timeout() should have no effect on the connect timeout.
425         //
426         controller->holdAdapter(-1);
427         to = ICE_UNCHECKED_CAST(TimeoutPrx, to->ice_timeout(1000));
428         try
429         {
430             to->op();
431             test(false);
432         }
433         catch(const Ice::ConnectTimeoutException&)
434         {
435             // Expected.
436         }
437         controller->resumeAdapter();
438         timeout->op(); // Ensure adapter is active.
439 
440         //
441         // Verify that timeout set via ice_timeout() is still used for requests.
442         //
443         to = ICE_UNCHECKED_CAST(TimeoutPrx, to->ice_timeout(250));
444         connect(to);
445         controller->holdAdapter(-1);
446         try
447         {
448             to->sendData(seq);
449             test(false);
450         }
451         catch(const Ice::TimeoutException&)
452         {
453             // Expected.
454         }
455         controller->resumeAdapter();
456         timeout->op(); // Ensure adapter is active.
457     }
458     {
459         //
460         // Test Ice.Override.CloseTimeout.
461         //
462         Ice::InitializationData initData;
463         initData.properties = communicator->getProperties()->clone();
464         initData.properties->setProperty("Ice.Override.CloseTimeout", "100");
465         Ice::CommunicatorHolder ich(initData);
466         Ice::ConnectionPtr connection = ich->stringToProxy(sref)->ice_getConnection();
467         controller->holdAdapter(-1);
468         IceUtil::Time now = IceUtil::Time::now();
469         ich.release()->destroy();
470         test(IceUtil::Time::now() - now < IceUtil::Time::milliSeconds(1000));
471         controller->resumeAdapter();
472         timeout->op(); // Ensure adapter is active.
473     }
474     cout << "ok" << endl;
475 
476     cout << "testing invocation timeouts with collocated calls... " << flush;
477     {
478         communicator->getProperties()->setProperty("TimeoutCollocated.AdapterId", "timeoutAdapter");
479 
480         Ice::ObjectAdapterPtr adapter = communicator->createObjectAdapter("TimeoutCollocated");
481         adapter->activate();
482 
483         timeout = ICE_UNCHECKED_CAST(TimeoutPrx, adapter->addWithUUID(ICE_MAKE_SHARED(TimeoutI)));
484         timeout = timeout->ice_invocationTimeout(100);
485         try
486         {
487             timeout->sleep(500);
488             test(false);
489         }
490         catch(const Ice::InvocationTimeoutException&)
491         {
492         }
493 
494         try
495         {
496 #ifdef ICE_CPP11_MAPPING
497             timeout->sleepAsync(500).get();
498 #else
499             timeout->end_sleep(timeout->begin_sleep(500));
500 #endif
501             test(false);
502         }
503         catch(const Ice::InvocationTimeoutException&)
504         {
505         }
506 
507         try
508         {
509             timeout->ice_invocationTimeout(-2)->ice_ping();
510             #ifdef ICE_CPP11_MAPPING
511             timeout->ice_invocationTimeout(-2)->ice_pingAsync().get();
512             #else
513             timeout->ice_invocationTimeout(-2)->begin_ice_ping()->waitForCompleted();
514             #endif
515         }
516         catch(const Ice::Exception&)
517         {
518             test(false);
519         }
520 
521         TimeoutPrxPtr batchTimeout = timeout->ice_batchOneway();
522         batchTimeout->ice_ping();
523         batchTimeout->ice_ping();
524         batchTimeout->ice_ping();
525 
526         // Keep the server thread pool busy.
527 #ifdef ICE_CPP11_MAPPING
528         timeout->ice_invocationTimeout(-1)->sleepAsync(300);
529 #else
530         timeout->ice_invocationTimeout(-1)->begin_sleep(300);
531 #endif
532         try
533         {
534             batchTimeout->ice_flushBatchRequests();
535             test(false);
536         }
537         catch(const Ice::InvocationTimeoutException&)
538         {
539         }
540 
541         batchTimeout->ice_ping();
542         batchTimeout->ice_ping();
543         batchTimeout->ice_ping();
544 
545         // Keep the server thread pool busy.
546 #ifdef ICE_CPP11_MAPPING
547         timeout->ice_invocationTimeout(-1)->sleepAsync(300);
548 #else
549         timeout->ice_invocationTimeout(-1)->begin_sleep(300);
550 #endif
551         try
552         {
553 #ifdef ICE_CPP11_MAPPING
554             batchTimeout->ice_flushBatchRequestsAsync().get();
555 #else
556             batchTimeout->end_ice_flushBatchRequests(batchTimeout->begin_ice_flushBatchRequests());
557 #endif
558             test(false);
559         }
560         catch(const Ice::InvocationTimeoutException&)
561         {
562         }
563 
564         adapter->destroy();
565     }
566     cout << "ok" << endl;
567 
568     controller->shutdown();
569 }
570