proxygen
SSLSessionPersistentCacheTest.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2017-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include <folly/Format.h>
17 #include <folly/DynamicConverter.h>
18 #include <folly/Memory.h>
19 
20 #include <gmock/gmock.h>
21 
22 #include <gtest/gtest.h>
23 
24 #include <vector>
25 
29 
30 using namespace testing;
31 using namespace std::chrono;
32 using namespace wangle;
33 
34 namespace wangle {
35 
37  public:
38  void advance(std::chrono::milliseconds ms) {
39  t_ += ms;
40  }
41 
42  std::chrono::time_point<std::chrono::system_clock> now() const override {
43  return t_;
44  }
45 
46  private:
47  std::chrono::time_point<std::chrono::system_clock> t_;
48 };
49 
51  public:
53  filename_(getPersistentCacheFilename()) {}
54 
55  void SetUp() override {
56  for (auto& it : getSessions()) {
57  sessions_.emplace_back(it.first, it.second);
58  }
59  sessionWithTicket_ = getSessionWithTicket();
60  sessions_.emplace_back(sessionWithTicket_.first, sessionWithTicket_.second);
61 
62  // Create the cache fresh for each test
63  mockTimeUtil_ = new MockTimeUtil();
64  cache_.reset(new SSLSessionPersistentCache(filename_,
65  50, std::chrono::seconds(150)));
66  cache_->setTimeUtil(
67  std::unique_ptr<SSLSessionPersistentCache::TimeUtil>(mockTimeUtil_));
68  }
69 
70  void TearDown() override {
71  for (const auto& it : sessions_) {
72  SSL_SESSION_free(it.first);
73  }
74  sessions_.clear();
75  cache_.reset();
76  EXPECT_TRUE(unlink(filename_.c_str()) != -1);
77  }
78 
79  protected:
80  void verifyEntryInCache(const std::string& hostname,
81  std::pair<SSL_SESSION*, size_t> session,
82  bool inCache = true) {
83  auto s = cache_->getSSLSession(hostname);
84  if (inCache) {
85  ASSERT_TRUE(s != nullptr);
88  session,
89  std::make_pair(s.get(), session.second)));
90  } else {
91  ASSERT_FALSE(s);
92  }
93  }
94 
95  protected:
98  std::unique_ptr<SSLSessionPersistentCache> cache_;
99  std::vector<std::pair<SSL_SESSION*, size_t>> sessions_;
100  std::pair<SSL_SESSION*, size_t> sessionWithTicket_;
101 };
102 
104  for (size_t i = 0; i < sessions_.size(); ++i) {
105  auto hostname = folly::sformat("host{}", i);
106 
107  // The session data does not exist before set.
108  ASSERT_EQ(i, cache_->size());
109  ASSERT_FALSE(cache_->getSSLSession(hostname));
110 
111  cache_->setSSLSession(hostname, createPersistentTestSession(sessions_[i]));
112 
113  // The session data should exist before set.
114  ASSERT_EQ(i + 1, cache_->size());
115  verifyEntryInCache(hostname, sessions_[i]);
116  }
117 
118  // The previously inserted sessions shouldn't have changed. Then remove them
119  // one by one and verify they are not in cache after the removal.
120  for (size_t i = 0; i < sessions_.size(); ++i) {
121  auto hostname = folly::sformat("host{}", i);
122  verifyEntryInCache(hostname, sessions_[i]);
123  cache_->removeSSLSession(hostname);
124  verifyEntryInCache(hostname, sessions_[i], false);
125  }
126 }
127 
129  std::string badHost = "bad";
130 
131  // Insert bad session to an empty cache.
132  cache_->setSSLSession(badHost, SSLSessionPtr(nullptr));
133  ASSERT_FALSE(cache_->getSSLSession(badHost));
134  ASSERT_EQ(0, cache_->size());
135 
136  cache_->setSSLSession("host0", createPersistentTestSession(sessions_[0]));
137  cache_->setSSLSession("host1", createPersistentTestSession(sessions_[1]));
138 
139  // Insert bad session to non-empty cache
140  cache_->setSSLSession(badHost, SSLSessionPtr(nullptr));
141  ASSERT_FALSE(cache_->getSSLSession(badHost));
142  ASSERT_EQ(2, cache_->size());
143 
144  verifyEntryInCache("host0", sessions_[0]);
145  verifyEntryInCache("host1", sessions_[1]);
146 }
147 
149  cache_->setSSLSession("host0", createPersistentTestSession(sessions_[0]));
150  cache_->setSSLSession("host1", createPersistentTestSession(sessions_[1]));
151 
152  {
153  // Overwrite host1 with a nullptr, the cache shouldn't have changed.
154  cache_->setSSLSession("host1", SSLSessionPtr(nullptr));
155  verifyEntryInCache("host0", sessions_[0]);
156  verifyEntryInCache("host1", sessions_[1]);
157  }
158 
159  {
160  // Valid overwrite.
161  cache_->setSSLSession("host1", createPersistentTestSession(sessions_[3]));
162  verifyEntryInCache("host0", sessions_[0]);
163  verifyEntryInCache("host1", sessions_[3]);
164  }
165 
166 }
167 
168 TEST_F(SSLSessionPersistentCacheTest, SessionTicketTimeout) {
169  std::string myhost("host3");
170  cache_->setSSLSession(
171  myhost,
172  createPersistentTestSession(sessionWithTicket_));
173 
174  // First verify element is successfully added to the cache
175  auto s = cache_->getSSLSession(myhost);
176  ASSERT_TRUE(s != nullptr);
178  verifyEntryInCache(myhost, sessions_[3]);
179 
180  // Verify that if element is added to cache within
181  // tlsext_tick_lifetime_hint seconds ago, we can retrieve it
182 
183  // advance current time by slightly less than tlsext_tick_lifetime_hint
184  // Ticket should still be in cache
185  long lifetime_seconds = SSL_SESSION_get_ticket_lifetime_hint(s.get());
186  mockTimeUtil_->advance(
187  duration_cast<milliseconds>(seconds(lifetime_seconds - 10)));
188 
189  verifyEntryInCache(myhost, sessions_[3]);
190 
191  // advance current time to >= tlsext_tick_lifetime_hint
192  // Ticket should not be in cache
193  mockTimeUtil_->advance(duration_cast<milliseconds>(seconds(15)));
194  ASSERT_FALSE(cache_->getSSLSession(myhost));
195 }
196 
197 }
std::vector< std::pair< SSL_SESSION *, size_t > > sessions_
#define ASSERT_EQ(val1, val2)
Definition: gtest.h:1956
void verifyEntryInCache(const std::string &hostname, std::pair< SSL_SESSION *, size_t > session, bool inCache=true)
SSLSessionPtr createPersistentTestSession(std::pair< SSL_SESSION *, size_t > session)
Definition: TestUtil.cpp:350
TEST_F(TestInfoTest, Names)
std::string sformat(StringPiece fmt, Args &&...args)
Definition: Format.h:280
bool isSameSession(std::pair< SSL_SESSION *, size_t > lhs, std::pair< SSL_SESSION *, size_t > rhs)
Definition: TestUtil.cpp:368
std::unique_ptr< SSL_SESSION, SessionDestructor > SSLSessionPtr
Definition: SSLSession.h:32
void advance(std::chrono::milliseconds ms)
std::pair< SSL_SESSION *, size_t > sessionWithTicket_
std::vector< std::pair< SSL_SESSION *, size_t > > getSessions()
Definition: TestUtil.cpp:320
std::chrono::time_point< std::chrono::system_clock > now() const override
std::unique_ptr< SSLSessionPersistentCache > cache_
int SSL_SESSION_has_ticket(const SSL_SESSION *s)
Definition: OpenSSL.cpp:203
TimeUtilGeneric<> TimeUtil
Definition: Time.h:194
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
MockTimeUtilGeneric<> MockTimeUtil
Definition: MockTime.h:42
unsigned long SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *s)
Definition: OpenSSL.cpp:207
std::chrono::time_point< std::chrono::system_clock > t_
const char * string
Definition: Conv.cpp:212
static set< string > s
#define ASSERT_NE(val1, val2)
Definition: gtest.h:1960
#define ASSERT_FALSE(condition)
Definition: gtest.h:1868
std::pair< SSL_SESSION *, size_t > getSessionWithTicket()
Definition: TestUtil.cpp:340
#define ASSERT_TRUE(condition)
Definition: gtest.h:1865
std::string getPersistentCacheFilename()
Definition: TestUtil.cpp:22