Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(64)

Side by Side Diff: net/http/http_cache_unittest.cc

Issue 2721933002: HttpCache::Transaction layer allowing parallel validation (Closed)
Patch Set: Feedback addressed Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/http/http_cache.h" 5 #include "net/http/http_cache.h"
6 6
7 #include <stdint.h> 7 #include <stdint.h>
8 8
9 #include <algorithm> 9 #include <algorithm>
10 #include <memory> 10 #include <memory>
(...skipping 1357 matching lines...) Expand 10 before | Expand all | Expand 10 after
1368 // All requests are waiting for the active entry. 1368 // All requests are waiting for the active entry.
1369 for (int i = 0; i < kNumTransactions; ++i) { 1369 for (int i = 0; i < kNumTransactions; ++i) {
1370 Context* c = context_list[i]; 1370 Context* c = context_list[i];
1371 EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, c->trans->GetLoadState()); 1371 EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, c->trans->GetLoadState());
1372 } 1372 }
1373 1373
1374 // Allow all requests to move from the Create queue to the active entry. 1374 // Allow all requests to move from the Create queue to the active entry.
1375 base::RunLoop().RunUntilIdle(); 1375 base::RunLoop().RunUntilIdle();
1376 1376
1377 // The first request should be a writer at this point, and the subsequent 1377 // The first request should be a writer at this point, and the subsequent
1378 // requests should be pending. 1378 // requests should have passed the validation phase and waiting for the
1379 // response to be written to the cache before they can read.
1380 EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
1381 EXPECT_EQ(4, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
1379 1382
1380 EXPECT_EQ(1, cache.network_layer()->transaction_count()); 1383 EXPECT_EQ(1, cache.network_layer()->transaction_count());
1381 EXPECT_EQ(0, cache.disk_cache()->open_count()); 1384 EXPECT_EQ(0, cache.disk_cache()->open_count());
1382 EXPECT_EQ(1, cache.disk_cache()->create_count()); 1385 EXPECT_EQ(1, cache.disk_cache()->create_count());
1383 1386
1384 // All requests depend on the writer, and the writer is between Start and 1387 // All requests depend on the writer, and the writer is between Start and
1385 // Read, i.e. idle. 1388 // Read, i.e. idle.
1386 for (int i = 0; i < kNumTransactions; ++i) { 1389 for (int i = 0; i < kNumTransactions; ++i) {
1387 Context* c = context_list[i]; 1390 Context* c = context_list[i];
1388 EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState()); 1391 EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
1389 } 1392 }
1390 1393
1391 for (int i = 0; i < kNumTransactions; ++i) { 1394 for (int i = 0; i < kNumTransactions; ++i) {
1392 Context* c = context_list[i]; 1395 Context* c = context_list[i];
1393 if (c->result == ERR_IO_PENDING) 1396 if (c->result == ERR_IO_PENDING)
1394 c->result = c->callback.WaitForResult(); 1397 c->result = c->callback.WaitForResult();
1398
1399 if (i == 1) {
1400 EXPECT_FALSE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
1401 EXPECT_EQ(4, cache.GetCountReaders(kSimpleGET_Transaction.url));
1402 }
1403
1395 ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction); 1404 ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
1396 } 1405 }
1397 1406
1398 // We should not have had to re-open the disk entry 1407 // We should not have had to re-open the disk entry
1399 1408
1400 EXPECT_EQ(1, cache.network_layer()->transaction_count()); 1409 EXPECT_EQ(1, cache.network_layer()->transaction_count());
1401 EXPECT_EQ(0, cache.disk_cache()->open_count()); 1410 EXPECT_EQ(0, cache.disk_cache()->open_count());
1402 EXPECT_EQ(1, cache.disk_cache()->create_count()); 1411 EXPECT_EQ(1, cache.disk_cache()->create_count());
1403 1412
1404 for (int i = 0; i < kNumTransactions; ++i) { 1413 for (int i = 0; i < kNumTransactions; ++i) {
1405 Context* c = context_list[i]; 1414 Context* c = context_list[i];
1406 delete c; 1415 delete c;
1407 } 1416 }
1408 } 1417 }
1409 1418
1419 // Parallel validation results in 200.
1420 TEST(HttpCache, SimpleGET_ParallelValidationNoMatch) {
1421 MockHttpCache cache;
1422
1423 MockHttpRequest request(kSimpleGET_Transaction);
1424 request.load_flags |= LOAD_VALIDATE_CACHE;
1425
1426 std::vector<Context*> context_list;
1427 const int kNumTransactions = 5;
1428
1429 for (int i = 0; i < kNumTransactions; ++i) {
1430 context_list.push_back(new Context());
1431 Context* c = context_list[i];
1432
1433 c->result = cache.CreateTransaction(&c->trans);
1434 ASSERT_THAT(c->result, IsOk());
1435 EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
1436
1437 c->result =
1438 c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
1439 }
1440
1441 // All requests are waiting for the active entry.
1442 for (int i = 0; i < kNumTransactions; ++i) {
1443 Context* c = context_list[i];
1444 EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, c->trans->GetLoadState());
1445 }
1446
1447 // Allow all requests to move from the Create queue to the active entry.
1448 base::RunLoop().RunUntilIdle();
1449
1450 // The first request should be a writer at this point, and the subsequent
1451 // requests should have passed the validation phase and created their own
1452 // entries since none of them matched the headers of the earlier one.
1453 EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
1454
1455 EXPECT_EQ(5, cache.network_layer()->transaction_count());
1456 EXPECT_EQ(0, cache.disk_cache()->open_count());
1457 EXPECT_EQ(3, cache.disk_cache()->create_count());
1458
1459 // All requests depend on the writer, and the writer is between Start and
1460 // Read, i.e. idle.
1461 for (int i = 0; i < kNumTransactions; ++i) {
1462 Context* c = context_list[i];
1463 EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
1464 }
1465
1466 for (int i = 0; i < kNumTransactions; ++i) {
1467 Context* c = context_list[i];
1468 if (c->result == ERR_IO_PENDING)
1469 c->result = c->callback.WaitForResult();
1470 ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
1471 }
1472
1473 EXPECT_EQ(5, cache.network_layer()->transaction_count());
1474 EXPECT_EQ(0, cache.disk_cache()->open_count());
1475 EXPECT_EQ(3, cache.disk_cache()->create_count());
1476
1477 for (int i = 0; i < kNumTransactions; ++i) {
1478 Context* c = context_list[i];
1479 delete c;
1480 }
1481 }
1482
1483 // Tests that a GET followed by a DELETE results in DELETE immediately starting
1484 // the headers phase and the entry is doomed.
1485 TEST(HttpCache, SimpleGET_ParallelValidationDelete) {
1486 MockHttpCache cache;
1487
1488 MockHttpRequest request(kSimpleGET_Transaction);
1489 request.load_flags |= LOAD_VALIDATE_CACHE;
1490
1491 MockHttpRequest delete_request(kSimpleGET_Transaction);
1492 delete_request.method = "DELETE";
1493
1494 std::vector<Context*> context_list;
1495 const int kNumTransactions = 2;
1496
1497 for (int i = 0; i < kNumTransactions; ++i) {
1498 context_list.push_back(new Context());
1499 Context* c = context_list[i];
1500
1501 MockHttpRequest* this_request = &request;
1502 if (i == 1)
1503 this_request = &delete_request;
1504
1505 c->result = cache.CreateTransaction(&c->trans);
1506 ASSERT_THAT(c->result, IsOk());
1507 EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
1508
1509 c->result = c->trans->Start(this_request, c->callback.callback(),
1510 NetLogWithSource());
1511 }
1512
1513 // All requests are waiting for the active entry.
1514 for (int i = 0; i < kNumTransactions; ++i) {
1515 Context* c = context_list[i];
1516 EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, c->trans->GetLoadState());
1517 }
1518
1519 // Allow all requests to move from the Create queue to the active entry.
1520 base::RunLoop().RunUntilIdle();
1521
1522 // The first request should be a writer at this point, and the subsequent
1523 // request should have passed the validation phase and doomed the existing
1524 // entry.
1525 EXPECT_TRUE(
1526 cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url));
1527
1528 EXPECT_EQ(2, cache.network_layer()->transaction_count());
1529 EXPECT_EQ(0, cache.disk_cache()->open_count());
1530 EXPECT_EQ(1, cache.disk_cache()->create_count());
1531
1532 // All requests depend on the writer, and the writer is between Start and
1533 // Read, i.e. idle.
1534 for (int i = 0; i < kNumTransactions; ++i) {
1535 Context* c = context_list[i];
1536 EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
1537 }
1538
1539 for (int i = 0; i < kNumTransactions; ++i) {
1540 Context* c = context_list[i];
1541 if (c->result == ERR_IO_PENDING)
1542 c->result = c->callback.WaitForResult();
1543 ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
1544 }
1545
1546 EXPECT_EQ(2, cache.network_layer()->transaction_count());
1547 EXPECT_EQ(0, cache.disk_cache()->open_count());
1548 EXPECT_EQ(1, cache.disk_cache()->create_count());
1549
1550 for (int i = 0; i < kNumTransactions; ++i) {
1551 Context* c = context_list[i];
1552 delete c;
1553 }
1554 }
1555
1556 // Tests that a transaction which is in validated queue can be destroyed without
1557 // any impact to other transactions.
1558 TEST(HttpCache, SimpleGET_ParallelValidationCancelValidated) {
1559 MockHttpCache cache;
1560
1561 MockHttpRequest request(kSimpleGET_Transaction);
1562
1563 std::vector<Context*> context_list;
1564 const int kNumTransactions = 2;
1565
1566 for (int i = 0; i < kNumTransactions; ++i) {
1567 context_list.push_back(new Context());
1568 Context* c = context_list[i];
1569
1570 c->result = cache.CreateTransaction(&c->trans);
1571 ASSERT_THAT(c->result, IsOk());
1572
1573 c->result =
1574 c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
1575 }
1576
1577 // Allow all requests to move from the Create queue to the active entry.
1578 base::RunLoop().RunUntilIdle();
1579
1580 // The first request should be a writer at this point, and the subsequent
1581 // requests should have completed validation.
1582
1583 EXPECT_EQ(1, cache.network_layer()->transaction_count());
1584 EXPECT_EQ(0, cache.disk_cache()->open_count());
1585 EXPECT_EQ(1, cache.disk_cache()->create_count());
1586
1587 EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
1588 EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
1589
1590 Context* c = context_list[1];
1591 delete c;
1592 context_list[1] = nullptr;
1593
1594 EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
1595
1596 // Complete the rest of the transactions.
1597 for (int i = 0; i < kNumTransactions; ++i) {
1598 if (i == 1)
1599 continue;
1600 Context* c = context_list[i];
1601 ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
1602 }
1603
1604 EXPECT_EQ(1, cache.network_layer()->transaction_count());
1605 EXPECT_EQ(0, cache.disk_cache()->open_count());
1606 EXPECT_EQ(1, cache.disk_cache()->create_count());
1607
1608 for (int i = 0; i < kNumTransactions; ++i) {
1609 if (i == 1)
1610 continue;
1611 Context* c = context_list[i];
1612 delete c;
1613 }
1614 }
1615
1616 // Tests that a transaction which is in readers can be destroyed without
1617 // any impact to other transactions.
1618 TEST(HttpCache, SimpleGET_ParallelValidationCancelReader) {
1619 MockHttpCache cache;
1620
1621 MockHttpRequest request(kSimpleGET_Transaction);
1622
1623 MockTransaction transaction(kSimpleGET_Transaction);
1624 transaction.load_flags |= LOAD_VALIDATE_CACHE;
1625 MockHttpRequest validate_request(transaction);
1626
1627 std::vector<Context*> context_list;
1628 int kNumTransactions = 4;
1629
1630 for (int i = 0; i < kNumTransactions; ++i) {
1631 context_list.push_back(new Context());
1632 Context* c = context_list[i];
1633
1634 c->result = cache.CreateTransaction(&c->trans);
1635 ASSERT_THAT(c->result, IsOk());
1636
1637 MockHttpRequest* this_request = &request;
1638 if (i == 3) {
1639 this_request = &validate_request;
1640 c->trans->SetBeforeNetworkStartCallback(
1641 base::Bind(&MockNetworkTransaction::DeferNetworkStart));
1642 }
1643
1644 c->result = c->trans->Start(this_request, c->callback.callback(),
1645 NetLogWithSource());
1646 }
1647
1648 // Allow all requests to move from the Create queue to the active entry.
1649 base::RunLoop().RunUntilIdle();
1650
1651 EXPECT_EQ(2, cache.network_layer()->transaction_count());
1652 EXPECT_EQ(0, cache.disk_cache()->open_count());
1653 EXPECT_EQ(1, cache.disk_cache()->create_count());
1654
1655 EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
1656 EXPECT_EQ(2, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
1657 EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
1658
1659 // Complete the response body.
1660 Context* c = context_list[0];
1661 ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
1662
1663 // Rest of the transactions should move to readers.
1664 EXPECT_FALSE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
1665 EXPECT_EQ(2, cache.GetCountReaders(kSimpleGET_Transaction.url));
1666 EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
1667 EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
1668
1669 // Add 2 new transactions.
1670 kNumTransactions = 6;
1671
1672 for (int i = 4; i < kNumTransactions; ++i) {
1673 context_list.push_back(new Context());
1674 Context* c = context_list[i];
1675
1676 c->result = cache.CreateTransaction(&c->trans);
1677 ASSERT_THAT(c->result, IsOk());
1678
1679 c->result =
1680 c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
1681 }
1682
1683 EXPECT_EQ(2, cache.GetCountAddToEntryQueue(kSimpleGET_Transaction.url));
1684
1685 // Delete a reader.
1686 c = context_list[1];
1687 delete c;
1688 context_list[1] = nullptr;
1689
1690 // Deleting the reader did not impact any other transaction.
1691 EXPECT_EQ(1, cache.GetCountReaders(kSimpleGET_Transaction.url));
1692 EXPECT_EQ(2, cache.GetCountAddToEntryQueue(kSimpleGET_Transaction.url));
1693 EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
1694
1695 // Resume network start for headers_transaction. It will doom the entry as it
1696 // will be a 200 and will go to network for the response body.
1697 c = context_list[3];
1698 c->trans->ResumeNetworkStart();
1699
1700 // The pending transactions will be added to a new entry.
1701 base::RunLoop().RunUntilIdle();
1702
1703 EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
1704 EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
1705
1706 // Complete the rest of the transactions.
1707 for (int i = 2; i < kNumTransactions; ++i) {
1708 Context* c = context_list[i];
1709 ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
1710 }
1711
1712 EXPECT_EQ(3, cache.network_layer()->transaction_count());
1713 EXPECT_EQ(0, cache.disk_cache()->open_count());
1714 EXPECT_EQ(2, cache.disk_cache()->create_count());
1715
1716 for (int i = 0; i < kNumTransactions; ++i) {
1717 if (i == 1)
1718 continue;
1719 Context* c = context_list[i];
1720 delete c;
1721 }
1722 }
1723
1724 // Tests that a transaction is in validated queue and writer is destroyed
1725 // leading to restarting the validated transaction.
1726 TEST(HttpCache, SimpleGET_ParallelValidationCancelWriter) {
1727 MockHttpCache cache;
1728
1729 MockHttpRequest request(kSimpleGET_Transaction);
1730
1731 MockTransaction transaction(kSimpleGET_Transaction);
1732 transaction.load_flags |= LOAD_VALIDATE_CACHE;
1733 MockHttpRequest validate_request(transaction);
1734
1735 std::vector<Context*> context_list;
1736 const int kNumTransactions = 3;
1737
1738 for (int i = 0; i < kNumTransactions; ++i) {
1739 context_list.push_back(new Context());
1740 Context* c = context_list[i];
1741
1742 c->result = cache.CreateTransaction(&c->trans);
1743 ASSERT_THAT(c->result, IsOk());
1744
1745 MockHttpRequest* this_request = &request;
1746 if (i == 2) {
1747 this_request = &validate_request;
1748 c->trans->SetBeforeNetworkStartCallback(
1749 base::Bind(&MockNetworkTransaction::DeferNetworkStart));
1750 }
1751
1752 c->result = c->trans->Start(this_request, c->callback.callback(),
1753 NetLogWithSource());
1754 }
1755
1756 // Allow all requests to move from the Create queue to the active entry.
1757 base::RunLoop().RunUntilIdle();
1758
1759 EXPECT_EQ(2, cache.network_layer()->transaction_count());
1760 EXPECT_EQ(0, cache.disk_cache()->open_count());
1761 EXPECT_EQ(1, cache.disk_cache()->create_count());
1762
1763 EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
1764 EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
1765 EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
1766
1767 // Deleting the writer at this point will lead to destroying the entry and
1768 // restarting the remaining transactions which will then create a new entry.
1769 Context* c = context_list[0];
1770 delete c;
1771 context_list[0] = nullptr;
1772
1773 // Resume network start for headers_transaction. It should be restarted due to
1774 // writer cancellation.
1775 c = context_list[2];
1776 c->trans->ResumeNetworkStart();
1777
1778 base::RunLoop().RunUntilIdle();
1779
1780 EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
1781 EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
1782
1783 // Resume network start for the transaction the second time.
1784 c->trans->ResumeNetworkStart();
1785 base::RunLoop().RunUntilIdle();
1786
1787 // Headers transaction would have doomed the new entry created.
1788 EXPECT_TRUE(
1789 cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url));
1790
1791 // Complete the rest of the transactions.
1792 for (int i = 1; i < kNumTransactions; ++i) {
1793 Context* c = context_list[i];
1794 ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
1795 }
1796
1797 EXPECT_EQ(4, cache.network_layer()->transaction_count());
1798 EXPECT_EQ(0, cache.disk_cache()->open_count());
1799 EXPECT_EQ(2, cache.disk_cache()->create_count());
1800
1801 for (int i = 1; i < kNumTransactions; ++i) {
1802 Context* c = context_list[i];
1803 delete c;
1804 }
1805 }
1806
1807 // Tests that a transaction is currently in headers phase and is destroyed
1808 // leading to destroying the entry.
1809 TEST(HttpCache, SimpleGET_ParallelValidationCancelHeaders) {
1810 MockHttpCache cache;
1811
1812 MockHttpRequest request(kSimpleGET_Transaction);
1813
1814 std::vector<Context*> context_list;
1815 const int kNumTransactions = 2;
1816
1817 for (int i = 0; i < kNumTransactions; ++i) {
1818 context_list.push_back(new Context());
1819 Context* c = context_list[i];
1820
1821 c->result = cache.CreateTransaction(&c->trans);
1822 ASSERT_THAT(c->result, IsOk());
1823
1824 if (i == 0)
1825 c->trans->SetBeforeNetworkStartCallback(
1826 base::Bind(&MockNetworkTransaction::DeferNetworkStart));
1827
1828 c->result =
1829 c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
1830 }
1831
1832 base::RunLoop().RunUntilIdle();
1833
1834 EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
1835 EXPECT_EQ(1, cache.GetCountAddToEntryQueue(kSimpleGET_Transaction.url));
1836
1837 EXPECT_EQ(1, cache.network_layer()->transaction_count());
1838 EXPECT_EQ(0, cache.disk_cache()->open_count());
1839 EXPECT_EQ(1, cache.disk_cache()->create_count());
1840
1841 // Delete the headers transaction.
1842 Context* c = context_list[0];
1843 delete c;
1844 context_list[0] = nullptr;
1845
1846 base::RunLoop().RunUntilIdle();
1847
1848 // Complete the rest of the transactions.
1849 for (int i = 0; i < kNumTransactions; ++i) {
1850 if (i == 0)
1851 continue;
1852 Context* c = context_list[i];
1853 ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
1854 }
1855
1856 EXPECT_EQ(2, cache.network_layer()->transaction_count());
1857 EXPECT_EQ(0, cache.disk_cache()->open_count());
1858 EXPECT_EQ(2, cache.disk_cache()->create_count());
1859
1860 for (int i = 1; i < kNumTransactions; ++i) {
1861 Context* c = context_list[i];
1862 delete c;
1863 }
1864 }
1865
1866 // Similar to the above test, except here cache write fails and the
1867 // validated transactions should be restarted.
1868 TEST(HttpCache, SimpleGET_ParallelValidationFailWrite) {
1869 MockHttpCache cache;
1870
1871 MockHttpRequest request(kSimpleGET_Transaction);
1872
1873 std::vector<Context*> context_list;
1874 const int kNumTransactions = 5;
1875
1876 for (int i = 0; i < kNumTransactions; ++i) {
1877 context_list.push_back(new Context());
1878 Context* c = context_list[i];
1879
1880 c->result = cache.CreateTransaction(&c->trans);
1881 ASSERT_THAT(c->result, IsOk());
1882 EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
1883
1884 c->result =
1885 c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
1886 }
1887
1888 // All requests are waiting for the active entry.
1889 for (int i = 0; i < kNumTransactions; ++i) {
1890 Context* c = context_list[i];
1891 EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, c->trans->GetLoadState());
1892 }
1893
1894 // Allow all requests to move from the Create queue to the active entry.
1895 base::RunLoop().RunUntilIdle();
1896
1897 // The first request should be a writer at this point, and the subsequent
1898 // requests should have passed the validation phase and waiting for the
1899 // response to be written to the cache before they can read.
1900 EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
1901 EXPECT_EQ(4, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
1902
1903 // All requests depend on the writer, and the writer is between Start and
1904 // Read, i.e. idle.
1905 for (int i = 0; i < kNumTransactions; ++i) {
1906 Context* c = context_list[i];
1907 EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
1908 }
1909
1910 // The first request should be a writer at this point, and the subsequent
1911 // requests should have passed the validation phase and waiting for the
1912 // response to be written to the cache before they can read.
1913
1914 EXPECT_EQ(1, cache.network_layer()->transaction_count());
1915 EXPECT_EQ(0, cache.disk_cache()->open_count());
1916 EXPECT_EQ(1, cache.disk_cache()->create_count());
1917
1918 // Fail the request.
1919 cache.disk_cache()->set_soft_failures(true);
1920 // We have to open the entry again to propagate the failure flag.
1921 disk_cache::Entry* en;
1922 cache.OpenBackendEntry(kSimpleGET_Transaction.url, &en);
1923 en->Close();
1924
1925 for (int i = 0; i < kNumTransactions; ++i) {
1926 Context* c = context_list[i];
1927 if (c->result == ERR_IO_PENDING)
1928 c->result = c->callback.WaitForResult();
1929 if (i == 1) {
1930 // The earlier entry must be destroyed and its disk entry doomed.
1931 EXPECT_TRUE(
1932 cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url));
1933 }
1934 ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
1935 }
1936
1937 // Since validated transactions were restarted and new entry read/write
1938 // operations would also fail, all requests would have gone to the network.
1939 EXPECT_EQ(5, cache.network_layer()->transaction_count());
1940 EXPECT_EQ(1, cache.disk_cache()->open_count());
1941 EXPECT_EQ(5, cache.disk_cache()->create_count());
1942
1943 for (int i = 0; i < kNumTransactions; ++i) {
1944 Context* c = context_list[i];
1945 delete c;
1946 }
1947 }
1948
1410 // This is a test for http://code.google.com/p/chromium/issues/detail?id=4769. 1949 // This is a test for http://code.google.com/p/chromium/issues/detail?id=4769.
1411 // If cancelling a request is racing with another request for the same resource 1950 // If cancelling a request is racing with another request for the same resource
1412 // finishing, we have to make sure that we remove both transactions from the 1951 // finishing, we have to make sure that we remove both transactions from the
1413 // entry. 1952 // entry.
1414 TEST(HttpCache, SimpleGET_RacingReaders) { 1953 TEST(HttpCache, SimpleGET_RacingReaders) {
1415 MockHttpCache cache; 1954 MockHttpCache cache;
1416 1955
1417 MockHttpRequest request(kSimpleGET_Transaction); 1956 MockHttpRequest request(kSimpleGET_Transaction);
1418 MockHttpRequest reader_request(kSimpleGET_Transaction); 1957 MockHttpRequest reader_request(kSimpleGET_Transaction);
1419 reader_request.load_flags = LOAD_ONLY_FROM_CACHE | LOAD_SKIP_CACHE_VALIDATION; 1958 reader_request.load_flags = LOAD_ONLY_FROM_CACHE | LOAD_SKIP_CACHE_VALIDATION;
(...skipping 24 matching lines...) Expand all
1444 1983
1445 EXPECT_EQ(1, cache.network_layer()->transaction_count()); 1984 EXPECT_EQ(1, cache.network_layer()->transaction_count());
1446 EXPECT_EQ(0, cache.disk_cache()->open_count()); 1985 EXPECT_EQ(0, cache.disk_cache()->open_count());
1447 EXPECT_EQ(1, cache.disk_cache()->create_count()); 1986 EXPECT_EQ(1, cache.disk_cache()->create_count());
1448 1987
1449 Context* c = context_list[0]; 1988 Context* c = context_list[0];
1450 ASSERT_THAT(c->result, IsError(ERR_IO_PENDING)); 1989 ASSERT_THAT(c->result, IsError(ERR_IO_PENDING));
1451 c->result = c->callback.WaitForResult(); 1990 c->result = c->callback.WaitForResult();
1452 ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction); 1991 ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
1453 1992
1454 // Now we have 2 active readers and two queued transactions. 1993 // Now all transactions should be waiting for read to be invoked. Two readers
1455 1994 // are because of the load flags and remaining two transactions were converted
1995 // to readers after skipping validation. Note that the remaining two went on
1996 // to process the headers in parallel with readers presnt on the entry.
1456 EXPECT_EQ(LOAD_STATE_IDLE, context_list[2]->trans->GetLoadState()); 1997 EXPECT_EQ(LOAD_STATE_IDLE, context_list[2]->trans->GetLoadState());
1457 EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, 1998 EXPECT_EQ(LOAD_STATE_IDLE, context_list[3]->trans->GetLoadState());
1458 context_list[3]->trans->GetLoadState());
1459 1999
1460 c = context_list[1]; 2000 c = context_list[1];
1461 ASSERT_THAT(c->result, IsError(ERR_IO_PENDING)); 2001 ASSERT_THAT(c->result, IsError(ERR_IO_PENDING));
1462 c->result = c->callback.WaitForResult(); 2002 c->result = c->callback.WaitForResult();
1463 if (c->result == OK) 2003 if (c->result == OK)
1464 ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction); 2004 ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
1465 2005
1466 // At this point we have one reader, two pending transactions and a task on 2006 // At this point we have one reader, two pending transactions and a task on
1467 // the queue to move to the next transaction. Now we cancel the request that 2007 // the queue to move to the next transaction. Now we cancel the request that
1468 // is the current reader, and expect the queued task to be able to start the 2008 // is the current reader, and expect the queued task to be able to start the
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
1513 ASSERT_THAT(c->result, IsOk()); 2053 ASSERT_THAT(c->result, IsOk());
1514 2054
1515 MockHttpRequest* this_request = &request; 2055 MockHttpRequest* this_request = &request;
1516 if (i == 3) 2056 if (i == 3)
1517 this_request = &writer_request; 2057 this_request = &writer_request;
1518 2058
1519 c->result = c->trans->Start(this_request, c->callback.callback(), 2059 c->result = c->trans->Start(this_request, c->callback.callback(),
1520 NetLogWithSource()); 2060 NetLogWithSource());
1521 } 2061 }
1522 2062
2063 base::RunLoop().RunUntilIdle();
2064
1523 // The first request should be a writer at this point, and the two subsequent 2065 // The first request should be a writer at this point, and the two subsequent
1524 // requests should be pending. The last request doomed the first entry. 2066 // requests should be pending. The last request doomed the first entry.
1525 2067
1526 EXPECT_EQ(2, cache.network_layer()->transaction_count()); 2068 EXPECT_EQ(2, cache.network_layer()->transaction_count());
1527 2069
1528 // Cancel the first queued transaction. 2070 // Cancel the second transaction. Note that this and the 3rd transactions
2071 // would have completed their headers phase and would be waiting in the
2072 // done_headers_queue when the 2nd transaction is cancelled.
1529 context_list[1].reset(); 2073 context_list[1].reset();
1530 2074
1531 for (int i = 0; i < kNumTransactions; ++i) { 2075 for (int i = 0; i < kNumTransactions; ++i) {
1532 if (i == 1) 2076 if (i == 1)
1533 continue; 2077 continue;
1534 Context* c = context_list[i].get(); 2078 Context* c = context_list[i].get();
1535 ASSERT_THAT(c->result, IsError(ERR_IO_PENDING)); 2079 ASSERT_THAT(c->result, IsError(ERR_IO_PENDING));
1536 c->result = c->callback.WaitForResult(); 2080 c->result = c->callback.WaitForResult();
1537 ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction); 2081 ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
1538 } 2082 }
(...skipping 21 matching lines...) Expand all
1560 ASSERT_THAT(c->result, IsOk()); 2104 ASSERT_THAT(c->result, IsOk());
1561 2105
1562 c->result = 2106 c->result =
1563 c->trans->Start(&request, c->callback.callback(), NetLogWithSource()); 2107 c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
1564 } 2108 }
1565 2109
1566 // Allow all requests to move from the Create queue to the active entry. 2110 // Allow all requests to move from the Create queue to the active entry.
1567 base::RunLoop().RunUntilIdle(); 2111 base::RunLoop().RunUntilIdle();
1568 2112
1569 // The first request should be a writer at this point, and the subsequent 2113 // The first request should be a writer at this point, and the subsequent
1570 // requests should be pending. 2114 // requests should have completed validation. Since the validation does not
2115 // result in a match, a new entry would be created.
1571 2116
1572 EXPECT_EQ(1, cache.network_layer()->transaction_count()); 2117 EXPECT_EQ(3, cache.network_layer()->transaction_count());
1573 EXPECT_EQ(0, cache.disk_cache()->open_count()); 2118 EXPECT_EQ(0, cache.disk_cache()->open_count());
1574 EXPECT_EQ(1, cache.disk_cache()->create_count()); 2119 EXPECT_EQ(2, cache.disk_cache()->create_count());
1575 2120
1576 // Now, make sure that the second request asks for the entry not to be stored. 2121 // Now, make sure that the second request asks for the entry not to be stored.
1577 request_handler.set_no_store(true); 2122 request_handler.set_no_store(true);
1578 2123
1579 for (int i = 0; i < kNumTransactions; ++i) { 2124 for (int i = 0; i < kNumTransactions; ++i) {
1580 Context* c = context_list[i]; 2125 Context* c = context_list[i];
1581 if (c->result == ERR_IO_PENDING) 2126 if (c->result == ERR_IO_PENDING)
1582 c->result = c->callback.WaitForResult(); 2127 c->result = c->callback.WaitForResult();
1583 ReadAndVerifyTransaction(c->trans.get(), kFastNoStoreGET_Transaction); 2128 ReadAndVerifyTransaction(c->trans.get(), kFastNoStoreGET_Transaction);
1584 delete c; 2129 delete c;
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
1618 2163
1619 EXPECT_EQ(1, cache.network_layer()->transaction_count()); 2164 EXPECT_EQ(1, cache.network_layer()->transaction_count());
1620 EXPECT_EQ(0, cache.disk_cache()->open_count()); 2165 EXPECT_EQ(0, cache.disk_cache()->open_count());
1621 EXPECT_EQ(1, cache.disk_cache()->create_count()); 2166 EXPECT_EQ(1, cache.disk_cache()->create_count());
1622 2167
1623 for (int i = 0; i < kNumTransactions; ++i) { 2168 for (int i = 0; i < kNumTransactions; ++i) {
1624 Context* c = context_list[i]; 2169 Context* c = context_list[i];
1625 if (c->result == ERR_IO_PENDING) 2170 if (c->result == ERR_IO_PENDING)
1626 c->result = c->callback.WaitForResult(); 2171 c->result = c->callback.WaitForResult();
1627 // Destroy only the first transaction. 2172 // Destroy only the first transaction.
2173 // This should lead to all transactions to restart, even those that have
2174 // validated themselves and were waiting for the writer transaction to
2175 // complete writing to the cache.
1628 if (i == 0) { 2176 if (i == 0) {
1629 delete c; 2177 delete c;
1630 context_list[i] = NULL; 2178 context_list[i] = NULL;
1631 } 2179 }
1632 } 2180 }
1633 2181
1634 // Complete the rest of the transactions. 2182 // Complete the rest of the transactions.
1635 for (int i = 1; i < kNumTransactions; ++i) { 2183 for (int i = 1; i < kNumTransactions; ++i) {
1636 Context* c = context_list[i]; 2184 Context* c = context_list[i];
1637 ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction); 2185 ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
(...skipping 895 matching lines...) Expand 10 before | Expand all | Expand 10 after
2533 } 3081 }
2534 3082
2535 // Helper that does 4 requests using HttpCache: 3083 // Helper that does 4 requests using HttpCache:
2536 // 3084 //
2537 // (1) loads |kUrl| -- expects |net_response_1| to be returned. 3085 // (1) loads |kUrl| -- expects |net_response_1| to be returned.
2538 // (2) loads |kUrl| from cache only -- expects |net_response_1| to be returned. 3086 // (2) loads |kUrl| from cache only -- expects |net_response_1| to be returned.
2539 // (3) loads |kUrl| using |extra_request_headers| -- expects |net_response_2| to 3087 // (3) loads |kUrl| using |extra_request_headers| -- expects |net_response_2| to
2540 // be returned. 3088 // be returned.
2541 // (4) loads |kUrl| from cache only -- expects |cached_response_2| to be 3089 // (4) loads |kUrl| from cache only -- expects |cached_response_2| to be
2542 // returned. 3090 // returned.
3091 // The entry will be created once and will be opened for the 3 subsequent
3092 // requests.
2543 static void ConditionalizedRequestUpdatesCacheHelper( 3093 static void ConditionalizedRequestUpdatesCacheHelper(
2544 const Response& net_response_1, 3094 const Response& net_response_1,
2545 const Response& net_response_2, 3095 const Response& net_response_2,
2546 const Response& cached_response_2, 3096 const Response& cached_response_2,
2547 const char* extra_request_headers) { 3097 const char* extra_request_headers) {
2548 MockHttpCache cache; 3098 MockHttpCache cache;
2549 3099
2550 // The URL we will be requesting. 3100 // The URL we will be requesting.
2551 const char kUrl[] = "http://foobar.com/main.css"; 3101 const char kUrl[] = "http://foobar.com/main.css";
2552 3102
(...skipping 3555 matching lines...) Expand 10 before | Expand all | Expand 10 after
6108 pending->trans->Start(&request, pending->callback.callback(), 6658 pending->trans->Start(&request, pending->callback.callback(),
6109 NetLogWithSource())); 6659 NetLogWithSource()));
6110 EXPECT_THAT(c->callback.GetResult(rv), IsOk()); 6660 EXPECT_THAT(c->callback.GetResult(rv), IsOk());
6111 6661
6112 // Make sure that the entry has some data stored. 6662 // Make sure that the entry has some data stored.
6113 scoped_refptr<IOBufferWithSize> buf(new IOBufferWithSize(5)); 6663 scoped_refptr<IOBufferWithSize> buf(new IOBufferWithSize(5));
6114 rv = c->trans->Read(buf.get(), buf->size(), c->callback.callback()); 6664 rv = c->trans->Read(buf.get(), buf->size(), c->callback.callback());
6115 EXPECT_EQ(5, c->callback.GetResult(rv)); 6665 EXPECT_EQ(5, c->callback.GetResult(rv));
6116 6666
6117 // Cancel the requests. 6667 // Cancel the requests.
6668 // Since |pending| is currently vaidating the already written headers
6669 // it will be restarted as well.
6118 delete c; 6670 delete c;
6119 delete pending; 6671 delete pending;
6120 6672
6121 EXPECT_EQ(1, cache.network_layer()->transaction_count()); 6673 EXPECT_EQ(1, cache.network_layer()->transaction_count());
6122 EXPECT_EQ(1, cache.disk_cache()->open_count()); 6674 EXPECT_EQ(1, cache.disk_cache()->open_count());
6123 EXPECT_EQ(2, cache.disk_cache()->create_count()); 6675 EXPECT_EQ(1, cache.disk_cache()->create_count());
6124 6676
6125 base::RunLoop().RunUntilIdle(); 6677 base::RunLoop().RunUntilIdle();
6126 RemoveMockTransaction(&transaction); 6678 RemoveMockTransaction(&transaction);
6127 } 6679 }
6128 6680
6129 // Tests that we delete truncated entries if the server changes its mind midway. 6681 // Tests that we delete truncated entries if the server changes its mind midway.
6130 TEST(HttpCache, GET_IncompleteResource2) { 6682 TEST(HttpCache, GET_IncompleteResource2) {
6131 MockHttpCache cache; 6683 MockHttpCache cache;
6132 AddMockTransaction(&kRangeGET_TransactionOK); 6684 AddMockTransaction(&kRangeGET_TransactionOK);
6133 6685
(...skipping 704 matching lines...) Expand 10 before | Expand all | Expand 10 after
6838 7390
6839 RunTransactionTestWithResponseInfo(cache.http_cache(), kSimpleGET_Transaction, 7391 RunTransactionTestWithResponseInfo(cache.http_cache(), kSimpleGET_Transaction,
6840 &response); 7392 &response);
6841 EXPECT_TRUE(response.metadata.get() == NULL); 7393 EXPECT_TRUE(response.metadata.get() == NULL);
6842 7394
6843 EXPECT_EQ(1, cache.network_layer()->transaction_count()); 7395 EXPECT_EQ(1, cache.network_layer()->transaction_count());
6844 EXPECT_EQ(2, cache.disk_cache()->open_count()); 7396 EXPECT_EQ(2, cache.disk_cache()->open_count());
6845 EXPECT_EQ(1, cache.disk_cache()->create_count()); 7397 EXPECT_EQ(1, cache.disk_cache()->create_count());
6846 } 7398 }
6847 7399
6848 // Tests that if a metadata writer transaction hits cache lock timeout, it will
6849 // error out.
6850 TEST(HttpCache, WriteMetadata_CacheLockTimeout) {
6851 MockHttpCache cache;
6852
6853 // Write to the cache
6854 HttpResponseInfo response;
6855 RunTransactionTestWithResponseInfo(cache.http_cache(), kSimpleGET_Transaction,
6856 &response);
6857 EXPECT_FALSE(response.metadata.get());
6858
6859 MockHttpRequest request(kSimpleGET_Transaction);
6860 Context c1;
6861 ASSERT_THAT(cache.CreateTransaction(&c1.trans), IsOk());
6862 ASSERT_EQ(ERR_IO_PENDING, c1.trans->Start(&request, c1.callback.callback(),
6863 NetLogWithSource()));
6864
6865 cache.SimulateCacheLockTimeout();
6866
6867 // Write meta data to the same entry.
6868 scoped_refptr<IOBufferWithSize> buf(new IOBufferWithSize(50));
6869 memset(buf->data(), 0, buf->size());
6870 base::strlcpy(buf->data(), "Hi there", buf->size());
6871 cache.http_cache()->WriteMetadata(GURL(kSimpleGET_Transaction.url),
6872 DEFAULT_PRIORITY, response.response_time,
6873 buf.get(), buf->size());
6874
6875 // Release the buffer before the operation takes place.
6876 buf = NULL;
6877
6878 // Makes sure we finish pending operations.
6879 base::RunLoop().RunUntilIdle();
6880
6881 RunTransactionTestWithResponseInfo(cache.http_cache(), kSimpleGET_Transaction,
6882 &response);
6883
6884 // The writer transaction should fail due to cache lock timeout.
6885 ASSERT_FALSE(response.metadata.get());
6886 }
6887
6888 // Tests that we ignore VARY checks when writing metadata since the request 7400 // Tests that we ignore VARY checks when writing metadata since the request
6889 // headers for the WriteMetadata transaction are made up. 7401 // headers for the WriteMetadata transaction are made up.
6890 TEST(HttpCache, WriteMetadata_IgnoreVary) { 7402 TEST(HttpCache, WriteMetadata_IgnoreVary) {
6891 MockHttpCache cache; 7403 MockHttpCache cache;
6892 7404
6893 // Write to the cache 7405 // Write to the cache
6894 HttpResponseInfo response; 7406 HttpResponseInfo response;
6895 ScopedMockTransaction transaction(kSimpleGET_Transaction); 7407 ScopedMockTransaction transaction(kSimpleGET_Transaction);
6896 transaction.request_headers = "accept-encoding: gzip\r\n"; 7408 transaction.request_headers = "accept-encoding: gzip\r\n";
6897 transaction.response_headers = 7409 transaction.response_headers =
(...skipping 307 matching lines...) Expand 10 before | Expand all | Expand 10 after
7205 MockHttpRequest request(mock_transaction); 7717 MockHttpRequest request(mock_transaction);
7206 7718
7207 { 7719 {
7208 std::unique_ptr<HttpTransaction> trans; 7720 std::unique_ptr<HttpTransaction> trans;
7209 ASSERT_THAT(cache.CreateTransaction(&trans), IsOk()); 7721 ASSERT_THAT(cache.CreateTransaction(&trans), IsOk());
7210 7722
7211 int rv = trans->Start(&request, callback.callback(), NetLogWithSource()); 7723 int rv = trans->Start(&request, callback.callback(), NetLogWithSource());
7212 EXPECT_THAT(callback.GetResult(rv), IsOk()); 7724 EXPECT_THAT(callback.GetResult(rv), IsOk());
7213 7725
7214 trans->StopCaching(); 7726 trans->StopCaching();
7215
7216 scoped_refptr<IOBuffer> buf(new IOBuffer(256));
7217 rv = trans->Read(buf.get(), 10, callback.callback());
7218 EXPECT_EQ(callback.GetResult(rv), 10);
7219 } 7727 }
7220 RemoveMockTransaction(&mock_transaction); 7728 RemoveMockTransaction(&mock_transaction);
7221 7729
7222 // Make sure that the ActiveEntry is gone. 7730 // Make sure that the ActiveEntry is gone.
7223 base::RunLoop().RunUntilIdle(); 7731 base::RunLoop().RunUntilIdle();
7224 7732
7225 // Verify that the entry is gone. 7733 // Verify that the entry is gone.
7226 RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction); 7734 RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
7227 7735
7228 EXPECT_EQ(2, cache.network_layer()->transaction_count()); 7736 EXPECT_EQ(2, cache.network_layer()->transaction_count());
(...skipping 1031 matching lines...) Expand 10 before | Expand all | Expand 10 after
8260 ASSERT_TRUE(attrs->GetDictionary( 8768 ASSERT_TRUE(attrs->GetDictionary(
8261 base::trace_event::MemoryAllocatorDump::kNameSize, &size_attrs)); 8769 base::trace_event::MemoryAllocatorDump::kNameSize, &size_attrs));
8262 std::string size; 8770 std::string size;
8263 ASSERT_TRUE(size_attrs->GetString("value", &size)); 8771 ASSERT_TRUE(size_attrs->GetString("value", &size));
8264 int actual_size = 0; 8772 int actual_size = 0;
8265 ASSERT_TRUE(base::HexStringToInt(size, &actual_size)); 8773 ASSERT_TRUE(base::HexStringToInt(size, &actual_size));
8266 ASSERT_LT(0, actual_size); 8774 ASSERT_LT(0, actual_size);
8267 } 8775 }
8268 8776
8269 } // namespace net 8777 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698