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 <grpc/support/port_platform.h>
20 
21 #include "src/core/tsi/ssl/session_cache/ssl_session_cache.h"
22 
23 #include <grpc/support/log.h>
24 #include <grpc/support/string_util.h>
25 
26 #include "src/core/lib/gprpp/crash.h"
27 #include "src/core/lib/gprpp/sync.h"
28 #include "src/core/lib/slice/slice_internal.h"
29 #include "src/core/tsi/ssl/session_cache/ssl_session.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   GPR_ASSERT(capacity > 0);
66 }
67 
~SslSessionLRUCache()68 SslSessionLRUCache::~SslSessionLRUCache() {
69   Node* node = use_order_list_head_;
70   while (node) {
71     Node* next = node->next_;
72     delete node;
73     node = next;
74   }
75 }
76 
Size()77 size_t SslSessionLRUCache::Size() {
78   grpc_core::MutexLock lock(&lock_);
79   return use_order_list_size_;
80 }
81 
FindLocked(const std::string & key)82 SslSessionLRUCache::Node* SslSessionLRUCache::FindLocked(
83     const std::string& key) {
84   auto it = entry_by_key_.find(key);
85   if (it == entry_by_key_.end()) {
86     return nullptr;
87   }
88   Node* node = it->second;
89   // Move to the beginning.
90   Remove(node);
91   PushFront(node);
92   AssertInvariants();
93   return node;
94 }
95 
Put(const char * key,SslSessionPtr session)96 void SslSessionLRUCache::Put(const char* key, SslSessionPtr session) {
97   grpc_core::MutexLock lock(&lock_);
98   Node* node = FindLocked(key);
99   if (node != nullptr) {
100     node->SetSession(std::move(session));
101     return;
102   }
103   node = new Node(key, std::move(session));
104   PushFront(node);
105   entry_by_key_.emplace(key, node);
106   AssertInvariants();
107   if (use_order_list_size_ > capacity_) {
108     GPR_ASSERT(use_order_list_tail_);
109     node = use_order_list_tail_;
110     Remove(node);
111     // Order matters, key is destroyed after deleting node.
112     entry_by_key_.erase(node->key());
113     delete node;
114     AssertInvariants();
115   }
116 }
117 
Get(const char * key)118 SslSessionPtr SslSessionLRUCache::Get(const char* key) {
119   grpc_core::MutexLock lock(&lock_);
120   // Key is only used for lookups.
121   Node* node = FindLocked(key);
122   if (node == nullptr) {
123     return nullptr;
124   }
125   return node->CopySession();
126 }
127 
Remove(SslSessionLRUCache::Node * node)128 void SslSessionLRUCache::Remove(SslSessionLRUCache::Node* node) {
129   if (node->prev_ == nullptr) {
130     use_order_list_head_ = node->next_;
131   } else {
132     node->prev_->next_ = node->next_;
133   }
134   if (node->next_ == nullptr) {
135     use_order_list_tail_ = node->prev_;
136   } else {
137     node->next_->prev_ = node->prev_;
138   }
139   GPR_ASSERT(use_order_list_size_ >= 1);
140   use_order_list_size_--;
141 }
142 
PushFront(SslSessionLRUCache::Node * node)143 void SslSessionLRUCache::PushFront(SslSessionLRUCache::Node* node) {
144   if (use_order_list_head_ == nullptr) {
145     use_order_list_head_ = node;
146     use_order_list_tail_ = node;
147     node->next_ = nullptr;
148     node->prev_ = nullptr;
149   } else {
150     node->next_ = use_order_list_head_;
151     node->next_->prev_ = node;
152     use_order_list_head_ = node;
153     node->prev_ = nullptr;
154   }
155   use_order_list_size_++;
156 }
157 
158 #ifndef NDEBUG
AssertInvariants()159 void SslSessionLRUCache::AssertInvariants() {
160   size_t size = 0;
161   Node* prev = nullptr;
162   Node* current = use_order_list_head_;
163   while (current != nullptr) {
164     size++;
165     GPR_ASSERT(current->prev_ == prev);
166     auto it = entry_by_key_.find(current->key());
167     GPR_ASSERT(it != entry_by_key_.end());
168     GPR_ASSERT(it->second == current);
169     prev = current;
170     current = current->next_;
171   }
172   GPR_ASSERT(prev == use_order_list_tail_);
173   GPR_ASSERT(size == use_order_list_size_);
174   GPR_ASSERT(entry_by_key_.size() == use_order_list_size_);
175 }
176 #else
AssertInvariants()177 void SslSessionLRUCache::AssertInvariants() {}
178 #endif
179 
180 }  // namespace tsi
181