3 Copyright (c) 2008, Arvid Norberg
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in
14 the documentation and/or other materials provided with the distribution.
15 * Neither the name of the author nor the names of its
16 contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 POSSIBILITY OF SUCH DAMAGE.
33 #include "libtorrent/parse_url.hpp"
34 #include "libtorrent/http_tracker_connection.hpp"
35 #include "libtorrent/buffer.hpp"
36 #include "libtorrent/xml_parse.hpp"
37 #include "libtorrent/upnp.hpp"
38 #include "libtorrent/entry.hpp"
39 #include "libtorrent/bitfield.hpp"
40 #include "libtorrent/torrent_info.hpp"
41 #include "libtorrent/escape_string.hpp"
42 #include "libtorrent/broadcast_socket.hpp"
43 #ifndef TORRENT_DISABLE_DHT
44 #include "libtorrent/kademlia/node_id.hpp"
45 #include "libtorrent/kademlia/routing_table.hpp"
47 #include <boost/tuple/tuple.hpp>
48 #include <boost/tuple/tuple_comparison.hpp>
49 #include <boost/bind.hpp>
53 using namespace libtorrent
;
54 using namespace boost::tuples
;
57 tuple
<int, int, bool> feed_bytes(http_parser
& parser
, char const* str
)
59 tuple
<int, int, bool> ret(0, 0, false);
60 tuple
<int, int, bool> prev(0, 0, false);
61 for (int chunks
= 1; chunks
< 70; ++chunks
)
63 ret
= make_tuple(0, 0, false);
65 buffer::const_interval
recv_buf(str
, str
);
68 int chunk_size
= (std::min
)(chunks
, int(strlen(recv_buf
.end
)));
69 if (chunk_size
== 0) break;
70 recv_buf
.end
+= chunk_size
;
71 int payload
, protocol
;
73 tie(payload
, protocol
) = parser
.incoming(recv_buf
, error
);
74 ret
.get
<0>() += payload
;
75 ret
.get
<1>() += protocol
;
76 ret
.get
<2>() += error
;
77 std::cerr
<< payload
<< ", " << protocol
<< ", " << chunk_size
<< std::endl
;
78 TORRENT_ASSERT(payload
+ protocol
== chunk_size
);
80 TEST_CHECK(prev
== make_tuple(0, 0, false) || ret
== prev
);
86 void parser_callback(std::string
& out
, int token
, char const* s
, char const* val
)
90 case xml_start_tag
: out
+= "B"; break;
91 case xml_end_tag
: out
+= "F"; break;
92 case xml_empty_tag
: out
+= "E"; break;
93 case xml_declaration_tag
: out
+= "D"; break;
94 case xml_comment
: out
+= "C"; break;
95 case xml_string
: out
+= "S"; break;
96 case xml_attribute
: out
+= "A"; break;
97 case xml_parse_error
: out
+= "P"; break;
98 default: TEST_CHECK(false);
101 if (token
== xml_attribute
)
103 TEST_CHECK(val
!= 0);
109 TEST_CHECK(val
== 0);
113 #ifndef TORRENT_DISABLE_DHT
114 void add_and_replace(libtorrent::dht::node_id
& dst
, libtorrent::dht::node_id
const& add
)
117 for (int k
= 19; k
>= 0; --k
)
119 int sum
= dst
[k
] + add
[k
] + (carry
?1:0);
128 using namespace libtorrent
;
130 TEST_CHECK(parse_url_components("http://foo:bar@host.com:80/path/to/file")
131 == make_tuple("http", "foo:bar", "host.com", 80, "/path/to/file", (char const*)0));
133 TEST_CHECK(parse_url_components("http://host.com/path/to/file")
134 == make_tuple("http", "", "host.com", 80, "/path/to/file", (char const*)0));
136 TEST_CHECK(parse_url_components("ftp://host.com:21/path/to/file")
137 == make_tuple("ftp", "", "host.com", 21, "/path/to/file", (char const*)0));
139 TEST_CHECK(parse_url_components("http://host.com/path?foo:bar@foo:")
140 == make_tuple("http", "", "host.com", 80, "/path?foo:bar@foo:", (char const*)0));
142 TEST_CHECK(parse_url_components("http://192.168.0.1/path/to/file")
143 == make_tuple("http", "", "192.168.0.1", 80, "/path/to/file", (char const*)0));
145 TEST_CHECK(parse_url_components("http://[::1]/path/to/file")
146 == make_tuple("http", "", "[::1]", 80, "/path/to/file", (char const*)0));
148 // base64 test vectors from http://www.faqs.org/rfcs/rfc4648.html
150 TEST_CHECK(base64encode("") == "");
151 TEST_CHECK(base64encode("f") == "Zg==");
152 TEST_CHECK(base64encode("fo") == "Zm8=");
153 TEST_CHECK(base64encode("foo") == "Zm9v");
154 TEST_CHECK(base64encode("foob") == "Zm9vYg==");
155 TEST_CHECK(base64encode("fooba") == "Zm9vYmE=");
156 TEST_CHECK(base64encode("foobar") == "Zm9vYmFy");
158 // base32 test vectors from http://www.faqs.org/rfcs/rfc4648.html
160 TEST_CHECK(base32encode("") == "");
161 TEST_CHECK(base32encode("f") == "MY======");
162 TEST_CHECK(base32encode("fo") == "MZXQ====");
163 TEST_CHECK(base32encode("foo") == "MZXW6===");
164 TEST_CHECK(base32encode("foob") == "MZXW6YQ=");
165 TEST_CHECK(base32encode("fooba") == "MZXW6YTB");
166 TEST_CHECK(base32encode("foobar") == "MZXW6YTBOI======");
168 TEST_CHECK(base32decode("") == "");
169 TEST_CHECK(base32decode("MY======") == "f");
170 TEST_CHECK(base32decode("MZXQ====") == "fo");
171 TEST_CHECK(base32decode("MZXW6===") == "foo");
172 TEST_CHECK(base32decode("MZXW6YQ=") == "foob");
173 TEST_CHECK(base32decode("MZXW6YTB") == "fooba");
174 TEST_CHECK(base32decode("MZXW6YTBOI======") == "foobar");
176 TEST_CHECK(base32decode("MY") == "f");
177 TEST_CHECK(base32decode("MZXW6YQ") == "foob");
178 TEST_CHECK(base32decode("MZXW6YTBOI") == "foobar");
179 TEST_CHECK(base32decode("mZXw6yTBO1======") == "foobar");
182 for (int i
= 0; i
< 255; ++i
)
185 TEST_CHECK(base32decode(base32encode(test
)) == test
);
189 TEST_CHECK(!url_has_argument("http://127.0.0.1/test", "test"));
190 TEST_CHECK(!url_has_argument("http://127.0.0.1/test?foo=24", "bar"));
191 TEST_CHECK(*url_has_argument("http://127.0.0.1/test?foo=24", "foo") == "24");
192 TEST_CHECK(*url_has_argument("http://127.0.0.1/test?foo=24&bar=23", "foo") == "24");
193 TEST_CHECK(*url_has_argument("http://127.0.0.1/test?foo=24&bar=23", "bar") == "23");
194 TEST_CHECK(*url_has_argument("http://127.0.0.1/test?foo=24&bar=23&a=e", "bar") == "23");
195 TEST_CHECK(*url_has_argument("http://127.0.0.1/test?foo=24&bar=23&a=e", "a") == "e");
196 TEST_CHECK(!url_has_argument("http://127.0.0.1/test?foo=24&bar=23&a=e", "b"));
198 // HTTP request parser
201 boost::tuple
<int, int, bool> received
= feed_bytes(parser
202 , "HTTP/1.1 200 OK\r\n"
203 "Content-Length: 4\r\n"
204 "Content-Type: text/plain\r\n"
208 TEST_CHECK(received
== make_tuple(4, 64, false));
209 TEST_CHECK(parser
.finished());
210 TEST_CHECK(std::equal(parser
.get_body().begin
, parser
.get_body().end
, "test"));
211 TEST_CHECK(parser
.header("content-type") == "text/plain");
212 TEST_CHECK(atoi(parser
.header("content-length").c_str()) == 4);
216 TEST_CHECK(!parser
.finished());
218 char const* upnp_response
=
219 "HTTP/1.1 200 OK\r\n"
220 "ST:upnp:rootdevice\r\n"
221 "USN:uuid:000f-66d6-7296000099dc::upnp:rootdevice\r\n"
222 "Location: http://192.168.1.1:5431/dyndev/uuid:000f-66d6-7296000099dc\r\n"
223 "Server: Custom/1.0 UPnP/1.0 Proc/Ver\r\n"
225 "Cache-Control:max-age=180\r\n"
226 "DATE: Fri, 02 Jan 1970 08:10:38 GMT\r\n\r\n";
228 received
= feed_bytes(parser
, upnp_response
);
230 TEST_CHECK(received
== make_tuple(0, int(strlen(upnp_response
)), false));
231 TEST_CHECK(parser
.get_body().left() == 0);
232 TEST_CHECK(parser
.header("st") == "upnp:rootdevice");
233 TEST_CHECK(parser
.header("location")
234 == "http://192.168.1.1:5431/dyndev/uuid:000f-66d6-7296000099dc");
235 TEST_CHECK(parser
.header("ext") == "");
236 TEST_CHECK(parser
.header("date") == "Fri, 02 Jan 1970 08:10:38 GMT");
239 TEST_CHECK(!parser
.finished());
241 char const* upnp_notify
=
242 "NOTIFY * HTTP/1.1\r\n"
243 "Host:239.255.255.250:1900\r\n"
244 "NT:urn:schemas-upnp-org:device:MediaServer:1\r\n"
246 "Location:http://10.0.1.15:2353/upnphost/udhisapi.dll?content=uuid:c17f2c31-d19b-4912-af94-651945c8a84e\r\n"
247 "USN:uuid:c17f0c32-d1db-4be8-ae94-25f94583026e::urn:schemas-upnp-org:device:MediaServer:1\r\n"
248 "Cache-Control:max-age=900\r\n"
249 "Server:Microsoft-Windows-NT/5.1 UPnP/1.0 UPnP-Device-Host/1.0\r\n";
251 received
= feed_bytes(parser
, upnp_notify
);
253 TEST_CHECK(received
== make_tuple(0, int(strlen(upnp_notify
)), false));
254 TEST_CHECK(parser
.method() == "notify");
255 TEST_CHECK(parser
.path() == "*");
258 TEST_CHECK(!parser
.finished());
260 char const* bt_lsd
= "BT-SEARCH * HTTP/1.1\r\n"
261 "Host: 239.192.152.143:6771\r\n"
263 "Infohash: 12345678901234567890\r\n"
266 received
= feed_bytes(parser
, bt_lsd
);
268 TEST_CHECK(received
== make_tuple(2, int(strlen(bt_lsd
) - 2), false));
269 TEST_CHECK(parser
.method() == "bt-search");
270 TEST_CHECK(parser
.path() == "*");
271 TEST_CHECK(atoi(parser
.header("port").c_str()) == 6881);
272 TEST_CHECK(parser
.header("infohash") == "12345678901234567890");
274 TEST_CHECK(!parser
.finished());
277 TEST_CHECK(!parser
.finished());
279 // make sure we support trackers with incorrect line endings
280 char const* tracker_response
=
282 "content-length: 5\n"
283 "content-type: test/plain\n"
287 received
= feed_bytes(parser
, tracker_response
);
289 TEST_CHECK(received
== make_tuple(5, int(strlen(tracker_response
) - 5), false));
290 TEST_CHECK(parser
.get_body().left() == 5);
294 char xml1
[] = "<a>foo<b/>bar</a>";
297 xml_parse(xml1
, xml1
+ sizeof(xml1
) - 1, bind(&parser_callback
298 , boost::ref(out1
), _1
, _2
, _3
));
299 std::cerr
<< out1
<< std::endl
;
300 TEST_CHECK(out1
== "BaSfooEbSbarFa");
302 char xml2
[] = "<?xml version = \"1.0\"?><c x=\"1\" \t y=\"3\"/><d foo='bar'></d boo='foo'><!--comment-->";
305 xml_parse(xml2
, xml2
+ sizeof(xml2
) - 1, bind(&parser_callback
306 , boost::ref(out2
), _1
, _2
, _3
));
307 std::cerr
<< out2
<< std::endl
;
308 TEST_CHECK(out2
== "DxmlAversionV1.0EcAxV1AyV3BdAfooVbarFdAbooVfooCcomment");
310 char xml3
[] = "<a f=1>foo</a f='b>";
313 xml_parse(xml3
, xml3
+ sizeof(xml3
) - 1, bind(&parser_callback
314 , boost::ref(out3
), _1
, _2
, _3
));
315 std::cerr
<< out3
<< std::endl
;
316 TEST_CHECK(out3
== "BaPunquoted attribute valueSfooFaPmissing end quote on attribute");
318 char xml4
[] = "<a f>foo</a v >";
321 xml_parse(xml4
, xml4
+ sizeof(xml4
) - 1, bind(&parser_callback
322 , boost::ref(out4
), _1
, _2
, _3
));
323 std::cerr
<< out4
<< std::endl
;
324 TEST_CHECK(out4
== "BaPgarbage inside element bracketsSfooFaPgarbage inside element brackets");
326 // test network functions
329 TEST_CHECK(is_local(address::from_string("192.168.0.1", ec
)));
330 TEST_CHECK(is_local(address::from_string("10.1.1.56", ec
)));
331 TEST_CHECK(!is_local(address::from_string("14.14.251.63", ec
)));
332 TEST_CHECK(is_loopback(address::from_string("127.0.0.1", ec
)));
333 TEST_CHECK(is_loopback(address::from_string("::1", ec
)));
334 TEST_CHECK(is_any(address_v6::any()));
335 TEST_CHECK(is_any(address_v4::any()));
336 TEST_CHECK(!is_any(address::from_string("31.53.21.64", ec
)));
338 // test torrent parsing
341 info
["pieces"] = "aaaaaaaaaaaaaaaaaaaa";
342 info
["name.utf-8"] = "test1";
343 info
["name"] = "test__";
344 info
["piece length"] = 16 * 1024;
345 info
["length"] = 3245;
347 torrent
["info"] = info
;
349 torrent_info
ti(torrent
);
350 std::cerr
<< ti
.name() << std::endl
;
351 TEST_CHECK(ti
.name() == "test1");
353 #ifdef TORRENT_WINDOWS
354 info
["name.utf-8"] = "c:/test1/test2/test3";
356 info
["name.utf-8"] = "/test1/test2/test3";
358 torrent
["info"] = info
;
359 torrent_info
ti2(torrent
);
360 std::cerr
<< ti2
.name() << std::endl
;
361 TEST_CHECK(ti2
.name() == "test3");
363 info
["name.utf-8"] = "test2/../test3/.././../../test4";
364 torrent
["info"] = info
;
365 torrent_info
ti3(torrent
);
366 std::cerr
<< ti3
.name() << std::endl
;
367 TEST_CHECK(ti3
.name() == "test2/test3/test4");
369 #ifndef TORRENT_DISABLE_DHT
370 // test kademlia functions
372 using namespace libtorrent::dht
;
374 for (int i
= 0; i
< 160; i
+= 4)
376 for (int j
= 0; j
< 160; j
+= 4)
379 a
[(159-i
) / 8] = 1 << (i
& 7);
381 b
[(159-j
) / 8] = 1 << (j
& 7);
382 int dist
= distance_exp(a
, b
);
384 TEST_CHECK(dist
>= 0 && dist
< 160);
385 TEST_CHECK(dist
== ((i
== j
)?0:(std::max
)(i
, j
)));
387 for (int k
= 0; k
< 160; k
+= 4)
390 c
[(159-k
) / 8] = 1 << (k
& 7);
392 bool cmp
= compare_ref(a
, b
, c
);
393 TEST_CHECK(cmp
== (distance(a
, c
) < distance(b
, c
)));
398 // test kademlia routing table
400 node_id id
= boost::lexical_cast
<sha1_hash
>("6123456789abcdef01232456789abcdef0123456");
401 dht::routing_table
table(id
, 10, s
);
402 table
.node_seen(id
, udp::endpoint(address_v4::any(), rand()));
405 node_id diff
= boost::lexical_cast
<sha1_hash
>("00000f7459456a9453f8719b09547c11d5f34064");
406 std::vector
<node_entry
> nodes
;
407 for (int i
= 0; i
< 1000000; ++i
)
409 table
.node_seen(tmp
, udp::endpoint(address_v4::any(), rand()));
410 add_and_replace(tmp
, diff
);
413 std::copy(table
.begin(), table
.end(), std::back_inserter(nodes
));
415 std::cout
<< "nodes: " << nodes
.size() << std::endl
;
417 std::vector
<node_entry
> temp
;
419 std::generate(tmp
.begin(), tmp
.end(), &std::rand
);
420 table
.find_node(tmp
, temp
, false, nodes
.size() + 1);
421 std::cout
<< "returned: " << temp
.size() << std::endl
;
422 TEST_CHECK(temp
.size() == nodes
.size());
424 std::generate(tmp
.begin(), tmp
.end(), &std::rand
);
425 table
.find_node(tmp
, temp
, true, nodes
.size() + 1);
426 std::cout
<< "returned: " << temp
.size() << std::endl
;
427 TEST_CHECK(temp
.size() == nodes
.size() + 1);
429 std::generate(tmp
.begin(), tmp
.end(), &std::rand
);
430 table
.find_node(tmp
, temp
, false, 7);
431 std::cout
<< "returned: " << temp
.size() << std::endl
;
432 TEST_CHECK(temp
.size() == 7);
434 std::sort(nodes
.begin(), nodes
.end(), bind(&compare_ref
435 , bind(&node_entry::id
, _1
)
436 , bind(&node_entry::id
, _2
), tmp
));
439 for (std::vector
<node_entry
>::iterator i
= temp
.begin()
440 , end(temp
.end()); i
!= end
; ++i
)
442 int hit
= std::find_if(nodes
.begin(), nodes
.end()
443 , bind(&node_entry::id
, _1
) == i
->id
) - nodes
.begin();
444 std::cerr
<< hit
<< std::endl
;
445 if (hit
< int(temp
.size())) ++hits
;
447 TEST_CHECK(hits
> int(temp
.size()) / 2);
449 std::generate(tmp
.begin(), tmp
.end(), &std::rand
);
450 table
.find_node(tmp
, temp
, false, 15);
451 std::cout
<< "returned: " << temp
.size() << std::endl
;
452 TEST_CHECK(temp
.size() == 15);
454 std::sort(nodes
.begin(), nodes
.end(), bind(&compare_ref
455 , bind(&node_entry::id
, _1
)
456 , bind(&node_entry::id
, _2
), tmp
));
459 for (std::vector
<node_entry
>::iterator i
= temp
.begin()
460 , end(temp
.end()); i
!= end
; ++i
)
462 int hit
= std::find_if(nodes
.begin(), nodes
.end()
463 , bind(&node_entry::id
, _1
) == i
->id
) - nodes
.begin();
464 std::cerr
<< hit
<< std::endl
;
465 if (hit
< int(temp
.size())) ++hits
;
467 TEST_CHECK(hits
> int(temp
.size()) / 2);
473 // test peer_id/sha1_hash type
477 TEST_CHECK(h1
== h2
);
478 TEST_CHECK(!(h1
!= h2
));
479 TEST_CHECK(!(h1
< h2
));
480 TEST_CHECK(!(h1
< h2
));
481 TEST_CHECK(h1
.is_all_zeros());
483 h1
= boost::lexical_cast
<sha1_hash
>("0123456789012345678901234567890123456789");
484 h2
= boost::lexical_cast
<sha1_hash
>("0113456789012345678901234567890123456789");
487 TEST_CHECK(h2
== h2
);
488 TEST_CHECK(h1
== h1
);
490 TEST_CHECK(h2
.is_all_zeros());
492 h2
= boost::lexical_cast
<sha1_hash
>("ffffffffff0000000000ffffffffff0000000000");
493 h1
= boost::lexical_cast
<sha1_hash
>("fffff00000fffff00000fffff00000fffff00000");
495 TEST_CHECK(h1
== boost::lexical_cast
<sha1_hash
>("fffff000000000000000fffff000000000000000"));
497 h2
= boost::lexical_cast
<sha1_hash
>("ffffffffff0000000000ffffffffff0000000000");
498 h1
= boost::lexical_cast
<sha1_hash
>("fffff00000fffff00000fffff00000fffff00000");
500 TEST_CHECK(h1
== boost::lexical_cast
<sha1_hash
>("fffffffffffffff00000fffffffffffffff00000"));
502 h2
= boost::lexical_cast
<sha1_hash
>("0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f");
504 std::cerr
<< h1
<< std::endl
;
505 TEST_CHECK(h1
== boost::lexical_cast
<sha1_hash
>("f0f0f0f0f0f0f0ff0f0ff0f0f0f0f0f0f0ff0f0f"));
506 TEST_CHECK(h1
!= h2
);
509 TEST_CHECK(h2
== boost::lexical_cast
<sha1_hash
>("2020202020202020202020202020202020202020"));
511 // CIDR distance test
512 h1
= boost::lexical_cast
<sha1_hash
>("0123456789abcdef01232456789abcdef0123456");
513 h2
= boost::lexical_cast
<sha1_hash
>("0123456789abcdef01232456789abcdef0123456");
514 TEST_CHECK(common_bits(&h1
[0], &h2
[0], 20) == 160);
515 h2
= boost::lexical_cast
<sha1_hash
>("0120456789abcdef01232456789abcdef0123456");
516 TEST_CHECK(common_bits(&h1
[0], &h2
[0], 20) == 14);
517 h2
= boost::lexical_cast
<sha1_hash
>("012f456789abcdef01232456789abcdef0123456");
518 TEST_CHECK(common_bits(&h1
[0], &h2
[0], 20) == 12);
519 h2
= boost::lexical_cast
<sha1_hash
>("0123456789abcdef11232456789abcdef0123456");
520 TEST_CHECK(common_bits(&h1
[0], &h2
[0], 20) == 16 * 4 + 3);
524 bitfield
test1(10, false);
525 TEST_CHECK(test1
.count() == 0);
527 TEST_CHECK(test1
.count() == 1);
529 TEST_CHECK(test1
.count() == 0);
531 TEST_CHECK(test1
.count() == 1);
534 TEST_CHECK(test1
.count() == 3);
536 TEST_CHECK(test1
.count() == 2);
537 int distance
= std::distance(test1
.begin(), test1
.end());
538 std::cerr
<< distance
<< std::endl
;
539 TEST_CHECK(distance
== 10);
542 TEST_CHECK(test1
.count() == 10);
545 TEST_CHECK(test1
.count() == 0);