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/lib/gprpp/sync.h"
22 #include "src/core/lib/slice/slice_internal.h"
23 #include "src/core/tsi/ssl/session_cache/ssl_session.h"
24 #include "src/core/tsi/ssl/session_cache/ssl_session_cache.h"
25
26 #include <grpc/support/log.h>
27 #include <grpc/support/string_util.h>
28
29 namespace tsi {
30
cache_key_avl_destroy(void *,void *)31 static void cache_key_avl_destroy(void* /*key*/, void* /*unused*/) {}
32
cache_key_avl_copy(void * key,void *)33 static void* cache_key_avl_copy(void* key, void* /*unused*/) { return key; }
34
cache_key_avl_compare(void * key1,void * key2,void *)35 static long cache_key_avl_compare(void* key1, void* key2, void* /*unused*/) {
36 return grpc_slice_cmp(*static_cast<grpc_slice*>(key1),
37 *static_cast<grpc_slice*>(key2));
38 }
39
cache_value_avl_destroy(void *,void *)40 static void cache_value_avl_destroy(void* /*value*/, void* /*unused*/) {}
41
cache_value_avl_copy(void * value,void *)42 static void* cache_value_avl_copy(void* value, void* /*unused*/) {
43 return value;
44 }
45
46 // AVL only stores pointers, ownership belonges to the linked list.
47 static const grpc_avl_vtable cache_avl_vtable = {
48 cache_key_avl_destroy, cache_key_avl_copy, cache_key_avl_compare,
49 cache_value_avl_destroy, cache_value_avl_copy,
50 };
51
52 /// Node for single cached session.
53 class SslSessionLRUCache::Node {
54 public:
Node(const grpc_slice & key,SslSessionPtr session)55 Node(const grpc_slice& key, SslSessionPtr session) : key_(key) {
56 SetSession(std::move(session));
57 }
58
~Node()59 ~Node() { grpc_slice_unref_internal(key_); }
60
61 // Not copyable nor movable.
62 Node(const Node&) = delete;
63 Node& operator=(const Node&) = delete;
64
AvlKey()65 void* AvlKey() { return &key_; }
66
67 /// Returns a copy of the node's cache session.
CopySession() const68 SslSessionPtr CopySession() const { return session_->CopySession(); }
69
70 /// Set the \a session (which is moved) for the node.
SetSession(SslSessionPtr session)71 void SetSession(SslSessionPtr session) {
72 session_ = SslCachedSession::Create(std::move(session));
73 }
74
75 private:
76 friend class SslSessionLRUCache;
77
78 grpc_slice key_;
79 std::unique_ptr<SslCachedSession> session_;
80
81 Node* next_ = nullptr;
82 Node* prev_ = nullptr;
83 };
84
SslSessionLRUCache(size_t capacity)85 SslSessionLRUCache::SslSessionLRUCache(size_t capacity) : capacity_(capacity) {
86 GPR_ASSERT(capacity > 0);
87 entry_by_key_ = grpc_avl_create(&cache_avl_vtable);
88 }
89
~SslSessionLRUCache()90 SslSessionLRUCache::~SslSessionLRUCache() {
91 Node* node = use_order_list_head_;
92 while (node) {
93 Node* next = node->next_;
94 delete node;
95 node = next;
96 }
97 grpc_avl_unref(entry_by_key_, nullptr);
98 }
99
Size()100 size_t SslSessionLRUCache::Size() {
101 grpc_core::MutexLock lock(&lock_);
102 return use_order_list_size_;
103 }
104
FindLocked(const grpc_slice & key)105 SslSessionLRUCache::Node* SslSessionLRUCache::FindLocked(
106 const grpc_slice& key) {
107 void* value =
108 grpc_avl_get(entry_by_key_, const_cast<grpc_slice*>(&key), nullptr);
109 if (value == nullptr) {
110 return nullptr;
111 }
112 Node* node = static_cast<Node*>(value);
113 // Move to the beginning.
114 Remove(node);
115 PushFront(node);
116 AssertInvariants();
117 return node;
118 }
119
Put(const char * key,SslSessionPtr session)120 void SslSessionLRUCache::Put(const char* key, SslSessionPtr session) {
121 grpc_core::MutexLock lock(&lock_);
122 Node* node = FindLocked(grpc_slice_from_static_string(key));
123 if (node != nullptr) {
124 node->SetSession(std::move(session));
125 return;
126 }
127 grpc_slice key_slice = grpc_slice_from_copied_string(key);
128 node = new Node(key_slice, std::move(session));
129 PushFront(node);
130 entry_by_key_ = grpc_avl_add(entry_by_key_, node->AvlKey(), node, nullptr);
131 AssertInvariants();
132 if (use_order_list_size_ > capacity_) {
133 GPR_ASSERT(use_order_list_tail_);
134 node = use_order_list_tail_;
135 Remove(node);
136 // Order matters, key is destroyed after deleting node.
137 entry_by_key_ = grpc_avl_remove(entry_by_key_, node->AvlKey(), nullptr);
138 delete node;
139 AssertInvariants();
140 }
141 }
142
Get(const char * key)143 SslSessionPtr SslSessionLRUCache::Get(const char* key) {
144 grpc_core::MutexLock lock(&lock_);
145 // Key is only used for lookups.
146 grpc_slice key_slice = grpc_slice_from_static_string(key);
147 Node* node = FindLocked(key_slice);
148 if (node == nullptr) {
149 return nullptr;
150 }
151 return node->CopySession();
152 }
153
Remove(SslSessionLRUCache::Node * node)154 void SslSessionLRUCache::Remove(SslSessionLRUCache::Node* node) {
155 if (node->prev_ == nullptr) {
156 use_order_list_head_ = node->next_;
157 } else {
158 node->prev_->next_ = node->next_;
159 }
160 if (node->next_ == nullptr) {
161 use_order_list_tail_ = node->prev_;
162 } else {
163 node->next_->prev_ = node->prev_;
164 }
165 GPR_ASSERT(use_order_list_size_ >= 1);
166 use_order_list_size_--;
167 }
168
PushFront(SslSessionLRUCache::Node * node)169 void SslSessionLRUCache::PushFront(SslSessionLRUCache::Node* node) {
170 if (use_order_list_head_ == nullptr) {
171 use_order_list_head_ = node;
172 use_order_list_tail_ = node;
173 node->next_ = nullptr;
174 node->prev_ = nullptr;
175 } else {
176 node->next_ = use_order_list_head_;
177 node->next_->prev_ = node;
178 use_order_list_head_ = node;
179 node->prev_ = nullptr;
180 }
181 use_order_list_size_++;
182 }
183
184 #ifndef NDEBUG
calculate_tree_size(grpc_avl_node * node)185 static size_t calculate_tree_size(grpc_avl_node* node) {
186 if (node == nullptr) {
187 return 0;
188 }
189 return 1 + calculate_tree_size(node->left) + calculate_tree_size(node->right);
190 }
191
AssertInvariants()192 void SslSessionLRUCache::AssertInvariants() {
193 size_t size = 0;
194 Node* prev = nullptr;
195 Node* current = use_order_list_head_;
196 while (current != nullptr) {
197 size++;
198 GPR_ASSERT(current->prev_ == prev);
199 void* node = grpc_avl_get(entry_by_key_, current->AvlKey(), nullptr);
200 GPR_ASSERT(node == current);
201 prev = current;
202 current = current->next_;
203 }
204 GPR_ASSERT(prev == use_order_list_tail_);
205 GPR_ASSERT(size == use_order_list_size_);
206 GPR_ASSERT(calculate_tree_size(entry_by_key_.root) == use_order_list_size_);
207 }
208 #else
AssertInvariants()209 void SslSessionLRUCache::AssertInvariants() {}
210 #endif
211
212 } // namespace tsi
213