| OLD | NEW | 
|    1 // Copyright 2014 The Chromium Authors. All rights reserved. |    1 // Copyright 2014 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 "base/bind.h" |    5 #include "base/bind.h" | 
|    6 #include "base/files/file_util.h" |    6 #include "base/files/file_util.h" | 
|    7 #include "base/files/scoped_temp_dir.h" |    7 #include "base/files/scoped_temp_dir.h" | 
|    8 #include "base/memory/ref_counted.h" |    8 #include "base/memory/ref_counted.h" | 
|    9 #include "base/memory/scoped_vector.h" |    9 #include "base/memory/scoped_vector.h" | 
|   10 #include "base/message_loop/message_loop.h" |   10 #include "base/message_loop/message_loop.h" | 
|   11 #include "base/run_loop.h" |   11 #include "base/run_loop.h" | 
|   12 #include "base/stl_util.h" |   12 #include "base/stl_util.h" | 
 |   13 #include "crypto/ec_private_key.h" | 
|   13 #include "net/base/test_data_directory.h" |   14 #include "net/base/test_data_directory.h" | 
 |   15 #include "net/cert/asn1_util.h" | 
|   14 #include "net/extras/sqlite/sqlite_channel_id_store.h" |   16 #include "net/extras/sqlite/sqlite_channel_id_store.h" | 
 |   17 #include "net/ssl/channel_id_service.h" | 
|   15 #include "net/ssl/ssl_client_cert_type.h" |   18 #include "net/ssl/ssl_client_cert_type.h" | 
|   16 #include "net/test/cert_test_util.h" |   19 #include "net/test/cert_test_util.h" | 
 |   20 #include "net/test/channel_id_test_util.h" | 
