1 #include "UDPTrackerClient.h"
2 
3 #include <cstring>
4 
5 #include <cppunit/extensions/HelperMacros.h>
6 
7 #include "TestUtil.h"
8 #include "UDPTrackerRequest.h"
9 #include "bittorrent_helper.h"
10 #include "wallclock.h"
11 
12 namespace aria2 {
13 
14 class UDPTrackerClientTest : public CppUnit::TestFixture {
15 
16   CPPUNIT_TEST_SUITE(UDPTrackerClientTest);
17   CPPUNIT_TEST(testCreateUDPTrackerConnect);
18   CPPUNIT_TEST(testCreateUDPTrackerAnnounce);
19   CPPUNIT_TEST(testConnectFollowedByAnnounce);
20   CPPUNIT_TEST(testRequestFailure);
21   CPPUNIT_TEST(testTimeout);
22   CPPUNIT_TEST_SUITE_END();
23 
24 public:
setUp()25   void setUp() {}
26 
27   void testCreateUDPTrackerConnect();
28   void testCreateUDPTrackerAnnounce();
29   void testConnectFollowedByAnnounce();
30   void testRequestFailure();
31   void testTimeout();
32 };
33 
34 CPPUNIT_TEST_SUITE_REGISTRATION(UDPTrackerClientTest);
35 
36 namespace {
createAnnounce(const std::string & remoteAddr,uint16_t remotePort,uint32_t transactionId)37 std::shared_ptr<UDPTrackerRequest> createAnnounce(const std::string& remoteAddr,
38                                                   uint16_t remotePort,
39                                                   uint32_t transactionId)
40 {
41   std::shared_ptr<UDPTrackerRequest> req(new UDPTrackerRequest());
42   req->connectionId = std::numeric_limits<uint64_t>::max();
43   req->action = UDPT_ACT_ANNOUNCE;
44   req->remoteAddr = remoteAddr;
45   req->remotePort = remotePort;
46   req->transactionId = transactionId;
47   req->infohash = "bittorrent-infohash-";
48   req->peerId = "bittorrent-peer-id--";
49   req->downloaded = INT64_MAX - 1;
50   req->left = INT64_MAX - 2;
51   req->uploaded = INT64_MAX - 3;
52   req->event = UDPT_EVT_STARTED;
53   req->ip = 0;
54   req->key = 1000000007;
55   req->numWant = 50;
56   req->port = 6889;
57   req->extensions = 0;
58   return req;
59 }
60 } // namespace
61 
62 namespace {
createErrorReply(unsigned char * data,size_t len,uint32_t transactionId,const std::string & errorString)63 ssize_t createErrorReply(unsigned char* data, size_t len,
64                          uint32_t transactionId, const std::string& errorString)
65 {
66   bittorrent::setIntParam(data, UDPT_ACT_ERROR);
67   bittorrent::setIntParam(data + 4, transactionId);
68   memcpy(data + 8, errorString.c_str(), errorString.size());
69   return 8 + errorString.size();
70 }
71 } // namespace
72 
73 namespace {
createConnectReply(unsigned char * data,size_t len,uint64_t connectionId,uint32_t transactionId)74 ssize_t createConnectReply(unsigned char* data, size_t len,
75                            uint64_t connectionId, uint32_t transactionId)
76 {
77   bittorrent::setIntParam(data, UDPT_ACT_CONNECT);
78   bittorrent::setIntParam(data + 4, transactionId);
79   bittorrent::setLLIntParam(data + 8, connectionId);
80   return 16;
81 }
82 } // namespace
83 
84 namespace {
createAnnounceReply(unsigned char * data,size_t len,uint32_t transactionId,int numPeers=0)85 ssize_t createAnnounceReply(unsigned char* data, size_t len,
86                             uint32_t transactionId, int numPeers = 0)
87 {
88   bittorrent::setIntParam(data, UDPT_ACT_ANNOUNCE);
89   bittorrent::setIntParam(data + 4, transactionId);
90   bittorrent::setIntParam(data + 8, 1800);
91   bittorrent::setIntParam(data + 12, 100);
92   bittorrent::setIntParam(data + 16, 256);
93   for (int i = 0; i < numPeers; ++i) {
94     bittorrent::packcompact(data + 20 + 6 * i,
95                             "192.168.0." + util::uitos(i + 1), 6990 + i);
96   }
97   return 20 + 6 * numPeers;
98 }
99 } // namespace
100 
testCreateUDPTrackerConnect()101 void UDPTrackerClientTest::testCreateUDPTrackerConnect()
102 {
103   unsigned char data[16];
104   std::string remoteAddr;
105   uint16_t remotePort = 0;
106   std::shared_ptr<UDPTrackerRequest> req(new UDPTrackerRequest());
107   req->action = UDPT_ACT_CONNECT;
108   req->remoteAddr = "192.168.0.1";
109   req->remotePort = 6991;
110   req->transactionId = 1000000009;
111   ssize_t rv =
112       createUDPTrackerConnect(data, sizeof(data), remoteAddr, remotePort, req);
113   CPPUNIT_ASSERT_EQUAL((ssize_t)16, rv);
114   CPPUNIT_ASSERT_EQUAL(req->remoteAddr, remoteAddr);
115   CPPUNIT_ASSERT_EQUAL(req->remotePort, remotePort);
116   CPPUNIT_ASSERT_EQUAL((int64_t)UDPT_INITIAL_CONNECTION_ID,
117                        (int64_t)bittorrent::getLLIntParam(data, 0));
118   CPPUNIT_ASSERT_EQUAL((int)req->action, (int)bittorrent::getIntParam(data, 8));
119   CPPUNIT_ASSERT_EQUAL(req->transactionId, bittorrent::getIntParam(data, 12));
120 }
121 
testCreateUDPTrackerAnnounce()122 void UDPTrackerClientTest::testCreateUDPTrackerAnnounce()
123 {
124   unsigned char data[100];
125   std::string remoteAddr;
126   uint16_t remotePort = 0;
127   std::shared_ptr<UDPTrackerRequest> req(
128       createAnnounce("192.168.0.1", 6991, 1000000009));
129   ssize_t rv =
130       createUDPTrackerAnnounce(data, sizeof(data), remoteAddr, remotePort, req);
131   CPPUNIT_ASSERT_EQUAL((ssize_t)100, rv);
132   CPPUNIT_ASSERT_EQUAL(req->connectionId, bittorrent::getLLIntParam(data, 0));
133   CPPUNIT_ASSERT_EQUAL((int)req->action, (int)bittorrent::getIntParam(data, 8));
134   CPPUNIT_ASSERT_EQUAL(req->transactionId, bittorrent::getIntParam(data, 12));
135   CPPUNIT_ASSERT_EQUAL(req->infohash, std::string(&data[16], &data[36]));
136   CPPUNIT_ASSERT_EQUAL(req->peerId, std::string(&data[36], &data[56]));
137   CPPUNIT_ASSERT_EQUAL(req->downloaded,
138                        (int64_t)bittorrent::getLLIntParam(data, 56));
139   CPPUNIT_ASSERT_EQUAL(req->left, (int64_t)bittorrent::getLLIntParam(data, 64));
140   CPPUNIT_ASSERT_EQUAL(req->uploaded,
141                        (int64_t)bittorrent::getLLIntParam(data, 72));
142   CPPUNIT_ASSERT_EQUAL(req->event, (int32_t)bittorrent::getIntParam(data, 80));
143   CPPUNIT_ASSERT_EQUAL(req->ip, bittorrent::getIntParam(data, 84));
144   CPPUNIT_ASSERT_EQUAL(req->key, bittorrent::getIntParam(data, 88));
145   CPPUNIT_ASSERT_EQUAL(req->numWant,
146                        (int32_t)bittorrent::getIntParam(data, 92));
147   CPPUNIT_ASSERT_EQUAL(req->port, bittorrent::getShortIntParam(data, 96));
148   CPPUNIT_ASSERT_EQUAL(req->extensions, bittorrent::getShortIntParam(data, 98));
149 }
150 
testConnectFollowedByAnnounce()151 void UDPTrackerClientTest::testConnectFollowedByAnnounce()
152 {
153   ssize_t rv;
154   UDPTrackerClient tr;
155   unsigned char data[100];
156   std::string remoteAddr;
157   uint16_t remotePort;
158   Timer now;
159   std::shared_ptr<UDPTrackerRequest> recvReq;
160 
161   std::shared_ptr<UDPTrackerRequest> req1(
162       createAnnounce("192.168.0.1", 6991, 0));
163   std::shared_ptr<UDPTrackerRequest> req2(
164       createAnnounce("192.168.0.1", 6991, 0));
165   req2->infohash = "bittorrent-infohash2";
166 
167   tr.addRequest(req1);
168   tr.addRequest(req2);
169   CPPUNIT_ASSERT_EQUAL((size_t)2, tr.getPendingRequests().size());
170   rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
171   // CONNECT request was inserted
172   CPPUNIT_ASSERT_EQUAL((size_t)3, tr.getPendingRequests().size());
173   CPPUNIT_ASSERT_EQUAL((ssize_t)16, rv);
174   CPPUNIT_ASSERT_EQUAL(req1->remoteAddr, remoteAddr);
175   CPPUNIT_ASSERT_EQUAL(req1->remotePort, remotePort);
176   CPPUNIT_ASSERT_EQUAL((int64_t)UDPT_INITIAL_CONNECTION_ID,
177                        (int64_t)bittorrent::getLLIntParam(data, 0));
178   uint32_t transactionId = bittorrent::getIntParam(data, 12);
179   rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
180   // Duplicate CONNECT request was not inserted
181   CPPUNIT_ASSERT_EQUAL((size_t)3, tr.getPendingRequests().size());
182   CPPUNIT_ASSERT_EQUAL((ssize_t)16, rv);
183 
184   tr.requestSent(now);
185   // CONNECT request was moved to inflight
186   CPPUNIT_ASSERT_EQUAL((size_t)2, tr.getPendingRequests().size());
187   rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
188   // Now all pending requests were moved to connect
189   CPPUNIT_ASSERT_EQUAL((ssize_t)-1, rv);
190   CPPUNIT_ASSERT(tr.getPendingRequests().empty());
191 
192   uint64_t connectionId = 12345;
193   rv = createConnectReply(data, sizeof(data), connectionId, transactionId);
194   rv = tr.receiveReply(recvReq, data, rv, req1->remoteAddr, req1->remotePort,
195                        now);
196   CPPUNIT_ASSERT_EQUAL(0, (int)rv);
197   if (rv == 0) {
198     CPPUNIT_ASSERT_EQUAL((int32_t)UDPT_ACT_CONNECT, recvReq->action);
199   }
200   // Now 2 requests get back to pending
201   CPPUNIT_ASSERT_EQUAL((size_t)2, tr.getPendingRequests().size());
202 
203   rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
204   // Creates announce for req1
205   CPPUNIT_ASSERT_EQUAL((ssize_t)100, rv);
206   CPPUNIT_ASSERT_EQUAL((size_t)2, tr.getPendingRequests().size());
207   CPPUNIT_ASSERT_EQUAL(connectionId, bittorrent::getLLIntParam(data, 0));
208   CPPUNIT_ASSERT_EQUAL((int)UDPT_ACT_ANNOUNCE,
209                        (int)bittorrent::getIntParam(data, 8));
210   CPPUNIT_ASSERT_EQUAL(req1->infohash, std::string(&data[16], &data[36]));
211 
212   rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
213   // Don't duplicate same request data
214   CPPUNIT_ASSERT_EQUAL((ssize_t)100, rv);
215   CPPUNIT_ASSERT_EQUAL((size_t)2, tr.getPendingRequests().size());
216   uint32_t transactionId1 = bittorrent::getIntParam(data, 12);
217 
218   tr.requestSent(now);
219   CPPUNIT_ASSERT_EQUAL((size_t)1, tr.getPendingRequests().size());
220 
221   rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
222   uint32_t transactionId2 = bittorrent::getIntParam(data, 12);
223   // Creates announce for req2
224   CPPUNIT_ASSERT_EQUAL((ssize_t)100, rv);
225   CPPUNIT_ASSERT_EQUAL((size_t)1, tr.getPendingRequests().size());
226   CPPUNIT_ASSERT_EQUAL(connectionId, bittorrent::getLLIntParam(data, 0));
227   CPPUNIT_ASSERT_EQUAL((int)UDPT_ACT_ANNOUNCE,
228                        (int)bittorrent::getIntParam(data, 8));
229   CPPUNIT_ASSERT_EQUAL(req2->infohash, std::string(&data[16], &data[36]));
230 
231   tr.requestSent(now);
232   // Now all requests are inflight
233   CPPUNIT_ASSERT_EQUAL((size_t)0, tr.getPendingRequests().size());
234 
235   // Reply for req2
236   rv = createAnnounceReply(data, sizeof(data), transactionId2);
237   rv = tr.receiveReply(recvReq, data, rv, req2->remoteAddr, req2->remotePort,
238                        now);
239   CPPUNIT_ASSERT_EQUAL(0, (int)rv);
240   if (rv == 0) {
241     CPPUNIT_ASSERT_EQUAL((int32_t)UDPT_ACT_ANNOUNCE, recvReq->action);
242   }
243   CPPUNIT_ASSERT_EQUAL((int)UDPT_STA_COMPLETE, req2->state);
244   CPPUNIT_ASSERT_EQUAL((int)UDPT_ERR_SUCCESS, req2->error);
245 
246   // Reply for req1
247   rv = createAnnounceReply(data, sizeof(data), transactionId1, 2);
248   rv = tr.receiveReply(recvReq, data, rv, req1->remoteAddr, req1->remotePort,
249                        now);
250   CPPUNIT_ASSERT_EQUAL(0, (int)rv);
251   if (rv == 0) {
252     CPPUNIT_ASSERT_EQUAL((int32_t)UDPT_ACT_ANNOUNCE, recvReq->action);
253   }
254   CPPUNIT_ASSERT_EQUAL((int)UDPT_STA_COMPLETE, req1->state);
255   CPPUNIT_ASSERT_EQUAL((int)UDPT_ERR_SUCCESS, req1->error);
256   CPPUNIT_ASSERT_EQUAL((size_t)2, req1->reply->peers.size());
257   for (int i = 0; i < 2; ++i) {
258     CPPUNIT_ASSERT_EQUAL("192.168.0." + util::uitos(i + 1),
259                          req1->reply->peers[i].first);
260     CPPUNIT_ASSERT_EQUAL((uint16_t)(6990 + i), req1->reply->peers[i].second);
261   }
262 
263   // Since we have connection ID, next announce request can be sent
264   // immediately
265   std::shared_ptr<UDPTrackerRequest> req3(
266       createAnnounce("192.168.0.1", 6991, 0));
267   req3->infohash = "bittorrent-infohash3";
268   tr.addRequest(req3);
269   rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
270   CPPUNIT_ASSERT_EQUAL((ssize_t)100, rv);
271   CPPUNIT_ASSERT_EQUAL(req3->infohash, std::string(&data[16], &data[36]));
272 
273   tr.requestSent(now);
274 
275   std::shared_ptr<UDPTrackerRequest> req4(
276       createAnnounce("192.168.0.1", 6991, 0));
277   req4->infohash = "bittorrent-infohash4";
278   tr.addRequest(req4);
279   Timer future = now;
280   future.advance(1_h);
281   rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, future);
282   // connection ID is stale because of the timeout
283   CPPUNIT_ASSERT_EQUAL((ssize_t)16, rv);
284   CPPUNIT_ASSERT_EQUAL((int64_t)UDPT_INITIAL_CONNECTION_ID,
285                        (int64_t)bittorrent::getLLIntParam(data, 0));
286 }
287 
testRequestFailure()288 void UDPTrackerClientTest::testRequestFailure()
289 {
290   ssize_t rv;
291   UDPTrackerClient tr;
292   unsigned char data[100];
293   std::string remoteAddr;
294   uint16_t remotePort;
295   Timer now;
296   std::shared_ptr<UDPTrackerRequest> recvReq;
297 
298   {
299     std::shared_ptr<UDPTrackerRequest> req1(
300         createAnnounce("192.168.0.1", 6991, 0));
301     std::shared_ptr<UDPTrackerRequest> req2(
302         createAnnounce("192.168.0.1", 6991, 0));
303 
304     tr.addRequest(req1);
305     tr.addRequest(req2);
306     rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
307     CPPUNIT_ASSERT_EQUAL((int)UDPT_ACT_CONNECT,
308                          (int)bittorrent::getIntParam(data, 8));
309     tr.requestFail(UDPT_ERR_NETWORK);
310     CPPUNIT_ASSERT_EQUAL((int)UDPT_STA_COMPLETE, req1->state);
311     CPPUNIT_ASSERT_EQUAL((int)UDPT_ERR_NETWORK, req1->error);
312     CPPUNIT_ASSERT_EQUAL((int)UDPT_STA_COMPLETE, req2->state);
313     CPPUNIT_ASSERT_EQUAL((int)UDPT_ERR_NETWORK, req2->error);
314     CPPUNIT_ASSERT(tr.getConnectRequests().empty());
315     CPPUNIT_ASSERT(tr.getPendingRequests().empty());
316     CPPUNIT_ASSERT(tr.getInflightRequests().empty());
317   }
318   {
319     std::shared_ptr<UDPTrackerRequest> req1(
320         createAnnounce("192.168.0.1", 6991, 0));
321     std::shared_ptr<UDPTrackerRequest> req2(
322         createAnnounce("192.168.0.1", 6991, 0));
323 
324     tr.addRequest(req1);
325     tr.addRequest(req2);
326     rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
327     CPPUNIT_ASSERT_EQUAL((int)UDPT_ACT_CONNECT,
328                          (int)bittorrent::getIntParam(data, 8));
329     uint32_t transactionId = bittorrent::getIntParam(data, 12);
330     tr.requestSent(now);
331 
332     rv = createErrorReply(data, sizeof(data), transactionId, "error");
333     rv = tr.receiveReply(recvReq, data, rv, req1->remoteAddr, req1->remotePort,
334                          now);
335     CPPUNIT_ASSERT_EQUAL((ssize_t)0, rv);
336     if (rv == 0) {
337       CPPUNIT_ASSERT_EQUAL((int32_t)UDPT_ACT_CONNECT, recvReq->action);
338     }
339     CPPUNIT_ASSERT_EQUAL((int)UDPT_STA_COMPLETE, req1->state);
340     CPPUNIT_ASSERT_EQUAL((int)UDPT_ERR_TRACKER, req1->error);
341     CPPUNIT_ASSERT_EQUAL((int)UDPT_STA_COMPLETE, req2->state);
342     CPPUNIT_ASSERT_EQUAL((int)UDPT_ERR_TRACKER, req2->error);
343     CPPUNIT_ASSERT(tr.getConnectRequests().empty());
344     CPPUNIT_ASSERT(tr.getPendingRequests().empty());
345     CPPUNIT_ASSERT(tr.getInflightRequests().empty());
346   }
347   {
348     std::shared_ptr<UDPTrackerRequest> req1(
349         createAnnounce("192.168.0.1", 6991, 0));
350 
351     tr.addRequest(req1);
352     rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
353     CPPUNIT_ASSERT_EQUAL((ssize_t)16, rv);
354     CPPUNIT_ASSERT_EQUAL((int)UDPT_ACT_CONNECT,
355                          (int)bittorrent::getIntParam(data, 8));
356     uint32_t transactionId = bittorrent::getIntParam(data, 12);
357     tr.requestSent(now);
358 
359     uint64_t connectionId = 12345;
360     rv = createConnectReply(data, sizeof(data), connectionId, transactionId);
361     rv = tr.receiveReply(recvReq, data, rv, req1->remoteAddr, req1->remotePort,
362                          now);
363     CPPUNIT_ASSERT_EQUAL(0, (int)rv);
364 
365     rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
366     CPPUNIT_ASSERT_EQUAL((int)UDPT_ACT_ANNOUNCE,
367                          (int)bittorrent::getIntParam(data, 8));
368     transactionId = bittorrent::getIntParam(data, 12);
369     tr.requestSent(now);
370 
371     rv = createErrorReply(data, sizeof(data), transactionId, "announce error");
372     rv = tr.receiveReply(recvReq, data, rv, req1->remoteAddr, req1->remotePort,
373                          now);
374     CPPUNIT_ASSERT_EQUAL((int)UDPT_STA_COMPLETE, req1->state);
375     CPPUNIT_ASSERT_EQUAL((int)UDPT_ERR_TRACKER, req1->error);
376     CPPUNIT_ASSERT(tr.getConnectRequests().empty());
377     CPPUNIT_ASSERT(tr.getPendingRequests().empty());
378     CPPUNIT_ASSERT(tr.getInflightRequests().empty());
379   }
380 }
381 
testTimeout()382 void UDPTrackerClientTest::testTimeout()
383 {
384   ssize_t rv;
385   unsigned char data[100];
386   std::string remoteAddr;
387   uint16_t remotePort;
388   Timer now;
389   UDPTrackerClient tr;
390   std::shared_ptr<UDPTrackerRequest> recvReq;
391 
392   {
393     std::shared_ptr<UDPTrackerRequest> req1(
394         createAnnounce("192.168.0.1", 6991, 0));
395     std::shared_ptr<UDPTrackerRequest> req2(
396         createAnnounce("192.168.0.1", 6991, 0));
397 
398     tr.addRequest(req1);
399     tr.addRequest(req2);
400     rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
401     CPPUNIT_ASSERT_EQUAL((int)UDPT_ACT_CONNECT,
402                          (int)bittorrent::getIntParam(data, 8));
403     tr.requestSent(now);
404     now.advance(20_s);
405     // 15 seconds 1st stage timeout passed
406     tr.handleTimeout(now);
407     CPPUNIT_ASSERT(tr.getConnectRequests().empty());
408     CPPUNIT_ASSERT_EQUAL((size_t)3, tr.getPendingRequests().size());
409     CPPUNIT_ASSERT(tr.getInflightRequests().empty());
410 
411     rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
412     // CONNECT request was inserted
413     CPPUNIT_ASSERT_EQUAL((int)UDPT_ACT_CONNECT,
414                          (int)bittorrent::getIntParam(data, 8));
415     tr.requestSent(now);
416     now.advance(65_s);
417     // 60 seconds 2nd stage timeout passed
418     tr.handleTimeout(now);
419     CPPUNIT_ASSERT(tr.getConnectRequests().empty());
420     CPPUNIT_ASSERT(tr.getPendingRequests().empty());
421     CPPUNIT_ASSERT(tr.getInflightRequests().empty());
422     CPPUNIT_ASSERT_EQUAL((int)UDPT_STA_COMPLETE, req1->state);
423     CPPUNIT_ASSERT_EQUAL((int)UDPT_ERR_TIMEOUT, req1->error);
424     CPPUNIT_ASSERT_EQUAL((int)UDPT_STA_COMPLETE, req2->state);
425     CPPUNIT_ASSERT_EQUAL((int)UDPT_ERR_TIMEOUT, req2->error);
426   }
427   {
428     std::shared_ptr<UDPTrackerRequest> req1(
429         createAnnounce("192.168.0.1", 6991, 0));
430 
431     tr.addRequest(req1);
432     rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
433     CPPUNIT_ASSERT_EQUAL((ssize_t)16, rv);
434     CPPUNIT_ASSERT_EQUAL((int)UDPT_ACT_CONNECT,
435                          (int)bittorrent::getIntParam(data, 8));
436     uint32_t transactionId = bittorrent::getIntParam(data, 12);
437     tr.requestSent(now);
438 
439     uint64_t connectionId = 12345;
440     rv = createConnectReply(data, sizeof(data), connectionId, transactionId);
441     rv = tr.receiveReply(recvReq, data, rv, req1->remoteAddr, req1->remotePort,
442                          now);
443     CPPUNIT_ASSERT_EQUAL(0, (int)rv);
444 
445     rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
446     CPPUNIT_ASSERT_EQUAL((int)UDPT_ACT_ANNOUNCE,
447                          (int)bittorrent::getIntParam(data, 8));
448     tr.requestSent(now);
449     now.advance(20_s);
450     // 15 seconds 1st stage timeout passed
451     tr.handleTimeout(now);
452     CPPUNIT_ASSERT(tr.getConnectRequests().empty());
453     CPPUNIT_ASSERT_EQUAL((size_t)1, tr.getPendingRequests().size());
454     CPPUNIT_ASSERT(tr.getInflightRequests().empty());
455 
456     rv = tr.createRequest(data, sizeof(data), remoteAddr, remotePort, now);
457     CPPUNIT_ASSERT_EQUAL((int)UDPT_ACT_ANNOUNCE,
458                          (int)bittorrent::getIntParam(data, 8));
459     tr.requestSent(now);
460     now.advance(65_s);
461     // 60 seconds 2nd stage timeout passed
462     tr.handleTimeout(now);
463     CPPUNIT_ASSERT(tr.getConnectRequests().empty());
464     CPPUNIT_ASSERT(tr.getPendingRequests().empty());
465     CPPUNIT_ASSERT(tr.getInflightRequests().empty());
466     CPPUNIT_ASSERT_EQUAL((int)UDPT_STA_COMPLETE, req1->state);
467     CPPUNIT_ASSERT_EQUAL((int)UDPT_ERR_TIMEOUT, req1->error);
468   }
469 }
470 
471 } // namespace aria2
472