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