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/mutex_lock.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 * key,void * unused)31 static void cache_key_avl_destroy(void* key, void* unused) {}
32
cache_key_avl_copy(void * key,void * unused)33 static void* cache_key_avl_copy(void* key, void* unused) { return key; }
34
cache_key_avl_compare(void * key1,void * key2,void * unused)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 * value,void * unused)40 static void cache_value_avl_destroy(void* value, void* unused) {}
41
cache_value_avl_copy(void * value,void * unused)42 static void* cache_value_avl_copy(void* value, void* unused) { return value; }
43
44 // AVL only stores pointers, ownership belonges to the linked list.
45 static const grpc_avl_vtable cache_avl_vtable = {
46 cache_key_avl_destroy, cache_key_avl_copy, cache_key_avl_compare,
47 cache_value_avl_destroy, cache_value_avl_copy,
48 };
49
50 /// Node for single cached session.
51 class SslSessionLRUCache::Node {
52 public:
Node(const grpc_slice & key,SslSessionPtr session)53 Node(const grpc_slice& key, SslSessionPtr session) : key_(key) {
54 SetSession(std::move(session));
55 }
56
~Node()57 ~Node() { grpc_slice_unref_internal(key_); }
58
59 // Not copyable nor movable.
60 Node(const Node&) = delete;
61 Node& operator=(const Node&) = delete;
62
AvlKey()63 void* AvlKey() { return &key_; }
64
65 /// Returns a copy of the node's cache session.
CopySession() const66 SslSessionPtr CopySession() const { return session_->CopySession(); }
67
68 /// Set the \a session (which is moved) for the node.
SetSession(SslSessionPtr session)69 void SetSession(SslSessionPtr session) {
70 session_ = SslCachedSession::Create(std::move(session));
71 }
72
73 private:
74 friend class SslSessionLRUCache;
75
76 grpc_slice key_;
77 grpc_core::UniquePtr<SslCachedSession> session_;
78
79 Node* next_ = nullptr;
80 Node* prev_ = nullptr;
81 };
82
SslSessionLRUCache(size_t capacity)83 SslSessionLRUCache::SslSessionLRUCache(size_t capacity) : capacity_(capacity) {
84 GPR_ASSERT(capacity > 0);
85 gpr_mu_init(&lock_);
86 entry_by_key_ = grpc_avl_create(&cache_avl_vtable);
87 }
88
~SslSessionLRUCache()89 SslSessionLRUCache::~SslSessionLRUCache() {
90 Node* node = use_order_list_head_;
91 while (node) {
92 Node* next = node->next_;
93 grpc_core::Delete(node);
94 node = next;
95 }
96 grpc_avl_unref(entry_by_key_, nullptr);
97 gpr_mu_destroy(&lock_);
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 = grpc_core::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 grpc_core::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