|   17 #include "sql/statement.h" |   21 #include "sql/statement.h" | 
|   18 #include "testing/gtest/include/gtest/gtest.h" |   22 #include "testing/gtest/include/gtest/gtest.h" | 
|   19  |   23  | 
|   20 namespace net { |   24 namespace net { | 
|   21  |   25  | 
|   22 const base::FilePath::CharType kTestChannelIDFilename[] = |   26 const base::FilePath::CharType kTestChannelIDFilename[] = | 
|   23     FILE_PATH_LITERAL("ChannelID"); |   27     FILE_PATH_LITERAL("ChannelID"); | 
|   24  |   28  | 
|   25 class SQLiteChannelIDStoreTest : public testing::Test { |   29 class SQLiteChannelIDStoreTest : public testing::Test { | 
|   26  public: |   30  public: | 
|   27   void Load(ScopedVector<DefaultChannelIDStore::ChannelID>* channel_ids) { |   31   void Load(ScopedVector<DefaultChannelIDStore::ChannelID>* channel_ids) { | 
|   28     base::RunLoop run_loop; |   32     base::RunLoop run_loop; | 
|   29     store_->Load(base::Bind(&SQLiteChannelIDStoreTest::OnLoaded, |   33     store_->Load(base::Bind(&SQLiteChannelIDStoreTest::OnLoaded, | 
|   30                             base::Unretained(this), |   34                             base::Unretained(this), | 
|   31                             &run_loop)); |   35                             &run_loop)); | 
|   32     run_loop.Run(); |   36     run_loop.Run(); | 
|   33     channel_ids->swap(channel_ids_); |   37     channel_ids->swap(channel_ids_); | 
|   34     channel_ids_.clear(); |   38     channel_ids_.clear(); | 
|   35   } |   39   } | 
|   36  |   40  | 
|   37   void OnLoaded( |   41   void OnLoaded( | 
|   38       base::RunLoop* run_loop, |   42       base::RunLoop* run_loop, | 
|   39       scoped_ptr<ScopedVector<DefaultChannelIDStore::ChannelID> > channel_ids) { |   43       scoped_ptr<ScopedVector<DefaultChannelIDStore::ChannelID> > channel_ids) { | 
|   40     channel_ids_.swap(*channel_ids); |   44     channel_ids_.swap(*channel_ids); | 
|   41     run_loop->Quit(); |   45     run_loop->Quit(); | 
|   42   } |   46   } | 
|   43  |   47  | 
|   44  protected: |   48  protected: | 
|   45   static void ReadTestKeyAndCert(std::string* key, std::string* cert) { |   49   static void ReadTestKeyAndCert(std::string* key_data, | 
 |   50                                  std::string* cert_data, | 
 |   51                                  scoped_ptr<crypto::ECPrivateKey>* key) { | 
|   46     base::FilePath key_path = |   52     base::FilePath key_path = | 
|   47         GetTestCertsDirectory().AppendASCII("unittest.originbound.key.der"); |   53         GetTestCertsDirectory().AppendASCII("unittest.originbound.key.der"); | 
|   48     base::FilePath cert_path = |   54     base::FilePath cert_path = | 
|   49         GetTestCertsDirectory().AppendASCII("unittest.originbound.der"); |   55         GetTestCertsDirectory().AppendASCII("unittest.originbound.der"); | 
|   50     ASSERT_TRUE(base::ReadFileToString(key_path, key)); |   56     ASSERT_TRUE(base::ReadFileToString(key_path, key_data)); | 
|   51     ASSERT_TRUE(base::ReadFileToString(cert_path, cert)); |   57     ASSERT_TRUE(base::ReadFileToString(cert_path, cert_data)); | 
 |   58     std::vector<uint8> private_key(key_data->size()); | 
 |   59     memcpy(vector_as_array(&private_key), key_data->data(), key_data->size()); | 
 |   60     base::StringPiece spki; | 
 |   61     ASSERT_TRUE(asn1::ExtractSPKIFromDERCert(*cert_data, &spki)); | 
 |   62     std::vector<uint8> public_key(spki.size()); | 
 |   63     memcpy(vector_as_array(&public_key), spki.data(), spki.size()); | 
 |   64     key->reset(crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo( | 
 |   65         ChannelIDService::kEPKIPassword, private_key, public_key)); | 
|   52   } |   66   } | 
|   53  |   67  | 
|   54   static base::Time GetTestCertExpirationTime() { |   68   static base::Time GetTestCertExpirationTime() { | 
|   55     // Cert expiration time from 'dumpasn1 unittest.originbound.der': |   69     // Cert expiration time from 'openssl asn1parse -inform der -in | 
|   56     // GeneralizedTime 19/11/2111 02:23:45 GMT |   70     // unittest.originbound.der': | 
 |   71     // UTCTIME           :160507022239Z | 
|   57     // base::Time::FromUTCExploded can't generate values past 2038 on 32-bit |   72     // base::Time::FromUTCExploded can't generate values past 2038 on 32-bit | 
|   58     // linux, so we use the raw value here. |   73     // linux, so we use the raw value here. | 
|   59     return base::Time::FromInternalValue(GG_INT64_C(16121816625000000)); |   74     base::Time::Exploded exploded_time; | 
 |   75     exploded_time.year = 2016; | 
 |   76     exploded_time.month = 5; | 
 |   77     exploded_time.day_of_week = 0;  // Unused. | 
 |   78     exploded_time.day_of_month = 7; | 
 |   79     exploded_time.hour = 2; | 
 |   80     exploded_time.minute = 22; | 
 |   81     exploded_time.second = 39; | 
 |   82     exploded_time.millisecond = 0; | 
 |   83     return base::Time::FromUTCExploded(exploded_time); | 
|   60   } |   84   } | 
|   61  |   85  | 
|   62   static base::Time GetTestCertCreationTime() { |   86   static base::Time GetTestCertCreationTime() { | 
|   63     // UTCTime 13/12/2011 02:23:45 GMT |   87     // UTCTIME           :150508022239Z | 
|   64     base::Time::Exploded exploded_time; |   88     base::Time::Exploded exploded_time; | 
|   65     exploded_time.year = 2011; |   89     exploded_time.year = 2015; | 
|   66     exploded_time.month = 12; |   90     exploded_time.month = 5; | 
|   67     exploded_time.day_of_week = 0;  // Unused. |   91     exploded_time.day_of_week = 0;  // Unused. | 
|   68     exploded_time.day_of_month = 13; |   92     exploded_time.day_of_month = 8; | 
|   69     exploded_time.hour = 2; |   93     exploded_time.hour = 2; | 
|   70     exploded_time.minute = 23; |   94     exploded_time.minute = 22; | 
|   71     exploded_time.second = 45; |   95     exploded_time.second = 39; | 
|   72     exploded_time.millisecond = 0; |   96     exploded_time.millisecond = 0; | 
|   73     return base::Time::FromUTCExploded(exploded_time); |   97     return base::Time::FromUTCExploded(exploded_time); | 
|   74   } |   98   } | 
|   75  |   99  | 
|   76   void SetUp() override { |  100   void SetUp() override { | 
|   77     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |  101     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); | 
|   78     store_ = new SQLiteChannelIDStore( |  102     store_ = new SQLiteChannelIDStore( | 
|   79         temp_dir_.path().Append(kTestChannelIDFilename), |  103         temp_dir_.path().Append(kTestChannelIDFilename), | 
|   80         base::MessageLoopProxy::current()); |  104         base::MessageLoopProxy::current()); | 
|   81     ScopedVector<DefaultChannelIDStore::ChannelID> channel_ids; |  105     ScopedVector<DefaultChannelIDStore::ChannelID> channel_ids; | 
|   82     Load(&channel_ids); |  106     Load(&channel_ids); | 
|   83     ASSERT_EQ(0u, channel_ids.size()); |  107     ASSERT_EQ(0u, channel_ids.size()); | 
|   84     // Make sure the store gets written at least once. |  108     // Make sure the store gets written at least once. | 
|   85     store_->AddChannelID( |  109     google_key_.reset(crypto::ECPrivateKey::Create()); | 
|   86         DefaultChannelIDStore::ChannelID("google.com", |  110     store_->AddChannelID(DefaultChannelIDStore::ChannelID( | 
|   87                                          base::Time::FromInternalValue(1), |  111         "google.com", base::Time::FromInternalValue(1), | 
|   88                                          base::Time::FromInternalValue(2), |  112         make_scoped_ptr(google_key_->Copy()))); | 
|   89                                          "a", |  | 
|   90                                          "b")); |  | 
|   91   } |  113   } | 
|   92  |  114  | 
|   93   base::ScopedTempDir temp_dir_; |  115   base::ScopedTempDir temp_dir_; | 
|   94   scoped_refptr<SQLiteChannelIDStore> store_; |  116   scoped_refptr<SQLiteChannelIDStore> store_; | 
|   95   ScopedVector<DefaultChannelIDStore::ChannelID> channel_ids_; |  117   ScopedVector<DefaultChannelIDStore::ChannelID> channel_ids_; | 
 |  118   scoped_ptr<crypto::ECPrivateKey> google_key_; | 
|   96 }; |  119 }; | 
|   97  |  120  | 
|   98 // Test if data is stored as expected in the SQLite database. |  121 // Test if data is stored as expected in the SQLite database. | 
|   99 TEST_F(SQLiteChannelIDStoreTest, TestPersistence) { |  122 TEST_F(SQLiteChannelIDStoreTest, TestPersistence) { | 
|  100   store_->AddChannelID( |  123   scoped_ptr<crypto::ECPrivateKey> foo_key(crypto::ECPrivateKey::Create()); | 
|  101       DefaultChannelIDStore::ChannelID("foo.com", |  124   store_->AddChannelID(DefaultChannelIDStore::ChannelID( | 
|  102                                        base::Time::FromInternalValue(3), |  125       "foo.com", base::Time::FromInternalValue(3), | 
|  103                                        base::Time::FromInternalValue(4), |  126       make_scoped_ptr(foo_key->Copy()))); | 
|  104                                        "c", |  | 
|  105                                        "d")); |  | 
|  106  |  127  | 
|  107   ScopedVector<DefaultChannelIDStore::ChannelID> channel_ids; |  128   ScopedVector<DefaultChannelIDStore::ChannelID> channel_ids; | 
|  108   // Replace the store effectively destroying the current one and forcing it |  129   // Replace the store effectively destroying the current one and forcing it | 
|  109   // to write its data to disk. Then we can see if after loading it again it |  130   // to write its data to disk. Then we can see if after loading it again it | 
|  110   // is still there. |  131   // is still there. | 
|  111   store_ = NULL; |  132   store_ = NULL; | 
|  112   // Make sure we wait until the destructor has run. |  133   // Make sure we wait until the destructor has run. | 
|  113   base::RunLoop().RunUntilIdle(); |  134   base::RunLoop().RunUntilIdle(); | 
|  114   store_ = |  135   store_ = | 
|  115       new SQLiteChannelIDStore(temp_dir_.path().Append(kTestChannelIDFilename), |  136       new SQLiteChannelIDStore(temp_dir_.path().Append(kTestChannelIDFilename), | 
|  116                                base::MessageLoopProxy::current()); |  137                                base::MessageLoopProxy::current()); | 
|  117  |  138  | 
|  118   // Reload and test for persistence |  139   // Reload and test for persistence | 
|  119   Load(&channel_ids); |  140   Load(&channel_ids); | 
|  120   ASSERT_EQ(2U, channel_ids.size()); |  141   ASSERT_EQ(2U, channel_ids.size()); | 
|  121   DefaultChannelIDStore::ChannelID* goog_channel_id; |  142   DefaultChannelIDStore::ChannelID* goog_channel_id; | 
|  122   DefaultChannelIDStore::ChannelID* foo_channel_id; |  143   DefaultChannelIDStore::ChannelID* foo_channel_id; | 
|  123   if (channel_ids[0]->server_identifier() == "google.com") { |  144   if (channel_ids[0]->server_identifier() == "google.com") { | 
|  124     goog_channel_id = channel_ids[0]; |  145     goog_channel_id = channel_ids[0]; | 
|  125     foo_channel_id = channel_ids[1]; |  146     foo_channel_id = channel_ids[1]; | 
|  126   } else { |  147   } else { | 
|  127     goog_channel_id = channel_ids[1]; |  148     goog_channel_id = channel_ids[1]; | 
|  128     foo_channel_id = channel_ids[0]; |  149     foo_channel_id = channel_ids[0]; | 
|  129   } |  150   } | 
|  130   ASSERT_EQ("google.com", goog_channel_id->server_identifier()); |  151   ASSERT_EQ("google.com", goog_channel_id->server_identifier()); | 
|  131   ASSERT_STREQ("a", goog_channel_id->private_key().c_str()); |  152   EXPECT_TRUE(KeysEqual(google_key_.get(), goog_channel_id->key())); | 
|  132   ASSERT_STREQ("b", goog_channel_id->cert().c_str()); |  | 
|  133   ASSERT_EQ(1, goog_channel_id->creation_time().ToInternalValue()); |  153   ASSERT_EQ(1, goog_channel_id->creation_time().ToInternalValue()); | 
|  134   ASSERT_EQ(2, goog_channel_id->expiration_time().ToInternalValue()); |  | 
|  135   ASSERT_EQ("foo.com", foo_channel_id->server_identifier()); |  154   ASSERT_EQ("foo.com", foo_channel_id->server_identifier()); | 
|  136   ASSERT_STREQ("c", foo_channel_id->private_key().c_str()); |  155   EXPECT_TRUE(KeysEqual(foo_key.get(), foo_channel_id->key())); | 
|  137   ASSERT_STREQ("d", foo_channel_id->cert().c_str()); |  | 
|  138   ASSERT_EQ(3, foo_channel_id->creation_time().ToInternalValue()); |  156   ASSERT_EQ(3, foo_channel_id->creation_time().ToInternalValue()); | 
|  139   ASSERT_EQ(4, foo_channel_id->expiration_time().ToInternalValue()); |  | 
|  140  |  157  | 
|  141   // Now delete the cert and check persistence again. |  158   // Now delete the keypair and check persistence again. | 
|  142   store_->DeleteChannelID(*channel_ids[0]); |  159   store_->DeleteChannelID(*channel_ids[0]); | 
|  143   store_->DeleteChannelID(*channel_ids[1]); |  160   store_->DeleteChannelID(*channel_ids[1]); | 
|  144   store_ = NULL; |  161   store_ = NULL; | 
|  145   // Make sure we wait until the destructor has run. |  162   // Make sure we wait until the destructor has run. | 
|  146   base::RunLoop().RunUntilIdle(); |  163   base::RunLoop().RunUntilIdle(); | 
|  147   channel_ids.clear(); |  164   channel_ids.clear(); | 
|  148   store_ = |  165   store_ = | 
|  149       new SQLiteChannelIDStore(temp_dir_.path().Append(kTestChannelIDFilename), |  166       new SQLiteChannelIDStore(temp_dir_.path().Append(kTestChannelIDFilename), | 
|  150                                base::MessageLoopProxy::current()); |  167                                base::MessageLoopProxy::current()); | 
|  151  |  168  | 
|  152   // Reload and check if the cert has been removed. |  169   // Reload and check if the keypair has been removed. | 
|  153   Load(&channel_ids); |  170   Load(&channel_ids); | 
|  154   ASSERT_EQ(0U, channel_ids.size()); |  171   ASSERT_EQ(0U, channel_ids.size()); | 
|  155   // Close the store. |  172   // Close the store. | 
|  156   store_ = NULL; |  173   store_ = NULL; | 
|  157   // Make sure we wait until the destructor has run. |  174   // Make sure we wait until the destructor has run. | 
|  158   base::RunLoop().RunUntilIdle(); |  175   base::RunLoop().RunUntilIdle(); | 
|  159 } |  176 } | 
|  160  |  177  | 
|  161 // Test if data is stored as expected in the SQLite database. |  178 // Test if data is stored as expected in the SQLite database. | 
|  162 TEST_F(SQLiteChannelIDStoreTest, TestDeleteAll) { |  179 TEST_F(SQLiteChannelIDStoreTest, TestDeleteAll) { | 
|  163   store_->AddChannelID( |  180   store_->AddChannelID(DefaultChannelIDStore::ChannelID( | 
|  164       DefaultChannelIDStore::ChannelID("foo.com", |  181       "foo.com", base::Time::FromInternalValue(3), | 
|  165                                        base::Time::FromInternalValue(3), |  182       make_scoped_ptr(crypto::ECPrivateKey::Create()))); | 
|  166                                        base::Time::FromInternalValue(4), |  | 
|  167                                        "c", |  | 
|  168                                        "d")); |  | 
|  169  |  183  | 
|  170   ScopedVector<DefaultChannelIDStore::ChannelID> channel_ids; |  184   ScopedVector<DefaultChannelIDStore::ChannelID> channel_ids; | 
|  171   // Replace the store effectively destroying the current one and forcing it |  185   // Replace the store effectively destroying the current one and forcing it | 
|  172   // to write its data to disk. Then we can see if after loading it again it |  186   // to write its data to disk. Then we can see if after loading it again it | 
|  173   // is still there. |  187   // is still there. | 
|  174   store_ = NULL; |  188   store_ = NULL; | 
|  175   // Make sure we wait until the destructor has run. |  189   // Make sure we wait until the destructor has run. | 
|  176   base::RunLoop().RunUntilIdle(); |  190   base::RunLoop().RunUntilIdle(); | 
|  177   store_ = |  191   store_ = | 
|  178       new SQLiteChannelIDStore(temp_dir_.path().Append(kTestChannelIDFilename), |  192       new SQLiteChannelIDStore(temp_dir_.path().Append(kTestChannelIDFilename), | 
| (...skipping 28 matching lines...) Expand all  Loading... | 
|  207 } |  221 } | 
|  208  |  222  | 
|  209 TEST_F(SQLiteChannelIDStoreTest, TestUpgradeV1) { |  223 TEST_F(SQLiteChannelIDStoreTest, TestUpgradeV1) { | 
|  210   // Reset the store.  We'll be using a different database for this test. |  224   // Reset the store.  We'll be using a different database for this test. | 
|  211   store_ = NULL; |  225   store_ = NULL; | 
|  212  |  226  | 
|  213   base::FilePath v1_db_path(temp_dir_.path().AppendASCII("v1db")); |  227   base::FilePath v1_db_path(temp_dir_.path().AppendASCII("v1db")); | 
|  214  |  228  | 
|  215   std::string key_data; |  229   std::string key_data; | 
|  216   std::string cert_data; |  230   std::string cert_data; | 
|  217   ReadTestKeyAndCert(&key_data, &cert_data); |  231   scoped_ptr<crypto::ECPrivateKey> key; | 
 |  232   ASSERT_NO_FATAL_FAILURE(ReadTestKeyAndCert(&key_data, &cert_data, &key)); | 
|  218  |  233  | 
|  219   // Create a version 1 database. |  234   // Create a version 1 database. | 
|  220   { |  235   { | 
|  221     sql::Connection db; |  236     sql::Connection db; | 
|  222     ASSERT_TRUE(db.Open(v1_db_path)); |  237     ASSERT_TRUE(db.Open(v1_db_path)); | 
|  223     ASSERT_TRUE(db.Execute( |  238     ASSERT_TRUE(db.Execute( | 
|  224         "CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY," |  239         "CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY," | 
|  225         "value LONGVARCHAR);" |  240         "value LONGVARCHAR);" | 
|  226         "INSERT INTO \"meta\" VALUES('version','1');" |  241         "INSERT INTO \"meta\" VALUES('version','1');" | 
|  227         "INSERT INTO \"meta\" VALUES('last_compatible_version','1');" |  242         "INSERT INTO \"meta\" VALUES('last_compatible_version','1');" | 
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  260     store_ = NULL; |  275     store_ = NULL; | 
|  261     base::RunLoop().RunUntilIdle(); |  276     base::RunLoop().RunUntilIdle(); | 
|  262  |  277  | 
|  263     // Verify the database version is updated. |  278     // Verify the database version is updated. | 
|  264     { |  279     { | 
|  265       sql::Connection db; |  280       sql::Connection db; | 
|  266       ASSERT_TRUE(db.Open(v1_db_path)); |  281       ASSERT_TRUE(db.Open(v1_db_path)); | 
|  267       sql::Statement smt(db.GetUniqueStatement( |  282       sql::Statement smt(db.GetUniqueStatement( | 
|  268           "SELECT value FROM meta WHERE key = \"version\"")); |  283           "SELECT value FROM meta WHERE key = \"version\"")); | 
|  269       ASSERT_TRUE(smt.Step()); |  284       ASSERT_TRUE(smt.Step()); | 
|  270       EXPECT_EQ(4, smt.ColumnInt(0)); |  285       EXPECT_EQ(5, smt.ColumnInt(0)); | 
|  271       EXPECT_FALSE(smt.Step()); |  286       EXPECT_FALSE(smt.Step()); | 
|  272     } |  287     } | 
|  273   } |  288   } | 
|  274 } |  289 } | 
|  275  |  290  | 
|  276 TEST_F(SQLiteChannelIDStoreTest, TestUpgradeV2) { |  291 TEST_F(SQLiteChannelIDStoreTest, TestUpgradeV2) { | 
|  277   // Reset the store.  We'll be using a different database for this test. |  292   // Reset the store.  We'll be using a different database for this test. | 
|  278   store_ = NULL; |  293   store_ = NULL; | 
|  279  |  294  | 
|  280   base::FilePath v2_db_path(temp_dir_.path().AppendASCII("v2db")); |  295   base::FilePath v2_db_path(temp_dir_.path().AppendASCII("v2db")); | 
|  281  |  296  | 
|  282   std::string key_data; |  297   std::string key_data; | 
|  283   std::string cert_data; |  298   std::string cert_data; | 
|  284   ReadTestKeyAndCert(&key_data, &cert_data); |  299   scoped_ptr<crypto::ECPrivateKey> key; | 
 |  300   ASSERT_NO_FATAL_FAILURE(ReadTestKeyAndCert(&key_data, &cert_data, &key)); | 
|  285  |  301  | 
|  286   // Create a version 2 database. |  302   // Create a version 2 database. | 
|  287   { |  303   { | 
|  288     sql::Connection db; |  304     sql::Connection db; | 
|  289     ASSERT_TRUE(db.Open(v2_db_path)); |  305     ASSERT_TRUE(db.Open(v2_db_path)); | 
|  290     ASSERT_TRUE(db.Execute( |  306     ASSERT_TRUE(db.Execute( | 
|  291         "CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY," |  307         "CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY," | 
|  292         "value LONGVARCHAR);" |  308         "value LONGVARCHAR);" | 
|  293         "INSERT INTO \"meta\" VALUES('version','2');" |  309         "INSERT INTO \"meta\" VALUES('version','2');" | 
|  294         "INSERT INTO \"meta\" VALUES('last_compatible_version','1');" |  310         "INSERT INTO \"meta\" VALUES('last_compatible_version','1');" | 
|  295         "CREATE TABLE origin_bound_certs (" |  311         "CREATE TABLE origin_bound_certs (" | 
|  296         "origin TEXT NOT NULL UNIQUE PRIMARY KEY," |  312         "origin TEXT NOT NULL UNIQUE PRIMARY KEY," | 
|  297         "private_key BLOB NOT NULL," |  313         "private_key BLOB NOT NULL," | 
|  298         "cert BLOB NOT NULL," |  314         "cert BLOB NOT NULL," | 
|  299         "cert_type INTEGER);")); |  315         "cert_type INTEGER);")); | 
|  300  |  316  | 
|  301     sql::Statement add_smt(db.GetUniqueStatement( |  317     sql::Statement add_smt(db.GetUniqueStatement( | 
|  302         "INSERT INTO origin_bound_certs (origin, private_key, cert, cert_type) " |  318         "INSERT INTO origin_bound_certs (origin, private_key, cert, cert_type) " | 
|  303         "VALUES (?,?,?,?)")); |  319         "VALUES (?,?,?,?)")); | 
|  304     add_smt.BindString(0, "google.com"); |  320     add_smt.BindString(0, "google.com"); | 
|  305     add_smt.BindBlob(1, key_data.data(), key_data.size()); |  321     add_smt.BindBlob(1, key_data.data(), key_data.size()); | 
|  306     add_smt.BindBlob(2, cert_data.data(), cert_data.size()); |  322     add_smt.BindBlob(2, cert_data.data(), cert_data.size()); | 
|  307     add_smt.BindInt64(3, 64); |  323     add_smt.BindInt64(3, 64); | 
|  308     ASSERT_TRUE(add_smt.Run()); |  324     ASSERT_TRUE(add_smt.Run()); | 
|  309  |  325  | 
 |  326     // Malformed certs will be ignored and not migrated. | 
|  310     ASSERT_TRUE(db.Execute( |  327     ASSERT_TRUE(db.Execute( | 
|  311         "INSERT INTO \"origin_bound_certs\" VALUES(" |  328         "INSERT INTO \"origin_bound_certs\" VALUES(" | 
|  312         "'foo.com',X'AA',X'BB',64);")); |  329         "'foo.com',X'AA',X'BB',64);")); | 
|  313   } |  330   } | 
|  314  |  331  | 
|  315   // Load and test the DB contents twice.  First time ensures that we can use |  332   // Load and test the DB contents twice.  First time ensures that we can use | 
|  316   // the updated values immediately.  Second time ensures that the updated |  333   // the updated values immediately.  Second time ensures that the updated | 
|  317   // values are saved and read correctly on next load. |  334   // values are saved and read correctly on next load. | 
|  318   for (int i = 0; i < 2; ++i) { |  335   for (int i = 0; i < 2; ++i) { | 
|  319     SCOPED_TRACE(i); |  336     SCOPED_TRACE(i); | 
|  320  |  337  | 
|  321     ScopedVector<DefaultChannelIDStore::ChannelID> channel_ids; |  338     ScopedVector<DefaultChannelIDStore::ChannelID> channel_ids; | 
|  322     store_ = |  339     store_ = | 
|  323         new SQLiteChannelIDStore(v2_db_path, base::MessageLoopProxy::current()); |  340         new SQLiteChannelIDStore(v2_db_path, base::MessageLoopProxy::current()); | 
|  324  |  341  | 
|  325     // Load the database and ensure the certs can be read. |  342     // Load the database and ensure the certs can be read. | 
|  326     Load(&channel_ids); |  343     Load(&channel_ids); | 
|  327     ASSERT_EQ(2U, channel_ids.size()); |  344     ASSERT_EQ(1U, channel_ids.size()); | 
|  328  |  345  | 
|  329     ASSERT_EQ("google.com", channel_ids[0]->server_identifier()); |  346     ASSERT_EQ("google.com", channel_ids[0]->server_identifier()); | 
|  330     ASSERT_EQ(GetTestCertExpirationTime(), channel_ids[0]->expiration_time()); |  347     ASSERT_EQ(GetTestCertCreationTime(), channel_ids[0]->creation_time()); | 
|  331     ASSERT_EQ(key_data, channel_ids[0]->private_key()); |  348     EXPECT_TRUE(KeysEqual(key.get(), channel_ids[0]->key())); | 
|  332     ASSERT_EQ(cert_data, channel_ids[0]->cert()); |  | 
|  333  |  | 
|  334     ASSERT_EQ("foo.com", channel_ids[1]->server_identifier()); |  | 
|  335     // Undecodable cert, expiration time will be uninitialized. |  | 
|  336     ASSERT_EQ(base::Time(), channel_ids[1]->expiration_time()); |  | 
|  337     ASSERT_STREQ("\xaa", channel_ids[1]->private_key().c_str()); |  | 
|  338     ASSERT_STREQ("\xbb", channel_ids[1]->cert().c_str()); |  | 
|  339  |  349  | 
|  340     store_ = NULL; |  350     store_ = NULL; | 
|  341     // Make sure we wait until the destructor has run. |  351     // Make sure we wait until the destructor has run. | 
|  342     base::RunLoop().RunUntilIdle(); |  352     base::RunLoop().RunUntilIdle(); | 
|  343  |  353  | 
|  344     // Verify the database version is updated. |  354     // Verify the database version is updated. | 
|  345     { |  355     { | 
|  346       sql::Connection db; |  356       sql::Connection db; | 
|  347       ASSERT_TRUE(db.Open(v2_db_path)); |  357       ASSERT_TRUE(db.Open(v2_db_path)); | 
|  348       sql::Statement smt(db.GetUniqueStatement( |  358       sql::Statement smt(db.GetUniqueStatement( | 
|  349           "SELECT value FROM meta WHERE key = \"version\"")); |  359           "SELECT value FROM meta WHERE key = \"version\"")); | 
|  350       ASSERT_TRUE(smt.Step()); |  360       ASSERT_TRUE(smt.Step()); | 
|  351       EXPECT_EQ(4, smt.ColumnInt(0)); |  361       EXPECT_EQ(5, smt.ColumnInt(0)); | 
|  352       EXPECT_FALSE(smt.Step()); |  362       EXPECT_FALSE(smt.Step()); | 
|  353     } |  363     } | 
|  354   } |  364   } | 
|  355 } |  365 } | 
|  356  |  366  | 
|  357 TEST_F(SQLiteChannelIDStoreTest, TestUpgradeV3) { |  367 TEST_F(SQLiteChannelIDStoreTest, TestUpgradeV3) { | 
|  358   // Reset the store.  We'll be using a different database for this test. |  368   // Reset the store.  We'll be using a different database for this test. | 
|  359   store_ = NULL; |  369   store_ = NULL; | 
|  360  |  370  | 
|  361   base::FilePath v3_db_path(temp_dir_.path().AppendASCII("v3db")); |  371   base::FilePath v3_db_path(temp_dir_.path().AppendASCII("v3db")); | 
|  362  |  372  | 
|  363   std::string key_data; |  373   std::string key_data; | 
|  364   std::string cert_data; |  374   std::string cert_data; | 
|  365   ReadTestKeyAndCert(&key_data, &cert_data); |  375   scoped_ptr<crypto::ECPrivateKey> key; | 
 |  376   ASSERT_NO_FATAL_FAILURE(ReadTestKeyAndCert(&key_data, &cert_data, &key)); | 
|  366  |  377  | 
|  367   // Create a version 3 database. |  378   // Create a version 3 database. | 
|  368   { |  379   { | 
|  369     sql::Connection db; |  380     sql::Connection db; | 
|  370     ASSERT_TRUE(db.Open(v3_db_path)); |  381     ASSERT_TRUE(db.Open(v3_db_path)); | 
|  371     ASSERT_TRUE(db.Execute( |  382     ASSERT_TRUE(db.Execute( | 
|  372         "CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY," |  383         "CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY," | 
|  373         "value LONGVARCHAR);" |  384         "value LONGVARCHAR);" | 
|  374         "INSERT INTO \"meta\" VALUES('version','3');" |  385         "INSERT INTO \"meta\" VALUES('version','3');" | 
|  375         "INSERT INTO \"meta\" VALUES('last_compatible_version','1');" |  386         "INSERT INTO \"meta\" VALUES('last_compatible_version','1');" | 
|  376         "CREATE TABLE origin_bound_certs (" |  387         "CREATE TABLE origin_bound_certs (" | 
|  377         "origin TEXT NOT NULL UNIQUE PRIMARY KEY," |  388         "origin TEXT NOT NULL UNIQUE PRIMARY KEY," | 
|  378         "private_key BLOB NOT NULL," |  389         "private_key BLOB NOT NULL," | 
|  379         "cert BLOB NOT NULL," |  390         "cert BLOB NOT NULL," | 
|  380         "cert_type INTEGER," |  391         "cert_type INTEGER," | 
|  381         "expiration_time INTEGER);")); |  392         "expiration_time INTEGER);")); | 
|  382  |  393  | 
|  383     sql::Statement add_smt(db.GetUniqueStatement( |  394     sql::Statement add_smt(db.GetUniqueStatement( | 
|  384         "INSERT INTO origin_bound_certs (origin, private_key, cert, cert_type, " |  395         "INSERT INTO origin_bound_certs (origin, private_key, cert, cert_type, " | 
|  385         "expiration_time) VALUES (?,?,?,?,?)")); |  396         "expiration_time) VALUES (?,?,?,?,?)")); | 
|  386     add_smt.BindString(0, "google.com"); |  397     add_smt.BindString(0, "google.com"); | 
|  387     add_smt.BindBlob(1, key_data.data(), key_data.size()); |  398     add_smt.BindBlob(1, key_data.data(), key_data.size()); | 
|  388     add_smt.BindBlob(2, cert_data.data(), cert_data.size()); |  399     add_smt.BindBlob(2, cert_data.data(), cert_data.size()); | 
|  389     add_smt.BindInt64(3, 64); |  400     add_smt.BindInt64(3, 64); | 
|  390     add_smt.BindInt64(4, 1000); |  401     add_smt.BindInt64(4, 1000); | 
|  391     ASSERT_TRUE(add_smt.Run()); |  402     ASSERT_TRUE(add_smt.Run()); | 
|  392  |  403  | 
 |  404     // Malformed certs will be ignored and not migrated. | 
|  393     ASSERT_TRUE(db.Execute( |  405     ASSERT_TRUE(db.Execute( | 
|  394         "INSERT INTO \"origin_bound_certs\" VALUES(" |  406         "INSERT INTO \"origin_bound_certs\" VALUES(" | 
|  395         "'foo.com',X'AA',X'BB',64,2000);")); |  407         "'foo.com',X'AA',X'BB',64,2000);")); | 
|  396   } |  408   } | 
|  397  |  409  | 
|  398   // Load and test the DB contents twice.  First time ensures that we can use |  410   // Load and test the DB contents twice.  First time ensures that we can use | 
|  399   // the updated values immediately.  Second time ensures that the updated |  411   // the updated values immediately.  Second time ensures that the updated | 
|  400   // values are saved and read correctly on next load. |  412   // values are saved and read correctly on next load. | 
|  401   for (int i = 0; i < 2; ++i) { |  413   for (int i = 0; i < 2; ++i) { | 
|  402     SCOPED_TRACE(i); |  414     SCOPED_TRACE(i); | 
|  403  |  415  | 
|  404     ScopedVector<DefaultChannelIDStore::ChannelID> channel_ids; |  416     ScopedVector<DefaultChannelIDStore::ChannelID> channel_ids; | 
|  405     store_ = |  417     store_ = | 
|  406         new SQLiteChannelIDStore(v3_db_path, base::MessageLoopProxy::current()); |  418         new SQLiteChannelIDStore(v3_db_path, base::MessageLoopProxy::current()); | 
|  407  |  419  | 
|  408     // Load the database and ensure the certs can be read. |  420     // Load the database and ensure the certs can be read. | 
|  409     Load(&channel_ids); |  421     Load(&channel_ids); | 
|  410     ASSERT_EQ(2U, channel_ids.size()); |  422     ASSERT_EQ(1U, channel_ids.size()); | 
|  411  |  423  | 
|  412     ASSERT_EQ("google.com", channel_ids[0]->server_identifier()); |  424     ASSERT_EQ("google.com", channel_ids[0]->server_identifier()); | 
|  413     ASSERT_EQ(1000, channel_ids[0]->expiration_time().ToInternalValue()); |  | 
|  414     ASSERT_EQ(GetTestCertCreationTime(), channel_ids[0]->creation_time()); |  425     ASSERT_EQ(GetTestCertCreationTime(), channel_ids[0]->creation_time()); | 
|  415     ASSERT_EQ(key_data, channel_ids[0]->private_key()); |  426     EXPECT_TRUE(KeysEqual(key.get(), channel_ids[0]->key())); | 
|  416     ASSERT_EQ(cert_data, channel_ids[0]->cert()); |  | 
|  417  |  | 
|  418     ASSERT_EQ("foo.com", channel_ids[1]->server_identifier()); |  | 
|  419     ASSERT_EQ(2000, channel_ids[1]->expiration_time().ToInternalValue()); |  | 
|  420     // Undecodable cert, creation time will be uninitialized. |  | 
|  421     ASSERT_EQ(base::Time(), channel_ids[1]->creation_time()); |  | 
|  422     ASSERT_STREQ("\xaa", channel_ids[1]->private_key().c_str()); |  | 
|  423     ASSERT_STREQ("\xbb", channel_ids[1]->cert().c_str()); |  | 
|  424  |  427  | 
|  425     store_ = NULL; |  428     store_ = NULL; | 
|  426     // Make sure we wait until the destructor has run. |  429     // Make sure we wait until the destructor has run. | 
|  427     base::RunLoop().RunUntilIdle(); |  430     base::RunLoop().RunUntilIdle(); | 
|  428  |  431  | 
|  429     // Verify the database version is updated. |  432     // Verify the database version is updated. | 
|  430     { |  433     { | 
|  431       sql::Connection db; |  434       sql::Connection db; | 
|  432       ASSERT_TRUE(db.Open(v3_db_path)); |  435       ASSERT_TRUE(db.Open(v3_db_path)); | 
|  433       sql::Statement smt(db.GetUniqueStatement( |  436       sql::Statement smt(db.GetUniqueStatement( | 
|  434           "SELECT value FROM meta WHERE key = \"version\"")); |  437           "SELECT value FROM meta WHERE key = \"version\"")); | 
|  435       ASSERT_TRUE(smt.Step()); |  438       ASSERT_TRUE(smt.Step()); | 
|  436       EXPECT_EQ(4, smt.ColumnInt(0)); |  439       EXPECT_EQ(5, smt.ColumnInt(0)); | 
|  437       EXPECT_FALSE(smt.Step()); |  440       EXPECT_FALSE(smt.Step()); | 
|  438     } |  441     } | 
|  439   } |  442   } | 
|  440 } |  443 } | 
|  441  |  444  | 
|  442 TEST_F(SQLiteChannelIDStoreTest, TestRSADiscarded) { |  445 TEST_F(SQLiteChannelIDStoreTest, TestUpgradeV4) { | 
|  443   // Reset the store.  We'll be using a different database for this test. |  446   // Reset the store.  We'll be using a different database for this test. | 
|  444   store_ = NULL; |  447   store_ = NULL; | 
|  445  |  448  | 
|  446   base::FilePath v4_db_path(temp_dir_.path().AppendASCII("v4dbrsa")); |  449   base::FilePath v4_db_path(temp_dir_.path().AppendASCII("v4db")); | 
|  447  |  450  | 
|  448   std::string key_data; |  451   std::string key_data; | 
|  449   std::string cert_data; |  452   std::string cert_data; | 
|  450   ReadTestKeyAndCert(&key_data, &cert_data); |  453   scoped_ptr<crypto::ECPrivateKey> key; | 
 |  454   ASSERT_NO_FATAL_FAILURE(ReadTestKeyAndCert(&key_data, &cert_data, &key)); | 
|  451  |  455  | 
|  452   // Create a version 4 database with a mix of RSA and ECDSA certs. |  456   // Create a version 4 database. | 
|  453   { |  457   { | 
|  454     sql::Connection db; |  458     sql::Connection db; | 
|  455     ASSERT_TRUE(db.Open(v4_db_path)); |  459     ASSERT_TRUE(db.Open(v4_db_path)); | 
|  456     ASSERT_TRUE(db.Execute( |  460     ASSERT_TRUE(db.Execute( | 
|  457         "CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY," |  461         "CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY," | 
|  458         "value LONGVARCHAR);" |  462         "value LONGVARCHAR);" | 
|  459         "INSERT INTO \"meta\" VALUES('version','4');" |  463         "INSERT INTO \"meta\" VALUES('version','4');" | 
|  460         "INSERT INTO \"meta\" VALUES('last_compatible_version','1');" |  464         "INSERT INTO \"meta\" VALUES('last_compatible_version','1');" | 
|  461         "CREATE TABLE origin_bound_certs (" |  465         "CREATE TABLE origin_bound_certs (" | 
|  462         "origin TEXT NOT NULL UNIQUE PRIMARY KEY," |  466         "origin TEXT NOT NULL UNIQUE PRIMARY KEY," | 
|  463         "private_key BLOB NOT NULL," |  467         "private_key BLOB NOT NULL," | 
|  464         "cert BLOB NOT NULL," |  468         "cert BLOB NOT NULL," | 
|  465         "cert_type INTEGER," |  469         "cert_type INTEGER," | 
|  466         "expiration_time INTEGER," |  470         "expiration_time INTEGER," | 
|  467         "creation_time INTEGER);")); |  471         "creation_time INTEGER);")); | 
|  468  |  472  | 
|  469     sql::Statement add_smt(db.GetUniqueStatement( |  473     sql::Statement add_smt(db.GetUniqueStatement( | 
|  470         "INSERT INTO origin_bound_certs " |  474         "INSERT INTO origin_bound_certs (origin, private_key, cert, cert_type, " | 
|  471         "(origin, private_key, cert, cert_type, expiration_time, creation_time)" |  475         "expiration_time, creation_time) VALUES (?,?,?,?,?,?)")); | 
|  472         " VALUES (?,?,?,?,?,?)")); |  | 
|  473     add_smt.BindString(0, "google.com"); |  476     add_smt.BindString(0, "google.com"); | 
|  474     add_smt.BindBlob(1, key_data.data(), key_data.size()); |  477     add_smt.BindBlob(1, key_data.data(), key_data.size()); | 
|  475     add_smt.BindBlob(2, cert_data.data(), cert_data.size()); |  478     add_smt.BindBlob(2, cert_data.data(), cert_data.size()); | 
|  476     add_smt.BindInt64(3, 64); |  479     add_smt.BindInt64(3, 64); | 
|  477     add_smt.BindInt64(4, GetTestCertExpirationTime().ToInternalValue()); |  480     add_smt.BindInt64(4, 1000); | 
|  478     add_smt.BindInt64(5, base::Time::Now().ToInternalValue()); |  481     add_smt.BindInt64(5, GetTestCertCreationTime().ToInternalValue()); | 
|  479     ASSERT_TRUE(add_smt.Run()); |  482     ASSERT_TRUE(add_smt.Run()); | 
|  480  |  483  | 
 |  484     // Add an RSA cert to the db. This cert should be ignored in the migration. | 
|  481     add_smt.Clear(); |  485     add_smt.Clear(); | 
|  482     add_smt.Assign(db.GetUniqueStatement( |  486     add_smt.Assign(db.GetUniqueStatement( | 
|  483         "INSERT INTO origin_bound_certs " |  487         "INSERT INTO origin_bound_certs " | 
|  484         "(origin, private_key, cert, cert_type, expiration_time, creation_time)" |  488         "(origin, private_key, cert, cert_type, expiration_time, creation_time)" | 
|  485         " VALUES (?,?,?,?,?,?)")); |  489         " VALUES (?,?,?,?,?,?)")); | 
|  486     add_smt.BindString(0, "foo.com"); |  490     add_smt.BindString(0, "foo.com"); | 
|  487     add_smt.BindBlob(1, key_data.data(), key_data.size()); |  491     add_smt.BindBlob(1, key_data.data(), key_data.size()); | 
|  488     add_smt.BindBlob(2, cert_data.data(), cert_data.size()); |  492     add_smt.BindBlob(2, cert_data.data(), cert_data.size()); | 
|  489     add_smt.BindInt64(3, 1); |  493     add_smt.BindInt64(3, 1); | 
|  490     add_smt.BindInt64(4, GetTestCertExpirationTime().ToInternalValue()); |  494     add_smt.BindInt64(4, GetTestCertExpirationTime().ToInternalValue()); | 
|  491     add_smt.BindInt64(5, base::Time::Now().ToInternalValue()); |  495     add_smt.BindInt64(5, base::Time::Now().ToInternalValue()); | 
|  492     ASSERT_TRUE(add_smt.Run()); |  496     ASSERT_TRUE(add_smt.Run()); | 
 |  497  | 
 |  498     // Malformed certs will be ignored and not migrated. | 
 |  499     ASSERT_TRUE(db.Execute( | 
 |  500         "INSERT INTO \"origin_bound_certs\" VALUES(" | 
 |  501         "'bar.com',X'AA',X'BB',64,2000,3000);")); | 
|  493   } |  502   } | 
|  494  |  503  | 
|  495   ScopedVector<DefaultChannelIDStore::ChannelID> channel_ids; |  504   // Load and test the DB contents twice.  First time ensures that we can use | 
|  496   store_ = |  505   // the updated values immediately.  Second time ensures that the updated | 
|  497       new SQLiteChannelIDStore(v4_db_path, base::MessageLoopProxy::current()); |  506   // values are saved and read correctly on next load. | 
 |  507   for (int i = 0; i < 2; ++i) { | 
 |  508     SCOPED_TRACE(i); | 
|  498  |  509  | 
|  499   // Load the database and ensure the certs can be read. |  510     ScopedVector<DefaultChannelIDStore::ChannelID> channel_ids; | 
|  500   Load(&channel_ids); |  511     store_ = | 
|  501   // Only the ECDSA cert (for google.com) is read, the RSA one is discarded. |  512         new SQLiteChannelIDStore(v4_db_path, base::MessageLoopProxy::current()); | 
|  502   ASSERT_EQ(1U, channel_ids.size()); |  | 
|  503  |  513  | 
|  504   ASSERT_EQ("google.com", channel_ids[0]->server_identifier()); |  514     // Load the database and ensure the certs can be read. | 
|  505   ASSERT_EQ(GetTestCertExpirationTime(), channel_ids[0]->expiration_time()); |  515     Load(&channel_ids); | 
|  506   ASSERT_EQ(key_data, channel_ids[0]->private_key()); |  516     ASSERT_EQ(1U, channel_ids.size()); | 
|  507   ASSERT_EQ(cert_data, channel_ids[0]->cert()); |  | 
|  508  |  517  | 
|  509   store_ = NULL; |  518     ASSERT_EQ("google.com", channel_ids[0]->server_identifier()); | 
|  510   // Make sure we wait until the destructor has run. |  519     ASSERT_EQ(GetTestCertCreationTime(), channel_ids[0]->creation_time()); | 
|  511   base::RunLoop().RunUntilIdle(); |  520     EXPECT_TRUE(KeysEqual(key.get(), channel_ids[0]->key())); | 
 |  521  | 
 |  522     store_ = NULL; | 
 |  523     // Make sure we wait until the destructor has run. | 
 |  524     base::RunLoop().RunUntilIdle(); | 
 |  525  | 
 |  526     // Verify the database version is updated. | 
 |  527     { | 
 |  528       sql::Connection db; | 
 |  529       ASSERT_TRUE(db.Open(v4_db_path)); | 
 |  530       sql::Statement smt(db.GetUniqueStatement( | 
 |  531           "SELECT value FROM meta WHERE key = \"version\"")); | 
 |  532       ASSERT_TRUE(smt.Step()); | 
 |  533       EXPECT_EQ(5, smt.ColumnInt(0)); | 
 |  534       EXPECT_FALSE(smt.Step()); | 
 |  535     } | 
 |  536   } | 
|  512 } |  537 } | 
|  513  |  538  | 
|  514 }  // namespace net |  539 }  // namespace net | 
| OLD | NEW |