• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 //
3 // Copyright 2018 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18 
19 #include "src/core/tsi/ssl/session_cache/ssl_session_cache.h"
20 
21 #include <grpc/support/port_platform.h>
22 #include <grpc/support/string_util.h>
23 
24 #include "absl/log/check.h"
25 #include "absl/log/log.h"
26 #include "src/core/lib/slice/slice_internal.h"
27 #include "src/core/tsi/ssl/session_cache/ssl_session.h"
28 #include "src/core/util/crash.h"
29 #include "src/core/util/sync.h"
30 
31 namespace tsi {
32 
33 /// Node for single cached session.
34 class SslSessionLRUCache::Node {
35  public:
Node(const std::string & key,SslSessionPtr session)36   Node(const std::string& key, SslSessionPtr session) : key_(key) {
37     SetSession(std::move(session));
38   }
39 
40   // Not copyable nor movable.
41   Node(const Node&) = delete;
42   Node& operator=(const Node&) = delete;
43 
key() const44   const std::string& key() const { return key_; }
45 
46   /// Returns a copy of the node's cache session.
CopySession() const47   SslSessionPtr CopySession() const { return session_->CopySession(); }
48 
49   /// Set the \a session (which is moved) for the node.
SetSession(SslSessionPtr session)50   void SetSession(SslSessionPtr session) {
51     session_ = SslCachedSession::Create(std::move(session));
52   }
53 
54  private:
55   friend class SslSessionLRUCache;
56 
57   std::string key_;
58   std::unique_ptr<SslCachedSession> session_;
59 
60   Node* next_ = nullptr;
61   Node* prev_ = nullptr;
62 };
63 
SslSessionLRUCache(size_t capacity)64 SslSessionLRUCache::SslSessionLRUCache(size_t capacity) : capacity_(capacity) {
65   if (capacity == 0) {
66     LOG(ERROR) << "SslSessionLRUCache capacity is zero. SSL sessions cannot be "
67                   "resumed.";
68   }
69 }
70 
~SslSessionLRUCache()71 SslSessionLRUCache::~SslSessionLRUCache() {
72   Node* node = use_order_list_head_;
73   while (node) {
74     Node* next = node->next_;
75     delete node;
76     node = next;
77   }
78 }
79 
Size()80 size_t SslSessionLRUCache::Size() {
81   grpc_core::MutexLock lock(&lock_);
82   return use_order_list_size_;
83 }
84 
FindLocked(const std::string & key)85 SslSessionLRUCache::Node* SslSessionLRUCache::FindLocked(
86     const std::string& key) {
87   auto it = entry_by_key_.find(key);
88   if (it == entry_by_key_.end()) {
89     return nullptr;
90   }
91   Node* node = it->second;
92   // Move to the beginning.
93   Remove(node);
94   PushFront(node);
95   AssertInvariants();
96   return node;
97 }
98 
Put(const char * key,SslSessionPtr session)99 void SslSessionLRUCache::Put(const char* key, SslSessionPtr session) {
100   if (session == nullptr) {
101     LOG(ERROR) << "Attempted to put null SSL session in session cache.";
102     return;
103   }
104   grpc_core::MutexLock lock(&lock_);
105   Node* node = FindLocked(key);
106   if (node != nullptr) {
107     node->SetSession(std::move(session));
108     return;
109   }
110   node = new Node(key, std::move(session));
111   PushFront(node);
112   entry_by_key_.emplace(key, node);
113   AssertInvariants();
114   if (use_order_list_size_ > capacity_) {
115     CHECK(use_order_list_tail_);
116     node = use_order_list_tail_;
117     Remove(node);
118     // Order matters, key is destroyed after deleting node.
119     entry_by_key_.erase(node->key());
120     delete node;
121     AssertInvariants();
122   }
123 }
124 
Get(const char * key)125 SslSessionPtr SslSessionLRUCache::Get(const char* key) {
126   grpc_core::MutexLock lock(&lock_);
127   // Key is only used for lookups.
128   Node* node = FindLocked(key);
129   if (node == nullptr) {
130     return nullptr;
131   }
132   return node->CopySession();
133 }
134 
Remove(SslSessionLRUCache::Node * node)135 void SslSessionLRUCache::Remove(SslSessionLRUCache::Node* node) {
136   if (node->prev_ == nullptr) {
137     use_order_list_head_ = node->next_;
138   } else {
139     node->prev_->next_ = node->next_;
140   }
141   if (node->next_ == nullptr) {
142     use_order_list_tail_ = node->prev_;
143   } else {
144     node->next_->prev_ = node->prev_;
145   }
146   CHECK_GE(use_order_list_size_, 1u);
147   use_order_list_size_--;
148 }
149 
PushFront(SslSessionLRUCache::Node * node)150 void SslSessionLRUCache::PushFront(SslSessionLRUCache::Node* node) {
151   if (use_order_list_head_ == nullptr) {
152     use_order_list_head_ = node;
153     use_order_list_tail_ = node;
154     node->next_ = nullptr;
155     node->prev_ = nullptr;
156   } else {
157     node->next_ = use_order_list_head_;
158     node->next_->prev_ = node;
159     use_order_list_head_ = node;
160     node->prev_ = nullptr;
161   }
162   use_order_list_size_++;
163 }
164 
165 #ifndef NDEBUG
AssertInvariants()166 void SslSessionLRUCache::AssertInvariants() {
167   size_t size = 0;
168   Node* prev = nullptr;
169   Node* current = use_order_list_head_;
170   while (current != nullptr) {
171     size++;
172     CHECK(current->prev_ == prev);
173     auto it = entry_by_key_.find(current->key());
174     CHECK(it != entry_by_key_.end());
175     CHECK(it->second == current);
176     prev = current;
177     current = current->next_;
178   }
179   CHECK(prev == use_order_list_tail_);
180   CHECK(size == use_order_list_size_);
181   CHECK(entry_by_key_.size() == use_order_list_size_);
182 }
183 #else
AssertInvariants()184 void SslSessionLRUCache::AssertInvariants() {}
185 #endif
186 
187 }  // namespace tsi
188